Pythonパンダ-2つのデータフレームの違いを見つける


103

2つのデータフレームdf1とdf2があります。ここで、df2はdf1のサブセットです。2つのデータフレームの違いである新しいデータフレーム(df3)を取得するにはどうすればよいですか?

言い換えると、df2にないdf1のすべての行/列を持つデータフレーム?

ここに画像の説明を入力してください


3
これを行う最も簡単な方法は、データフレームがどのように構造化されているか(つまり、インデックスを使用できるかどうかなど)によって異なります。これは、パンダの質問に再現可能な例を常に含める必要がある理由の良い例です。
cmaher 2018

1
データフレームのサンプル画像を追加しました
userPyGeo 2018

回答:


162

を使用して drop_duplicates

pd.concat([df1,df2]).drop_duplicates(keep=False)

Update :

Above method only working for those dataframes they do not have duplicate itself, For example

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

以下のように出力されますが、これは間違いです

間違った出力:

pd.concat([df1, df2]).drop_duplicates(keep=False)
Out[655]: 
   A  B
1  2  3

正しい出力

Out[656]: 
   A  B
1  2  3
2  3  4
3  3  4

それを達成する方法は?

方法1:で使用isinするtuple

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]
Out[657]: 
   A  B
1  2  3
2  3  4
3  3  4

方法2:mergeindicator

df1.merge(df2,indicator = True, how='left').loc[lambda x : x['_merge']!='both']
Out[421]: 
   A  B     _merge
1  2  3  left_only
2  3  4  left_only
3  3  4  left_only

4
重複を探すときに、どの列を考慮するかを決定することもできます。pd.concat([df1,df2]).drop_duplicates(subset = ['col1','col2'], keep=False)
Szpaqn19年

1
@Szpaqnは、このメソッドが特殊なケースを処理しないことに注意してください。:-)
BENY

データ型の1つがfloat(であるため12.00000000001 != 12)の場合、これにより予期しない行が結果に残る可能性があることに注意してください。より良い方法は、2つのデータフレームで設定されたIDの共通部分を見つけ、それに基づいて差を取得することです。
Jiāgěng

1
@DtechNetあなたは、2つのデータフレームと同じ名前を持つようにする必要があり
BENY

2
方法2(indicator=True)は非常に用途が広く便利なツールです。この回答の上部に表示したいのですが、3つの状況すべてをカバーするために、「外側」ではなく「左」結合を使用します。
mirekphd

34

行の場合Nameは、これを試してください。ここで、はジョイントインデックス列です(複数の共通列のリストにすることも、left_onandを指定することもできますright_on)。

m = df1.merge(df2, on='Name', how='outer', suffixes=['', '_'], indicator=True)

このindicator=True設定は_merge、との間のすべての変更df1を含むdf2、「left_only」、「right_only」、または「both」の3つの可能な種類に分類されるという列を追加するので便利です。

列については、これを試してください。

set(df1.columns).symmetric_difference(df2.columns)

9
反対票を投じてコメントしますか?mergewithindicator=Trueは、特定のフィールドごとにデータフレームを比較するための古典的なソリューションです。
JPP

9

受け入れられた回答方法1は、NaNが内部にあるデータフレームでは機能しませんpd.np.nan != pd.np.nan。これが最善の方法かどうかはわかりませんが、次の方法で回避できます。

df1[~df1.astype(str).apply(tuple, 1).isin(df2.astype(str).apply(tuple, 1))]

7

edit2、インデックスを設定せずに新しいソリューションを見つけました

newdf=pd.concat([df1,df2]).drop_duplicates(keep=False)

さて、私は最高票の答えがすでに私が理解したものを含んでいるのを見つけました。はい、このコードを使用できるのは、2つのdfのそれぞれに重複がないことを条件とします。


トリッキーな方法があります。まず、質問で指定された2つのデータフレームのインデックスとして「名前」を設定します。2つのdfに同じ「​​名前」があるので、「大きい」dfから「小さい」dfのインデックスを削除できます。これがコードです。

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)

1
あなたはおそらくpd.concat([DF1、DF2])を意味drop_duplicates(キープ=偽)。
マナスル

4
import pandas as pd
# given
df1 = pd.DataFrame({'Name':['John','Mike','Smith','Wale','Marry','Tom','Menda','Bolt','Yuswa',],
    'Age':[23,45,12,34,27,44,28,39,40]})
df2 = pd.DataFrame({'Name':['John','Smith','Wale','Tom','Menda','Yuswa',],
    'Age':[23,12,34,44,28,40]})

# find elements in df1 that are not in df2
df_1notin2 = df1[~(df1['Name'].isin(df2['Name']) & df1['Age'].isin(df2['Age']))].reset_index(drop=True)

# output:
print('df1\n', df1)
print('df2\n', df2)
print('df_1notin2\n', df_1notin2)

