pandas Dataframeをvalidationするpanderaを使ってみる

 # 概要

[pandera](https://pandera.readthedocs.io/en/latest/index.html)を使ってirsiとtitanicのデータをvalidationしてみる。
対応しているのはpandasだけではないので詳しくは公式の冒頭を読むと分かりやすいです。

# どうしてこの記事を書いたのか

外部からCSVとかでデータを取得してデータフレームにして整形して分析とか計算とかをするってことを自動化していると、
データフレームは当初の想定と同じ形になっているのだろうかと不安になるし、
型が変わっていたりカラムが増えていたりする事に気づきやすくしたいなって思ったので。

# 前提

- panderaのバージョンは 0.11.0
- pandasのバージョンは 1.4.1
- Pythonのバージョンは 3.9.10
- irisのデータセットを取得するためだけsklearnをimport
- titanicのデータは[Kaggleからダウンロードしたtrain.csv](https://www.kaggle.com/competitions/titanic/data)を使う
- pandera以外のインストール(Pythonとかsklearnとかpandasとか)は済んでいる

# 準備

## panderaのインストール

```shell
pip install pandera
```

# irisのデータをバリデーションしてみる

## irisのデータの準備

```python
from sklearn import datasets

data = datasets.load_iris()
df = pd.DataFrame(data.data, columns=data.feature_names)
```

データは以下の感じ
![iris_overview.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86853/94a8fdc8-c6d4-732a-c6be-7a086c4bfda6.png)

## schemaの定義をする

```python
import pandera as pa
schema = pa.DataFrameSchema(
    {
        "sepal length (cm)": pa.Column(float, checks=pa.Check.le(10)), # 型は浮動小数点で10以下
        "sepal width (cm)": pa.Column(
            float, checks=[pa.Check.gt(0.0), pa.Check.lt(10.0)]
        ), # 型は浮動小数点で0.0より大きく、10.0より小さい
        "petal length (cm)": pa.Column(float, checks=pa.Check.le(7.0)), # 型は浮動小数点で7.0以下
        "petal width (cm)": pa.Column(
            float,
            checks=[
                pa.Check.gt(0.0),
                pa.Check.le(2.5),
            ], # 型は浮動小数点で0.0より大きく2.5以下
        ),
    }
)
```

## validationする

```python
schema(df)
```

データフレームが返ってくれば定義した要件を満たしている。
エラーが出たら何かしらを満たしていない。

# titanicのデータをvalidationしてみる

## titanicのデータの読み込み

```python
import pandas as pd

df = pd.read_csv("./train.csv")
```

データは以下の感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86853/a40e8559-0160-ffd7-3439-3a4d2d924bdc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86853/0b5d29c1-7e4c-0a9e-3954-04e1cfb24152.png)




## schemaの定義をする_その1

```python
# schemaに無いカラムがあったらエラーにするためにstrictをtrueにする
schema = pa.DataFrameSchema(
    {
        "PassengerId": pa.Column(int, checks=[pa.Check.ge(1), pa.Check.le(891)]),
        "Survived": pa.Column(int, checks=[pa.Check.ge(0), pa.Check.le(1)])
    },
    strict=True
)
```


## validationする_その1

データフレームにはschemaで定義していないカラムがあるのでエラーになります。

```python
schema(df)
```

エラーメッセージは以下の感じ。

```plaintext
SchemaError: column 'Pclass' not in DataFrameSchema {'PassengerId': <Schema Column(name=PassengerId, type=DataType(int64))>, 'Survived': <Schema Column(name=Survived, type=DataType(int64))>}
```

## schemaを定義する_その2

今度は全部のカラムを定義します。

```python
schema = pa.DataFrameSchema(
    {
        "PassengerId": pa.Column(int),
        "Survived": pa.Column(int, checks=[pa.Check.greater_than_or_equal_to(0), pa.Check.less_than_or_equal_to(1)]),
        "Pclass": pa.Column(int, checks=pa.Check.less_than_or_equal_to(3)),
        "Name": pa.Column(str),
        "Sex": pa.Column(str, checks=pa.Check.isin(["male", "female"])), # リスト内のいずれかの値に合致することを確認する
        "Age": pa.Column(float, checks=pa.Check.greater_than_or_equal_to(0), nullable=True), # nullを許容する
        "SibSp": pa.Column(int, checks=pa.Check.isin([0,1,2,3,4,5,8])),
        "Parch": pa.Column(int, checks=pa.Check.in_range(min_value=0, max_value=6)), # 範囲で指定する
        "Ticket": pa.Column(str, checks=pa.Check.str_length(max_value=30)), # 30文字以内
        "Fare": pa.Column(float, checks=pa.Check(lambda s: s.mean() == 32.2042079685746)), # 平均でチェックする
        "Cabin": pa.Column(str, checks=pa.Check.str_matches(r'^[A-Z]'), regex=True, nullable=True), # 正規表現を使う
        "Embarked": pa.Column(str, checks=pa.Check.str_length(max_value=1), nullable=True) # 文字数をチェックする
    },
    strict=True # 定義していないカラムがあったらエラーにする。
)
```

## validationする_その2

今度はOK！

```python
schema(df)
```

# おわり

分かりやすく書けるのでとても良いと思います！定義した関数をチェックに使えるみたいなので、
用意されているもので足りなければ色々自分で用意することもできそうです。