pandas DataFrame比較のプロ技:compareと代替手法を使いこなす

2025-04-26

基本的な使い方

import pandas as pd

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

result = df1.compare(df2)
print(result)

このコードでは、df1df2 という2つの DataFrame を作成し、df1.compare(df2) を実行して、それらの差異を result に格納しています。

出力結果の解釈

compare メソッドの出力は、MultiIndex を持つ DataFrame です。


  • 差異のある行のインデックス

  • 差異のある列
  • MultiIndex のレベル 1
    • self: df1 の値
    • other: df2 の値

上記の例では、result は次のようになります。

  col1 col2
  self other self other
1  2.0   4.0  B    X

この結果から、次のことがわかります。

  • col2 列のインデックス 1 の行で、df1 の値は 'B'、df2 の値は 'X' である。
  • col1 列のインデックス 1 の行で、df1 の値は 2、df2 の値は 4 である。

主なパラメータ

  • result_names: 結果の MultiIndex のレベル 1 の名前。デフォルトは ('self', 'other')
  • keep_equal: 等しい値を結果に含めるかどうか。デフォルトは False
  • keep_shape: 結果の DataFrame を元の DataFrame と同じ形状にするかどうか。デフォルトは False
  • align_axis: 比較する軸(0 または 'index' で行、1 または 'columns' で列)。デフォルトは 'columns'
  • other: 比較対象の DataFrame。

例(keep_equal=True の場合)

result = df1.compare(df2, keep_equal=True)
print(result)

この場合、等しい値も結果に含まれます。

例(keep_shape=True の場合)

result = df1.compare(df2, keep_shape=True)
print(result)

この場合、結果の DataFrame は元の DataFrame と同じ形状になります。差異がないセルは NaN になります。



一般的なエラーとトラブルシューティング

    • 原因
      compare メソッドの other 引数に DataFrame 以外のオブジェクト(リスト、辞書、Series など)を渡した場合に発生します。
    • 解決策
      other 引数に DataFrame オブジェクトを渡すように修正します。

    • import pandas as pd
      
      df1 = pd.DataFrame({'col1': [1, 2, 3]})
      other_list = [1, 2, 3] # DataFrameではない
      # df1.compare(other_list) # エラー!
      df2 = pd.DataFrame({'col1': [1, 4, 3]})
      df1.compare(df2) # 正しい
      
  1. TypeError: '<' not supported between instances of 'str' and 'int' (または、他の型に関する同様のエラー)

    • 原因
      compare メソッドは内部で比較演算子(<>== など)を使用するため、列のデータ型が混在している場合に発生します。例えば、文字列と数値を比較しようとするとエラーが発生します。
    • 解決策
      DataFrame の列のデータ型を統一するか、比較対象の列のデータ型が一致するように変換します。

    • import pandas as pd
      
      df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C']})
      df2 = pd.DataFrame({'col1': ['1', 4, 3], 'col2': ['A', 'X', 'C']}) # col1のデータ型が混在
      # df1.compare(df2) # エラー!
      df2['col1'] = pd.to_numeric(df2['col1'], errors='coerce') #数値型に変換
      df1.compare(df2)
      
  2. KeyError: '列名'

    • 原因
      比較対象の DataFrame (other) に、元の DataFrame (self) と同じ列名が存在しない場合に発生します。
    • 解決策
      両方の DataFrame の列名を一致させるか、比較対象の列を絞り込みます。

    • import pandas as pd
      
      df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C']})
      df2 = pd.DataFrame({'col3': [1, 4, 3], 'col2': ['A', 'X', 'C']}) # col1がない
      # df1.compare(df2) # エラー!
      df2.rename(columns={'col3':'col1'}, inplace=True) # 列名を修正
      df1.compare(df2)
      
  3. 結果が期待通りにならない

    • 原因
      keep_equalkeep_shape などのパラメータの理解不足や誤った設定が原因となることがあります。
    • 解決策
      パラメータのドキュメントをよく読み、目的に合わせて適切に設定します。
      • keep_equal=True: 等しい値も結果に含めます。
      • keep_shape=True: 結果の DataFrame を元の DataFrame と同じ形状にします。
    • また、比較対象のindexが一致しているか確認してください。indexが異なると比較結果が想定と異なる場合があります。
  4. パフォーマンスの問題

    • 原因
      大規模な DataFrame を比較する場合、処理に時間がかかることがあります。
    • 解決策
      • 比較対象の DataFrame を小さく分割して比較します。
      • 必要な列のみを比較対象とします。
      • NumPy などの高速なライブラリを使用して比較処理を最適化します。

トラブルシューティングの一般的な手順

  1. エラーメッセージをよく読む
    エラーメッセージには、問題の原因に関する情報が含まれています。
  2. データ型を確認する
    DataFrame のデータ型を確認し、必要に応じて変換します。
  3. 列名を確認する
    両方の DataFrame の列名が一致していることを確認します。
  4. パラメータを確認する
    compare メソッドのパラメータが適切に設定されていることを確認します。
  5. 小さなデータで試す
    問題を特定するために、小さなデータセットでコードを試してみます。
  6. ドキュメントを参照する
    pandas の公式ドキュメントを参照して、compare メソッドの詳細を確認します。


例1: 基本的な比較

import pandas as pd

# 2つのDataFrameを作成
df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C']})
df2 = pd.DataFrame({'col1': [1, 4, 3], 'col2': ['A', 'X', 'C']})

# DataFrameを比較
result = df1.compare(df2)

# 結果を表示
print(result)

