質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

Q&A

解決済

2回答

1494閲覧

pandasのDataFrameのCompareが何をしているのかを知りたい。

xail2222

総合スコア1497

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

pandas

Pandasは、PythonでRにおけるデータフレームに似た型を持たせることができるライブラリです。 行列計算の負担が大幅に軽減されるため、Rで行っていた集計作業をPythonでも比較的簡単に行えます。 データ構造を変更したりデータ分析したりするときにも便利です。

0グッド

0クリップ

投稿2021/12/30 11:33

前提・実現したいこと

pandasのDataFrameのCompareが何をしているのかを知りたい。

pandas.DataFrame.compare
これを読むと、差分を出力してくれる。という事はわかります。
ただ、差があるとはどうやって判定しているのかが分りません。

pandasからSource RepositoryをクリックしてGithubへ行って、DataFrameのクラスの定義から
compareのメソッドの定義を見てみたのですが
途中から追いかける事が出来ず、正確に何をしているのかが読み取れませんでした。

試したこと

python

1import pandas as pd 2 3class MyClass : 4 user_name = None 5 age = None 6 def __str__(self) -> str: 7 return "名前:{0}、年齢:{1}".format ( self.user_name , self.age ) 8 def __eq__(self, __o: object) -> bool: 9 return self.age==__o.age 10 11user1 = MyClass() 12user1.user_name="takeda" 13user1.age=12 14 15user2 = MyClass() 16user2.user_name="takeda2" 17user2.age=13 18 19user3 = MyClass() 20user3.user_name="takeda3" 21user3.age=12 22 23a=[user1,user3,user3] 24b=[user3,user2,None] 25 26df1=pd.DataFrame({'A':a}) 27df2=pd.DataFrame({'A':b}) 28 29print(df1.compare(df2))
この結果が A self other 1 名前:takeda3、年齢:12 名前:takeda2、年齢:13 2 名前:takeda3、年齢:12 None となり、user1とuser3はuser_nameが違うにも関わらず同じと判定しており user3とNoneも違うと判定してくれています。 この事からNoneとNone以外は違うと判定して その後、==つまり__eq__で判定して__str__で内容を出力してくれるのだと感じます。

この解釈で有っているのでしょうか。
何かご存知の方よろしくお願いします。

気になった理由

二つのDataFrame間の差分を調べる事があったのですが
本当にちゃんと差分を出力してくれているのかが気になり
その為、どういう風に差分の出し方をしているのかが疑問になった次第です。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

pandas.DataFrame.compare() の実体は pandas.core.frame.NDFrame.compare で、ソースコードは以下になります。
pandas.core.frame.NDFrame.compare

処理内容を大幅に簡略化すると以下の様になっていますので、1ステップづつ確認するとよいかと思います。

python

1mask = ~(df1 == df2) 2df1 = df1.where(mask) 3df2 = df2.where(mask) 4 5cmask = mask.any() # column mask 6rmask = mask.any(axis=1) # row mask 7df1 = df1.loc[rmask, cmask] 8df2 = df2.loc[rmask, cmask] 9 10diff = pd.concat([df1, df2], axis=1, keys=['self', 'other']) 11 12print(diff) 13 14# 15 self other 16 A A 171 名前:takeda3、年齢:12 名前:takeda2、年齢:13 182 名前:takeda3、年齢:12 None

投稿2021/12/30 13:16

編集2021/12/30 13:18
melian

総合スコア19749

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

xail2222

2021/12/30 14:33

ちょうどそこからわからなくなった所でした。 ~((self == other) | (self.isna() & other.isna())) としている「(self.isna() & other.isna())」が理由でDataFrame同士の==とcompareが違う結果となるってことなのですね。 後は、DataFrameの親クラスであるNDFrameに対して、df1 == df2なので、NDFrameの__eq__を使っているのですよね。 このNDFrameの__eq__の実体が、どのように定義されているかを調べる方法はわかりますでしょうか。
melian

2021/12/30 14:40

実は NDFrame の __eq__ は使われていないのです。使われているのは df1 や df2 の中身(カラム A) である、MyClass.__eq__ が使われているのです。つまり、 user1 == user3 が True になるから、がこの結果の原因なのです。
xail2222

2021/12/30 15:06

ん… class NDFrame(PandasObject, indexing.IndexingMixin): の中の次のメソッド def compare( self, other, align_axis: Axis = 1, keep_shape: bool_t = False, keep_equal: bool_t = False, ): の中の self == other ですよね。 なのでselfはNDFrameじゃないのですか? だとするとself == otherでは、NDFrame の __eq__ が使われているのではないでしょうか。 確かに実際に最下位で使われているのはMyClass.__eq__ だとは思うのですが…
melian

2021/12/30 15:13