# df1
#     Age   Name
# 0   23   John
# 1   45   Mike
# 2   12  Smith
# 3   34   Wale
# 4   27  Marry
# 5   44    Tom
# 6   28  Menda
# 7   39   Bolt
# 8   40  Yuswa
# df2
#     Age   Name
# 0   23   John
# 1   12  Smith
# 2   34   Wale
# 3   44    Tom
# 4   28  Menda
# 5   40  Yuswa
# df_1notin2
#     Age   Name
# 0   45   Mike
# 1   27  Marry
# 2   39   Bolt

'〜'はどういう意味ですか?
PiotrekLeśniak

'〜'はブールインデックス用ではありません。参照:pandas.pydata.org/pandas-docs/stable/user_guide/...
SpeedCoder5

3

おそらく、同じまたは異なる列名を持つ、より単純なワンライナーです。df2 ['Name2']に重複する値が含まれている場合でも機能しました。

newDf = df1.set_index('Name1')
           .drop(df2['Name2'], errors='ignore')
           .reset_index(drop=False)

2
シンプルで効果的。宛先値がソース(つまり交差点)になく、最後にインデックスをリセットすると元の値と同様のdfが発生する場合の問題を解決するために、errors = 'ignore'が追加されました。
MRE

1

ここ で述べように

df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]

正しい解決策ですが、次の場合は間違った出力が生成されます

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

その場合、上記のソリューションは空のDataFrameを提供し ますが、代わりにconcat、各datframeから重複を削除した後にメソッドを使用する必要があります。

使用する concate with drop_duplicates

df1=df1.drop_duplicates(keep="first") 
df2=df2.drop_duplicates(keep="first") 
pd.concat([df1,df2]).drop_duplicates(keep=False)

質問の作成者は、df2にないdf1のすべての値を返すように求めました。したがって、df1[~df1.apply(tuple,1).isin(df2.apply(tuple,1))]この場合でも正解です。df1またはdf2のいずれかにあるが、両方にはない値を取得したい場合は、推奨されるアプローチが正しいです(元のデータフレームから重複を削除することに注意してください)。
IRA

0

既存のデータフレームのインデックスを変更する必要のない、素敵な@liangliのソリューションのわずかなバリエーション:

newdf = df1.drop(df1.join(df2.set_index('Name').index))

0

インデックスによる違いの検索。df1がdf2のサブセットであり、サブセット化時にインデックスが繰り越されると仮定します。

df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

# Example

df1 = pd.DataFrame({"gender":np.random.choice(['m','f'],size=5), "subject":np.random.choice(["bio","phy","chem"],size=5)}, index = [1,2,3,4,5])

df2 =  df1.loc[[1,3,5]]

df1

 gender subject
1      f     bio
2      m    chem
3      f     phy
4      m     bio
5      f     bio

df2

  gender subject
1      f     bio
3      f     phy
5      f     bio

df3 = df1.loc[set(df1.index).symmetric_difference(set(df2.index))].dropna()

df3

  gender subject
2      m    chem
4      m     bio


0

受け入れられた答えに加えて、任意の/を持つ2つのデータフレームの2Dセットの違いを見つけることができるもう1つのより広いソリューションを提案したいと思います(両方のデータラムで一致しない可能性があります)。また、メソッドでは、データフレーム比較のために要素の許容値を設定できます(使用します)indexcolumnsfloatnp.isclose


import numpy as np
import pandas as pd

def get_dataframe_setdiff2d(df_new: pd.DataFrame, 
                            df_old: pd.DataFrame, 
                            rtol=1e-03, atol=1e-05) -> pd.DataFrame:
    """Returns set difference of two pandas DataFrames"""

    union_index = np.union1d(df_new.index, df_old.index)
    union_columns = np.union1d(df_new.columns, df_old.columns)

    new = df_new.reindex(index=union_index, columns=union_columns)
    old = df_old.reindex(index=union_index, columns=union_columns)

    mask_diff = ~np.isclose(new, old, rtol, atol)

    df_bool = pd.DataFrame(mask_diff, union_index, union_columns)

    df_diff = pd.concat([new[df_bool].stack(),
                         old[df_bool].stack()], axis=1)

    df_diff.columns = ["New", "Old"]

    return df_diff

例:

In [1]

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

print("df1:\n", df1, "\n")

print("df2:\n", df2, "\n")

diff = get_dataframe_setdiff2d(df1, df2)

print("diff:\n", diff, "\n")
Out [1]

df1:
   A  C
0  2  2
1  1  1
2  2  2 

df2:
   A  B
0  1  1
1  1  1 

diff:
     New  Old
0 A  2.0  1.0
  B  NaN  1.0
  C  2.0  NaN
1 B  NaN  1.0
  C  1.0  NaN
2 A  2.0  NaN
  C  2.0  NaN 
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.