簡體   English   中英

查找兩個數據框之間的差異

[英]Find difference between two data frames

我有兩個數據幀 df1 和 df2,其中 df2 是 df1 的子集。 我如何獲得一個新的數據幀(df3),這是兩個數據幀之間的區別?

換句話說,一個數據框包含 df1 中不在 df2 中的所有行/列?

在此處輸入圖像描述

通過使用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:將isintuple

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

方法二:與indicator merge

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

對於行,試試這個,其中Name是聯合索引列(可以是多個公共列的列表,或指定left_onright_on ):

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

indicator=True設置很有用,因為它添加了一個名為_merge的列,其中包含df1df2之間的所有更改,分為 3 種可能的類型:“left_only”、“right_only”或“both”。

對於列,試試這個:

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

接受的答案方法 1 不適用於內部包含 NaN 的數據幀,如pd.np.nan != pd.np.nan 我不確定這是否是最好的方法,但可以通過以下方式避免

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

它更慢,因為它需要將數據轉換為字符串,但多虧了這種轉換pd.np.nan == pd.np.nan

讓我們來看看代碼。 首先我們將值轉換為字符串,並將tuple函數應用於每一行。

df1.astype(str).apply(tuple, 1)
df2.astype(str).apply(tuple, 1)

多虧了這一點,我們得到了帶有元組列表的pd.Series對象。 每個元組包含來自df1 / df2整行。 然后我們在df1上應用isin方法來檢查每個元組是否“在” df2 結果是帶有 bool 值的pd.Series 如果來自df1元組在df2則為真。 最后,我們用~符號否定結果,並在df1上應用過濾器。 長話短說,我們只從df1中獲取那些不在df2

為了使其更具可讀性,我們可以將其寫為:

df1_str_tuples = df1.astype(str).apply(tuple, 1)
df2_str_tuples = df2.astype(str).apply(tuple, 1)
df1_values_in_df2_filter = df1_str_tuples.isin(df2_str_tuples)
df1_values_not_in_df2 = df1[~df1_values_in_df2_filter]

edit2,我想出了一個不需要設置索引的新解決方案

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

好的,我發現最高投票的答案已經包含了我所想的。 是的,我們只能在每兩個 dfs 中沒有重復的條件下使用此代碼。


我有一個棘手的方法。 首先,我們將“名稱”設置為問題給出的兩個數據幀的索引。 由於我們在兩個 dfs 中有相同的“名稱”,我們可以從“較大”的 df 中刪除“較小”的 df 索引。 這是代碼。

df1.set_index('Name',inplace=True)
df2.set_index('Name',inplace=True)
newdf=df1.drop(df2.index)
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

也許是更簡單的單行,具有相同或不同的列名。 即使 df2['Name2'] 包含重復值也能工作。

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

除了公認的答案之外,我還想提出一個更廣泛的解決方案,它可以找到具有任何index / columns的兩個數據幀的二維集差異(兩個數據幀可能不重合)。 方法還允許為數據幀比較的float元素設置容差(它使用np.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 

正如這里提到的那樣

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]})

在這種情況下,上述解決方案將提供Empty DataFrame ,而您應該在從每個數據幀中刪除重復項后使用concat方法。

concate with drop_duplicates

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

Pandas 現在提供了一個新的 API來做數據幀差異: pandas.DataFrame.compare

df.compare(df2)
  col1       col3
  self other self other
0    a     c  NaN   NaN
2  NaN   NaN  3.0   4.0

不錯的@lianli 解決方案的一個細微變化,不需要更改現有數據幀的索引:

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

按索引查找差異。 假設 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

當一側有重復項而另一側至少有一個重復項時,我在處理重復項時遇到了問題,所以我使用Counter.collections來做一個更好的差異,確保雙方具有相同的計數。 這不會返回重復項,但如果雙方的計數相同,則不會返回任何重復項。

from collections import Counter

def diff(df1, df2, on=None):
    """
    :param on: same as pandas.df.merge(on) (a list of columns)
    """
    on = on if on else df1.columns
    df1on = df1[on]
    df2on = df2[on]
    c1 = Counter(df1on.apply(tuple, 'columns'))
    c2 = Counter(df2on.apply(tuple, 'columns'))
    c1c2 = c1-c2
    c2c1 = c2-c1
    df1ondf2on = pd.DataFrame(list(c1c2.elements()), columns=on)
    df2ondf1on = pd.DataFrame(list(c2c1.elements()), columns=on)
    df1df2 = df1.merge(df1ondf2on).drop_duplicates(subset=on)
    df2df1 = df2.merge(df2ondf1on).drop_duplicates(subset=on)
    return pd.concat([df1df2, df2df1])
