2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

panderaを用いてデータフレームのバリデーションを実施する方法

Last updated at Posted at 2023-06-25

はじめに

データ分析や機械学習モデルの構築を行う際、データのバリデーションは信頼性を保つために非常に重要なステップとなります。
例えば、機械学習モデルを定期的に構築・更新する過程で、予期しない値を含むデータを検出せずに使用してしまうと、処理がエラーで中断したり、モデルの精度が悪化したりする可能性があります。
これを防ぐためには、データフレームに対する効率的なバリデーション方法が求められます。
今回は、その一例としてpanderaのDataFrameModelを用いたバリデーション手法を紹介します。

使用したバージョン

  • python 3.9.7
  • pandas 3.2.0
  • pandera 2.0.2

インストール方法

pip install pandera

基本的な使い方

以下は、'id'、'年齢(age)'、'所得(income)'、'性別(gender)'、そして'職業(job)'という項目を持つpandasのDataFrameに対してバリデーションを実施する例を示します。
この例では、データフレームの各列(スキーマ)の定義を含むUserSchemaという名前のクラスを作成しています。

import pandas as pd

import pandera as pa
from pandera.typing import Series

# バリデーション対象のデータフレームの作成
data = {
    "id": ["No_0001", "No_0002", "No_0003", "No_0004", "No_0005"],
    "age": [25, 27, 31, "45", 52],
    "income": [50000, 62000, 10000, 87000, 92000],
    "gender": ["male", "male", "female", "male", "female"],
    "job": ["engineer", "doctor", None, "engineer", "teacher"],
}

df = pd.DataFrame(data)


class UserSchema(pa.DataFrameModel):
    # 各フィールドの定義と基本的なチェックを設定。
    id: Series[str] = pa.Field()  # idは文字列である
    age: Series[int] = pa.Field(ge=18, le=60, coerce=True)  # 年齢は18以上60以下で、数値に強制変換する
    income: Series[int] = pa.Field(ge=1, le=100000)  # 収入は1以上100000以下である
    gender: Series[str] = pa.Field(isin=["male", "female"])  # 性別は"male"か"female"
    job: Series[str] = pa.Field(nullable=True)  # 職業は文字列で、欠損値(None)も許容する

    @pa.check("id")
    def id_split_check(cls, series: Series[str]) -> Series[bool]:
        """'_'でidを分割したときに2つの要素になることを確認する"""
        return series.str.split("_", expand=True).shape[1] == 2

    @pa.check("id")
    def id_length_check(cls, series: Series[str]) -> Series[bool]:
        """'_'でidを分割したときに、その2つの要素の文字数がそれぞれ2文字と4文字であることを確認する"""
        split_series = series.str.split("_", expand=True)
        return (split_series[0].str.len() == 2) & (split_series[1].str.len() == 4)


# データフレームのバリデーションを実行
validated_df = UserSchema.validate(df)
print("Before-----------------")
print(df.dtypes)
print("After------------------")
print(validated_df.dtypes)

バリデーションが問題なく完了すればエラーが吐かれず処理を完遂することができます。

出力
Before-----------------
id        object
age       object
income     int64
gender    object
job       object
dtype: object

After------------------
id        object
age        int64
income     int64
gender    object
job       object
dtype: object

ageの要素3のみあえて文字として入力しておりましたが、coerce=Trueとすることで強制的に指定した型(int)に変換が行われています。

例えば上記のスキーマの定義で、異常なデータ(incomeが-10000等)が混入していた場合はSchemaErrorが出ます。
incomeのindexの2番に異常値が含まれていると以下のように表示されます。

出力
pandera.errors.SchemaError: <Schema Column(name=income, type=DataType(int64))> failed element-wise validator 0:
<Check greater_than_or_equal_to: greater_than_or_equal_to(1)>
failure cases:
   index  failure_case
0      2        -10000

panderaのチェック機能では、各列(pandasのSeries)を入力とし、条件式を適用してブール値(True/False)のSeriesを出力します。このため、チェックをパスするためには出力されたブール値のSeriesの全ての値がTrueである必要があります。

pandera.Fieldで指定可能な引数

pandera.Fieldで指定可能な引数を一部抜粋しました。

引数 説明 引数の値の指定方法
eq 列の値が指定した値と等しいかをチェック 任意の値
ne 列の値が指定した値と等しくないかをチェック 任意の値
gt 列の値が指定した値より大きいかをチェック 数値
ge 列の値が指定した値以上かをチェック 数値
lt 列の値が指定した値より小さいかをチェック 数値
le 列の値が指定した値以下かをチェック 数値
in_range 列の値が指定した範囲内にあるかをチェック 辞書型で上限と下限を指定。{"min_value": 1, "max_value": 100} のように指定。
isin 列の値が指定したリストに含まれているかをチェック リストまたはイテラブルオブジェクト
notin 列の値が指定したリストに含まれていないかをチェック リストまたはイテラブルオブジェクト
str_contains 文字列列が指定した文字列を含むかをチェック 文字列
str_endswith 文字列列が指定した文字列で終わるかをチェック 文字列
str_length 文字列列の長さが指定した条件を満たすかをチェック 辞書型で文字列の長さの上限と下限を指定。str_length={"min_value": 1, "max_value": 6}のように指定。
str_matches 文字列列が指定した正規表現にマッチするかをチェック 正規表現の文字列
str_startswith 文字列列が指定した文字列で始まるかをチェック 文字列
nullable 列がnull値を含んでも良いかどうかを指定 ブール値
unique 列の値が一意であるべきかを指定 ブール値
coerce 列のデータ型を強制的に変換するかを指定 ブール値
regex フィールド名またはエイリアスが正規表現パターンであるかを指定 ブール値
ignore_na チェックでnull値を無視するかを指定 ブール値
raise_warning 例外ではなく警告を発するかを指定 ブール値
n_failure_cases 最初のn件のユニークな失敗ケースを報告。Noneの場合、全ての失敗ケースを報告 整数またはNone

おわりに

この記事では、panderaのDataFrameModelを用いたデータフレームのバリデーションについて説明しました。
データのバリデーションは機械学習モデルを構築し実際に推論を行うまでの一連の行程に必ずしも必要というわけではないため軽視されがちですが、モデルによる推論結果の信頼性を担保するために不可欠な工程だと思います。
特に定期的に連携されてくるデータを基にモデルで推論を行うなどのケースだと、データの中に予期していない新たな値が追加されていたり、処理異常でデータが一部欠損しているなどの事象は稀に起こります。このような異常にいち早く気づけるようにpanderaのバリデーション機能を積極的に使用していこうと思います。

参考

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?