Python
 Computer >> コンピューター >  >> プログラミング >> Python

欠損値を含むPython Pandasの2つのDataFrameを比較する方法

はじめに

Pandasでは、欠損値(missing value)を表すためにNumPyのNaN(np.nan)オブジェクトを使用します。このNumPyのNaN値には、興味深い数学的な特性がいくつかあります。例えば、NaNは自分自身と等しくないという性質を持っています。一方、PythonのNoneオブジェクトは、自分自身と比較するとTrueと評価されます。

np.nanの基本的な挙動

まず、いくつかの例を見て、np.nanがどのように振る舞うのかを理解しましょう。

import pandas as pd
import numpy as np

# PythonのNoneオブジェクトを自分自身と比較
print(f"Output \n *** {None == None} ")

出力

*** True

次に、NumPyのNaNを自分自身と比較してみます。

# NumPyのnanを自分自身と比較
print(f"Output \n *** {np.nan == np.nan} ")

出力

*** False

さらに、NaNが10より大きいかどうかを確認してみます。

# nanは10より大きい?
print(f"Output \n *** {np.nan > 10} ")

出力

*** False

サンプルDataFrameの作成

従来、SeriesやDataFrameの比較には等価演算子「==」が使われてきました。比較の結果はオブジェクトとして返されます。まずは、テニス選手とそのグランドスラム優勝回数を含むDataFrameを作成しましょう。

# テニス選手とグランドスラム優勝回数のDataFrameを作成
df = pd.DataFrame(data={"players": ['Federer', 'Nadal', 'Djokovic', 'Murray', 'Medvedev', 'Zverev'],
"titles": [20, 19, 17, 3, np.nan, np.nan]})
# インデックスを設定
df.index = df['players']

# インデックスを昇順でソート
df.sort_index(inplace=True, ascending=True)

# インデックスが設定されているか確認
df.index.is_monotonic_increasing

# レコードを表示
print(f"Output \n{df}")

出力

         players  titles
players                 
Djokovic Djokovic    17.0
Federer   Federer    20.0
Medvedev Medvedev     NaN
Murray     Murray     3.0
Nadal       Nadal    19.0
Zverev     Zverev     NaN

==演算子による比較

1. 理解を深めるために、まずすべてのプレイヤーをスカラー値「Federer」と比較して、結果を確認してみましょう。

print(f'Output \n {df == "Federer"}')

出力

         players  titles
players                 
Djokovic   False   False
Federer     True   False
Medvedev   False   False
Murray     False   False
Nadal      False   False
Zverev     False   False

2. これは期待どおりに動作しますが、欠損値を含むDataFrame同士を比較しようとすると問題が発生します。この様子を確認するために、dfをそれ自身と比較してみましょう。

df_compare = df == df
print(f'Output \n {df_compare}')

出力

         players  titles
players                 
Djokovic    True    True
Federer     True    True
Medvedev    True   False
Murray      True    True
Nadal       True    True
Zverev      True   False

3. 一見すると、すべての値が期待どおりに見えるかもしれません。しかし、.allメソッドを使って各列にTrueのみが含まれているかどうかを確認すると(同じオブジェクト同士を比較しているのですから、そうなるはずですよね?)、予期しない結果が得られます。

print(f'Output \n {df_compare.all()}')

出力

players     True
titles     False
dtype: bool

4. 前述のとおり、これは欠損値同士が互いに等しいと評価されないために起こります。MedvedevとZverevには優勝タイトルがない(つまりNaNである)ことは明らかなので、各列の欠損値の数を合計すれば、titles列では2、players列では0になるはずです。実際に何が起こるか見てみましょう。

print(f'Output \n {(df_compare == np.nan).sum()}')

出力

players    0
titles     0
dtype: int64

5. 上記の結果は予期しないものです。これはnanが非常に特殊な振る舞いをするためです。

.equals()メソッドによる正しい比較方法

6. 2つのDataFrame全体を正しく比較する方法は、等価演算子(==)ではなく、.equalsメソッドを使用することです。このメソッドは、同じ位置にあるNaN同士を等しいものとして扱います。

重要な注意点として、.eqメソッドは.equalsではなく、==と同等であることに注意してください。

print(f'Output \n {df_compare.equals(df_compare)}')

出力

True

assert_frame_equalによる単体テスト

7. 単体テストの一環として2つのDataFrameを比較したい場合には、もう一つの方法があります。assert_frame_equal関数は、2つのDataFrameが等しくない場合にAssertionErrorを発生させます。2つのDataFrameが等しい場合はNoneを返します。

from pandas.testing import assert_frame_equal
print(f'Output \n {assert_frame_equal(df_compare, df_compare) is None}')

出力

True

まとめ

欠損値(NaN)を含むDataFrameを比較する際には、==演算子ではなく.equals()メソッドを使用することが重要です。また、単体テストではpandas.testing.assert_frame_equal関数を活用することで、安全にDataFrameの等価性を検証できます。NaNの特殊な性質(自分自身とも等しくない)を理解しておくことで、データ分析における予期しないバグを防ぐことができます。

  1. Pythonで2つのタプルを比較するにはどうすればよいですか?

    Pythonでは、タプルは辞書式順序(lexicographical order)で比較されます。まず最初のタプルの1番目の要素と2番目のタプルの1番目の要素が比較され、等しくなければその時点で比較結果が確定します。等しい場合は2番目の要素、次に3番目の要素…というように、順番に比較が進んでいきます。例:タプル同士の大小比較>>> a = (1, 2, 3) >>> b = (1, 2, 5) >>> a < b Trueこの例では、最初の2つの要素(1と2)はどちらのタプルでも等しいため、比較は3番目の要素に進みます。「3 <

  2. Pythonで2つのリストを比較する方法:セット(set)を使って共通要素を簡単に取得しよう

    Pythonで2つのリストを比較するには? Pythonで2つのリストを比較し、共通する要素を取り出したい場合、最も簡単な方法はセット(set、集合)を使うことです。リストをセットに変換すると、重複する値が自動的に取り除かれ、一意の値だけが残ります。その上で、& 演算子を使用すると、積集合(インターセクション)のように動作し、両方のリストに存在する共通のオブジェクトを簡単に取得できます。 & 演算子を使った例 >>> a = [1, 2, 3, 4, 5] >>> b = [9, 8, 7, 6, 5] >>> set(a