> df1 = pd.DataFrame({'a': [1, 1, 3, 4, 4]})
> df2 = pd.DataFrame({'a': [1, 2, 3, 4, 4]})
> diff(df1, df2)
   a
0  1
0  2

我發現deepdiff庫是一個很棒的工具,如果需要不同的細節或排序問題,它也可以很好地擴展到數據幀。 您可以嘗試to_dict('records')to_numpy()和其他導出:

import pandas as pd
from deepdiff import DeepDiff

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

df2 = df1[df1.Name.isin(['John','Smith','Wale','Tom','Menda','Yuswa'])]

DeepDiff(df1.to_dict(), df2.to_dict())
# {'dictionary_item_removed': [root['Name'][1], root['Name'][4], root['Name'][7], root['Age'][1], root['Age'][4], root['Age'][7]]}

pandas DataFrame.compare中有一個新方法比較 2 個不同的數據幀並返回數據記錄的每一列中更改的值。

例子

第一 Dataframe

Id Customer Status      Date
1      ABC   Good  Mar 2023
2      BAC   Good  Feb 2024
3      CBA    Bad  Apr 2022

第二個 Dataframe

Id Customer Status      Date
1      ABC    Bad  Mar 2023
2      BAC   Good  Feb 2024
5      CBA   Good  Apr 2024

比較數據幀

print("Dataframe difference -- \n")
print(df1.compare(df2))

print("Dataframe difference keeping equal values -- \n")
print(df1.compare(df2, keep_equal=True))

print("Dataframe difference keeping same shape -- \n")
print(df1.compare(df2, keep_shape=True))

print("Dataframe difference keeping same shape and equal values -- \n")
print(df1.compare(df2, keep_shape=True, keep_equal=True))

結果

Dataframe difference -- 

    Id       Status            Date          
  self other   self other      self     other
0  NaN   NaN   Good   Bad       NaN       NaN
2  3.0   5.0    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping equal values -- 

    Id       Status            Date          
  self other   self other      self     other
0    1     1   Good   Bad  Mar 2023  Mar 2023
2    3     5    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping same shape -- 

    Id       Customer       Status            Date          
  self other     self other   self other      self     other
0  NaN   NaN      NaN   NaN   Good   Bad       NaN       NaN
1  NaN   NaN      NaN   NaN    NaN   NaN       NaN       NaN
2  3.0   5.0      NaN   NaN    Bad  Good  Apr 2022  Apr 2024

Dataframe difference keeping same shape and equal values -- 

    Id       Customer       Status            Date          
  self other     self other   self other      self     other
0    1     1      ABC   ABC   Good   Bad  Mar 2023  Mar 2023
1    2     2      BAC   BAC   Good  Good  Feb 2024  Feb 2024
2    3     5      CBA   CBA    Bad  Good  Apr 2022  Apr 2024

使用 lambda 函數,您可以過濾具有_merge“left_only”的行,以獲取df1df2中缺少的所有行

df3 = df1.merge(df2, how = 'outer' ,indicator=True).loc[lambda x :x['_merge']=='left_only']
df

定義我們的數據幀:

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

df2 = df1[df1.Name.isin(['John','Smith','Wale','Tom','Menda','Yuswa'])

df1

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

df2

    Name  Age
0   John   23
2  Smith   12
3   Wale   34
5    Tom   44
6  Menda   28
8  Yuswa   40

兩者之間的區別是:

df1[~df1.isin(df2)].dropna()

    Name   Age
1   Mike  45.0
4  Marry  27.0
7   Bolt  39.0

在哪里:

  • df1.isin(df2)返回df1中也在df2
  • ~ (逐元素邏輯NOT)在表達前否定的結果,所以我們得到在元件df1df2兩者之間-the差。
  • .dropna()刪除NaN表示所需輸出的行

注意這僅在len(df1) >= len(df2) 如果df2df1長,您可以反轉表達式: df2[~df2.isin(df1)].dropna()

對稱差異

如果您對僅在一個數據框中而不是兩個數據框中的行感興趣,則您正在尋找集合差異:

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

⚠️ 僅當兩個數據幀不包含任何重復項時才有效。

集差/關系代數差

如果您對關系代數差異/集合差異感興趣,即df1-df2df1\df2

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

⚠️ 僅當兩個數據幀不包含任何重復項時才有效。

試試這個: df_new = df1.merge(df2, how='outer', indicator=True).query('_merge == "left_only"').drop('_merge', 1)

它將產生一個新的 dataframe,但不同之處在於:df1 中存在但 df2 中不存在的值。

另一種可能的解決方案是使用numpy broadcasting

df1[np.all(~np.all(df1.values == df2.values[:, None], axis=2), axis=0)]

Output:

    Name  Age
1   Mike   45
4  Marry   27
7   Bolt   39

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM