プログラマー向けチュートリアル: pandas.testing.assert_frame_equal を使って DataFrame を比較する


この関数は、主に テストコード で使用され、期待される結果と実際の結果を比較するのに役立ちます。

基本的な使い方

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

pd.testing.assert_frame_equal(df1, df2)

上記のコードは、df1df2 が同じデータ、インデックス、列名を持っていることを検証し、一致していれば何も出力されません。

オプション

assert_frame_equal には、比較をより詳細に制御するためのオプションがいくつか用意されています。

  • rtol: 浮動小数点数の比較相対許容誤差を指定します。
  • atol: 浮動小数点数の比較許容誤差を指定します。
  • check_frame_type: DataFrame クラスの一致をチェックするかどうかを指定します。デフォルトは False です。
  • check_less_precise: 浮動小数点数の比較精度を調整します。デフォルトは False です。
  • check_column_type: 列型の一致をチェックするかどうかを指定します。デフォルトは False です。
  • check_index_type: インデックス型の一致をチェックするかどうかを指定します。デフォルトは False です。
  • check_dtype: データ型の一致をチェックするかどうかを指定します。デフォルトは True です。

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4.01, 5.001, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

pd.testing.assert_frame_equal(df1, df2, atol=0.01)

上記のコードは、df1df2 のデータが 01 の精度で一致しているかどうかを検証します。

  • 列名が異なる場合でも、列の順序が同じであれば一致とみなされます。
  • インデックス名が異なる場合でも、インデックス値が同じであれば一致とみなされます。
  • データ型が異なる場合でも、値が同じであれば一致とみなされます。
  • assert_frame_equal は、2つの DataFrame が同じオブジェクトであることを検証するものではありません。
  • pandas には、DataFrame を比較するための他の関数もいくつか用意されています。
    • DataFrame.equals: 2つの DataFrame を比較し、True または False を返します。
    • DataFrame.compare: 2つの DataFrame を比較し、差異をハイライト表示します。

pandas.testing.assert_frame_equal は、DataFrame を比較するための便利な関数です。テストコードでデータの一貫性を検証する際に役立ちます。



基本的な例

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

pd.testing.assert_frame_equal(df1, df2)

オプションの例

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4.01, 5.001, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

# 浮動小数点数の比較精度を調整
pd.testing.assert_frame_equal(df1, df2, atol=0.01)

# データ型の一致を無視
pd.testing.assert_frame_equal(df1, df2, check_dtype=False)

# インデックス型の一致を無視
pd.testing.assert_frame_equal(df1, df2, check_index_type=False)

このコードは、assert_frame_equal のオプションを使用して、比較をより詳細に制御する方法を示しています。

異なるデータフレームの比較

assert_frame_equal は、2つの DataFrame が完全に一致していなくても使用できます。ただし、この場合は、atolrtol などのオプションを使用して、許容される差異のレベルを指定する必要があります。

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4.01, 5.001, 6]})

# 浮動小数点数の比較精度を調整
pd.testing.assert_frame_equal(df1, df2, atol=0.01)

このコードは、df1df2B 列の値が 01 の精度で一致しているかどうかを検証します。

カスタムコンパレータ

assert_frame_equal は、カスタムコンパレータを使用して、特定の列の比較方法をカスタマイズすることもできます。

import pandas as pd

def my_cmp(a, b):
    # カスタム比較ロジック
    return a * 2 == b

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [2, 4, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [2, 4, 6]})

# 特定の列に対してカスタムコンパレータを使用
pd.testing.assert_frame_equal(df1, df2, rfunc=my_cmp, cols=['B'])

このコードは、B 列の値をカスタムコンパレータ my_cmp で比較します。



以下に、いくつかの代替方法とその利点と欠点をご紹介します。

手動比較

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

print(df1.eq(df2).all())
print((df1 - df2).sum().eq(0).all())

この方法は、シンプルな比較であれば有効ですが、複雑な比較やオプションが必要な場合は煩雑になります。

利点

  • シンプルで分かりやすい

欠点

  • コードが冗長になる可能性がある
  • オプションが限られている
  • 複雑な比較には不向き

DataFrame.equals

import pandas as pd

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})

print(df1.equals(df2))

DataFrame.equals は、assert_frame_equal と同様の機能を提供しますが、より簡潔な構文で使用できます。

利点

  • 基本的な比較には十分
  • assert_frame_equal よりも簡潔

欠点

  • カスタムコンパレータを使用できない
  • assert_frame_equal のオプションの一部が利用できない

カスタム比較ロジック

import pandas as pd

def my_cmp(df1, df2):
    # カスタム比較ロジック
    for col in df1.columns:
        if col == 'B':
            # B列はカスタムコンパレータで比較
            df1[col] = df1[col] * 2
        else:
            # その他の列は標準の比較
            df1[col] = df1[col].eq(df2[col])
    return df1.eq(df2).all()

df1 = pd.DataFrame({'A': [1, 2, 3], 'B': [2, 4, 6]})
df2 = pd.DataFrame({'A': [1, 2, 3], 'B': [2, 4, 6]})

print(my_cmp(df1.copy(), df2.copy()))

この方法は、最も柔軟性がありますが、複雑なロジックを実装する必要があるため、難易度が高くなります。

利点

  • 任意の比較ロジックを実装できる
  • 最も柔軟性が高い

欠点

  • テストが困難になる可能性がある
  • コードが冗長になる可能性がある
  • 複雑なロジックを実装する必要がある
  • scikit-learn.metrics.pairwise_distances: 2つのデータセット間の距離を計算するために使用できます。
  • numpy.testing.assert_allclose: 浮動小数点数の配列を比較するために使用できます。

これらのライブラリは、特定のニーズに特化した高度な比較機能を提供する場合があります。

選択の指針

どの代替方法を選択するかは、比較の複雑性、必要なオプション、および個人的な好みによって異なります。

  • 2つのデータセット間の距離を計算する必要がある場合は、scikit-learn.metrics.pairwise_distances を使用できます。
  • 浮動小数点数の比較に特化した場合は、numpy.testing.assert_allclose を使用できます。
  • より複雑な比較やカスタムロジックが必要な場合は、手動比較またはカスタム比較ロジックを使用する必要があります。
  • シンプルで基本的な比較の場合は、DataFrame.equals がおすすめです。