この例では、df1df2 という2つの DataFrame を作成し、compare メソッドを使用してそれらの差異を特定します。結果は、MultiIndex を持つ DataFrame として表示されます。

例2: keep_equal=True を使用して等しい値も含める

import pandas as pd

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

# 等しい値も含めて比較
result = df1.compare(df2, keep_equal=True)

print(result)

keep_equal=True を指定すると、等しい値も結果に含まれます。これにより、全ての要素の比較結果を確認できます。

例3: keep_shape=True を使用して元のDataFrameと同じ形状で結果を表示

import pandas as pd

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

# 元のDataFrameと同じ形状で比較
result = df1.compare(df2, keep_shape=True)

print(result)

keep_shape=True を指定すると、結果の DataFrame は元の DataFrame と同じ形状になります。差異がないセルは NaN になります。

例4: align_axis='columns' を使用して列ごとに比較

import pandas as pd

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

# 列ごとに比較(デフォルト)
result = df1.compare(df2, align_axis='columns')

print(result)

align_axis='columns'(デフォルト)は列ごとに比較します。

例5: align_axis='index' を使用して行ごとに比較(列名が同一である必要があります)

import pandas as pd

df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C']}, index=['row1','row2','row3'])
df2 = pd.DataFrame({'col1': [1, 4, 3], 'col2': ['A', 'X', 'C']}, index=['row1','row2','row3'])

# 行ごとに比較
result = df1.compare(df2, align_axis='index')

print(result)

align_axis='index' を指定すると、行ごとに比較します。この場合、indexが一致している必要があります。

import pandas as pd

df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C'], 'col3': [10, 20, 30]})
df2 = pd.DataFrame({'col1': [1, 4, 3], 'col2': ['A', 'X', 'C'], 'col3': [10, 25, 30]})

# 特定の列のみを比較
result = df1[['col1', 'col2']].compare(df2[['col1', 'col2']])

print(result)


pandas.DataFrame.eq とブールインデックスを用いた比較

eq メソッドは、2つの DataFrame の要素ごとの等価性を比較し、ブール値の DataFrame を返します。これを利用して、異なる要素を特定できます。

import pandas as pd

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

# 等価性を比較
equal_df = df1.eq(df2)

# 等しくない要素を特定
diff_df = df1[~equal_df]
diff_df_other = df2[~equal_df]

print("df1の異なる箇所:\n", diff_df)
print("\ndf2の異なる箇所:\n", diff_df_other)

# 差異のある箇所を特定する際、indexやcolumnsを揃える必要があることに注意してください。

この方法は、等しくない要素を直接的に特定し、必要に応じて元の DataFrame から抽出できる柔軟性があります。

pandas.DataFrame.where を用いた比較

where メソッドは、条件に基づいて DataFrame の要素を置き換えます。等しい要素を NaN に置き換えることで、差異を特定できます。

import pandas as pd

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

# 等しい要素をNaNに置き換え
diff_df1 = df1.where(df1.eq(df2), other=df2)
diff_df2 = df2.where(df1.eq(df2), other=df1)

print("差異のあるdf1:\n", diff_df1)
print("\n差異のあるdf2:\n", diff_df2)

# 差異のある箇所を特定する際、indexやcolumnsを揃える必要があることに注意してください。

この方法は、差異のある要素を直接的に確認したい場合に便利です。

pandas.merge を用いた比較

merge メソッドは、共通の列に基づいて2つの DataFrame を結合します。差異のある行を特定するために使用できます。

import pandas as pd

df1 = pd.DataFrame({'col1': [1, 2, 3], 'col2': ['A', 'B', 'C'], 'id': [10, 20, 30]})
df2 = pd.DataFrame({'col1': [1, 4, 3], 'col2': ['A', 'X', 'C'], 'id': [10, 20, 30]})

# id をキーとして結合し、差異のある行を特定
merged_df = pd.merge(df1, df2, on='id', suffixes=('_df1', '_df2'))
diff_rows = merged_df[merged_df['col1_df1'] != merged_df['col1_df2']]
diff_rows = pd.concat([diff_rows, merged_df[merged_df['col2_df1'] != merged_df['col2_df2']]])

print(diff_rows)

この方法は、特定のキーに基づいて2つの DataFrame を比較し、差異のある行を特定する場合に有効です。

numpy.where を用いた比較

numpy.where は、条件に基づいて配列の要素を選択します。pandas DataFrame の values 属性を使用して NumPy 配列に変換し、比較を行うことができます。

import pandas as pd
import numpy as np

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

# NumPy配列に変換
arr1 = df1.values
arr2 = df2.values

# 差異のある要素を特定
diff_indices = np.where(arr1 != arr2)

print("差異のあるインデックス:\n", diff_indices)
print("\ndf1の差異のある要素:\n", df1.values[diff_indices])
print("\ndf2の差異のある要素:\n", df2.values[diff_indices])

この方法は、パフォーマンスが重要な場合や、より細かい制御が必要な場合に適しています。

独自の比較関数を実装

特定の比較ロジックが必要な場合は、独自の比較関数を実装できます。

import pandas as pd

def custom_compare(row1, row2):
    diff = {}
    for col in row1.index:
        if row1[col] != row2[col]:
            diff[col] = (row1[col], row2[col])
    return diff

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

diff_list = []
for index, row1 in df1.iterrows():
    row2 = df2.loc[index]
    diff = custom_compare(row1, row2)
    if diff:
        diff_list.append((index, diff))

print(diff_list)

この方法は、複雑な比較ロジックを実装する必要がある場合に柔軟性があります。