self == other とする場合、これはデータフレーム(NDFrame 型のインスタンス)の同名の列の要素ごとの比較になります。
xail2222

2021/12/30 15:26

確かにそれに似た仕様だとは思うのですが、それを実現するための実装はどこでどうやっているのかな?と思った次第です。 実際私の質問に記載した例であれば、__eq__は2回しか呼ばれませんでした。 その為、ただ単に要素ごとに__eq__を呼んでいるわけではないと感じました
melian

2021/12/30 15:33 編集

a=[user1,user3,user3] b=[user3,user2,None] となっていて、b に None が含まれていますので 2 回しか呼ばれません。 NDFrame には __eq__ が無いので、親クラスを調べています。何か判ったらまたコメントします。
xail2222

2021/12/30 15:49

>となっていて、b に None が含まれていますので 2 回しか呼ばれません。 確かに、結果はそうだと思います。 しかし「print(user3 == None)」を実行すると、MyClass.__eq__が呼び出されエラーが発生します。 つまり、要素をそれぞれチェックし一方がNoneであれば、比較しないという処理が実装されているものだと思われます。 >NDFrame には __eq__ が無いので、親クラスを調べています。何か判ったらまたコメントします。 すみません。お手数おかけしています。 私には今の所、追い切れていないです。 動作から仕様をある程度、推測することは出来るのですが…
xail2222

2021/12/31 00:55 編集

ありがとうございます。 色々試していると、pandasが途中までデバッグ実行出来たのでその後追っていると result = libops.vec_compare(x.ravel(), y.ravel(), op) https://github.com/pandas-dev/pandas/blob/master/pandas/core/ops/array_ops.py#L74 ここまではステップ実行できました。 この後がステップインしてくれなくて、追い切れませんでした。 libops、opsのソースもよくわからないし、公開されている所だと、ここまでということでしょうか。
xail2222

2021/12/31 07:31

中身が良くわからないbuilt-in functionに行きついてしまってよくわからなかったのですが しかし「mask = ~((self == other) | (self.isna() & other.isna()))」という事から ==との差異がわかり、==は、個別比較した結果を返すということで納得することにします。 個別要素のNoneとNone以外の比較時は、==を使わずFalseで返す。というところの処理まで見たかったですが、動作から多分それで間違いないと思うので、ここで終わりとします。 色々とありがとうございました。
melian

2021/12/31 07:32

はい、了解です。ベストアンサーにお選びいただき、どうもありがとうございました。
guest

0

  • この事からNoneとNone以外は違うと判定して

その後、==つまり__eq__で判定して

その考え方だと、

python

1>>> df3 = pd.DataFrame({'A':[1,2,3]}) 2>>> df3.compare(df1) 3 A 4 self other 50 1 名前:takeda、年齢:12 61 2 名前:takeda3、年齢:12 72 3 名前:takeda3、年齢:12 8>>> 1 == user1 9Traceback (most recent call last): 10 File "<stdin>", line 1, in <module> 11 File "<stdin>", line 7, in __eq__ 12AttributeError: 'int' object has no attribute 'age' 13>>> user1 == 1 14Traceback (most recent call last): 15 File "<stdin>", line 1, in <module> 16 File "<stdin>", line 7, in __eq__ 17AttributeError: 'int' object has no attribute 'age'

が説明できないのではないでしょうか。
Noneであるかどうかに関係なく__eq__が失敗すれば等しくないと判断しているのでしょう。

  • __str__で内容を出力してくれるのだと感じます。

python

1>>> diff = df1.compare(df2) 2>>> type(diff.iloc[0,0]) 3<class '__main__.MyClass'>

ですので、diffの段階では、__str__はまだ使われていません。表示する段階で、pandasDataFrameの仕様として__str__が使われます。

python

1>>> diff 2 A 3 self other 41 名前:takeda3、年齢:12 名前:takeda2、年齢:13 52 名前:takeda3、年齢:12 None

投稿2021/12/30 12:50

ppaul

総合スコア24666

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

xail2222

2021/12/30 13:43

確かにNone じゃない同士の判定で__eq__を使っているとするとおかしいですね。あと、__str__もprintの時点で出力されるという事ですね。 指摘ありがとうございます。
xail2222

2021/12/30 14:57

>Noneであるかどうかに関係なく__eq__が失敗すれば等しくないと判断しているのでしょう。 の部分ですがvscodeでprint(df1.compare(df2))の所をステップインした所 定義した__eq__の関数が2回しか呼ばれませんでした。 この事から、型もチェックしているのでは?という感じがしました。 またdf3.compare(df1)は回答頂いた通りの動作でしたが df1.compare(df3)はエラーが発生して動作しませんでした。 __eq__でエラーが発生した場合はトラップせずにそのままエラーだと思いました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問