比较Python Pandas数据框以匹配行。

42

我有一个Pandas中的DataFrame (df1):

df1 = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
print df1

       A         B         C         D
0.860379  0.726956  0.394529  0.833217
0.014180  0.813828  0.559891  0.339647
0.782838  0.698993  0.551252  0.361034
0.833370  0.982056  0.741821  0.006864
0.855955  0.546562  0.270425  0.136006
0.491538  0.445024  0.971603  0.690001
0.911696  0.065338  0.796946  0.853456
0.744923  0.545661  0.492739  0.337628
0.576235  0.219831  0.946772  0.752403
0.164873  0.454862  0.745890  0.437729

我想检查另一个数据帧(df2)中的任何一行(所有列)是否存在于df1中。以下是df2

df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
print df2

           A         B         C         D
    0.855955  0.546562  0.270425  0.136006
    0.491538  0.445024  0.971603  0.690001
    0.911696  0.065338  0.796946  0.853456
    0.744923  0.545661  0.492739  0.337628
    0.576235  0.219831  0.946772  0.752403
    2.000000  3.000000  4.000000  5.000000
   14.000000 15.000000 16.000000 17.000000

我尝试使用df.lookup逐行搜索。 我是这样做的:

list1 = df2.ix[0].tolist()
cols = df1.columns.tolist()
print df1.lookup(list1, cols)

但我收到了这个错误信息:

  File "C:\Users\test.py", line 19, in <module>
    print df1.lookup(list1, cols)
  File "C:\python27\lib\site-packages\pandas\core\frame.py", line 2217, in lookup
    raise KeyError('One or more row labels was not found')
KeyError: 'One or more row labels was not found'

我也尝试过使用.all()

print (df2 == df1).all(1).any()

但是我遇到了这个错误信息:

  File "C:\Users\test.py", line 12, in <module>
    print (df2 == df1).all(1).any()
  File "C:\python27\lib\site-packages\pandas\core\ops.py", line 884, in f
    return self._compare_frame(other, func, str_rep)
  File "C:\python27\lib\site-packages\pandas\core\frame.py", line 3010, in _compare_frame
    raise ValueError('Can only compare identically-labeled '
ValueError: Can only compare identically-labeled DataFrame objects

我也尝试过像这样使用isin()

print df2.isin(df1)

但是我到处得到的都是False,这是不正确的:

    A      B      C      D
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False
False  False  False  False

能否通过比较两个数据框的行来搜索一组数据框中的行?

编辑:如果这些行在df1中也存在,是否可以删除df2中的这些行?

3个回答

60

你的问题可能有一个解决方案,那就是使用merge。检查另一个数据框(df2)中是否存在任何行(所有列)与df1相同,相当于确定两个数据框的交集。可以使用以下函数来实现:

pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner')

例如,如果df1是
    A           B            C          D
0   0.403846    0.312230    0.209882    0.397923
1   0.934957    0.731730    0.484712    0.734747
2   0.588245    0.961589    0.910292    0.382072
3   0.534226    0.276908    0.323282    0.629398
4   0.259533    0.277465    0.043652    0.925743
5   0.667415    0.051182    0.928655    0.737673
6   0.217923    0.665446    0.224268    0.772592
7   0.023578    0.561884    0.615515    0.362084
8   0.346373    0.375366    0.083003    0.663622
9   0.352584    0.103263    0.661686    0.246862

而df2被定义为:

     A          B            C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622
5   2.000000    3.000000    4.000000    5.000000
6   14.000000   15.000000   16.000000   17.000000

函数 pd.merge(df1, df2, on=['A', 'B', 'C', 'D'], how='inner') 产生以下结果:

     A           B           C           D
0   0.259533    0.277465    0.043652    0.925743
1   0.667415    0.051182    0.928655    0.737673
2   0.217923    0.665446    0.224268    0.772592
3   0.023578    0.561884    0.615515    0.362084
4   0.346373    0.375366    0.083003    0.663622

结果是df1和df2中都有的所有行(所有列)。
如果df1和df2中的列不相同,我们也可以修改此示例,并仅比较某些列的相同行值。如果我们修改原始示例:
df1 = pd.DataFrame(np.random.rand(10,4),columns=list('ABCD'))
df2 = df1.ix[4:8]
df2.reset_index(drop=True,inplace=True)
df2.loc[-1] = [2, 3, 4, 5]
df2.loc[-2] = [14, 15, 16, 17]
df2.reset_index(drop=True,inplace=True)
df2 = df2[['A', 'B', 'C']] # df2 has only columns A B C

然后我们可以使用common_cols = list(set(df1.columns) & set(df2.columns))查看两个数据框之间的公共列,然后进行合并:

pd.merge(df1, df2, on=common_cols, how='inner')

编辑: 新问题(评论),已经确定了在第一个数据框(df1)中也存在的df2行,是否有可能获取pd.merge()的结果并删除也存在于df1中的df2行?

我不知道有没有一种简单的方法来完成从df2中删除也存在于df1中的行的任务。 话虽如此,您可以使用以下方法:

ds1 = set(tuple(line) for line in df1.values)
ds2 = set(tuple(line) for line in df2.values)
df = pd.DataFrame(list(ds2.difference(ds1)), columns=df2.columns)

也许有更好的方法来完成这个任务,但我不知道这样的方法/函数。

编辑2:如何删除df1中也在df2中出现的行,如@WR答案所示。

提供的方法df2 [~ df2 ['A'] .isin(df12 ['A'])]不能考虑所有类型的情况。考虑以下数据框:

df1:

   A  B  C  D
0  6  4  1  6
1  7  6  6  8
2  1  6  2  7
3  8  0  4  1
4  1  0  2  3
5  8  4  7  5
6  4  7  1  1
7  3  7  3  4
8  5  2  8  8
9  3  2  8  4

df2:

   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8
5  1  1  1  1
6  2  2  2  2

df12:

   A  B  C  D
0  1  0  2  3
1  8  4  7  5
2  4  7  1  1
3  3  7  3  4
4  5  2  8  8

使用上述数据框的目标是从df2中删除也存在于df1中的行,将会得到以下结果:
   A  B  C  D
0  1  1  1  1
1  2  2  2  2

行(1, 1, 1, 1)和(2, 2, 2, 2)在df2中,而不在df1中。不幸的是,使用提供的方法(df2[~df2['A'].isin(df12['A'])])会导致:

   A  B  C  D
6  2  2  2  2

这是因为在交集DataFrame(即(1,0,2,3))和df2中都找到了列A中的值1,因此删除了(1,0,2,3)和(1,1,1,1)。这是无意的,因为行(1,1,1,1)不在df1中,不应该被删除。
我认为以下方法可以解决问题。它创建了一个虚拟列,稍后用于对DataFrame进行子集选择以获得所需结果:
df12['key'] = 'x'
temp_df = pd.merge(df2, df12, on=df2.columns.tolist(), how='left')
temp_df[temp_df['key'].isnull()].drop('key', axis=1)

当然!这是SQL的“内联接(INNER JOIN)”。我没想到。问题在于,我从未用过它来“连结”多列。“如果要检查所有数据框的列,您能否将on = ['A', 'B', 'C', 'D']替换为on = df1.columns?” - edesz
1
你可以使用 on=list(df1.columns) 或等价的 on=list(df2.columns)。如果你想要检查行是否相同(所有列),则 df1 和 df2 中的列必须是相同的。 - Andrew
Andrew,最后一个问题(我也添加到原始帖子中)-已经确定了在第一个数据框(df1)中也存在的来自df2的行,是否可以获取pd.merge()的结果,然后删除在df1中也存在的df2中的行? - edesz
在EDIT 2中,现在似乎您有两个起始数据框df12df2。您是不是想说df2['key'] = 'x'temp_df = pd.merge(df2, df1, on=df2.col...... - edesz
从上面的例子中,pd.merge() 应该生成 <= max(len(df1), len(df2)) 项。但是当我执行 pd.merge(df1, df2, on=df1.columns.tolist()[:-1], how='inner') 时,输出结果却是 > max(len(df1), len(df2))?我错过了什么吗? - Vishal

13

@Andrew: 我相信我找到了一种方法,可以删除一个数据框中已经存在于另一个数据框中的行(即回答我的编辑),而不使用循环 - 如果您不同意和/或我的原帖+编辑没有清楚地说明这一点,请告诉我:

这个方法有效

这两个数据框的列始终相同 - A,B,CD。基于 Andrew 的方法,以下是如何从df2中删除也存在于df1中的行:

common_cols = df1.columns.tolist()                         #generate list of column names
df12 = pd.merge(df1, df2, on=common_cols, how='inner')     #extract common rows with merge
df2 = df2[~df2['A'].isin(df12['A'])]

第三行执行以下操作:

  • df2中仅提取与df1不匹配的行:
  • 为了使两行不同,其中任何一行的相应列必须与另一行的列不同。
  • 在这里,我选择列A进行比较 - 可以使用任何列名称,但不是所有列名称都可以使用。

注意:此方法本质上等同于SQL的NOT IN()方法。


你无法想象我在尝试使用循环来完成这个任务时浪费了多少时间。 - edesz
1
我认为您的逻辑可能有问题(尽管我可能会误解您期望的结果)。我已经相应地更新了我的答案。 - Andrew
你是正确的。你的解决方案更好。感谢你指出这一点给我。 - edesz
在这条命令中,“~”运算符的含义是什么? df2 = df2 [〜df2 ['A'] .isin(df12 ['A'])] - user3698773
1
它意味着从df2的列A值,这些值不在df12的列A中。 - edesz
@edesz pandas有一个drop_duplicates函数,可能更加直观。 - Jason Li

0
我知道这个帖子很旧,但是我为了以后自己参考而添加这些注释...
使用pd.concat()然后删除重复项。
合并两个数据框
df3 = pd.concat([df1,df2])
重复行的数据框

df3[df3.duplicated()]

从两个数据框中获取不重复的行的数据框

df3.drop_duplicates().reset_index(drop='index')

直截了当地回答这个问题,我会采取以下措施:
# set a new column to label the dataframes
df1['df'] = 'df1'
df2['df'] = 'df2'
# combine dataframes
df3 = pd.concat([df1,df2)])
# setting duplicated(keep=False) keeps all duplicated rows from both dataframes 
# then we can set a mask to just show the rows from df1
df3[(df3.duplicated(['A','B','C','D'],keep=False)) & df3['df'] == df1)]

# if you don't want to list out all of the columns you can run it like this:
df3[(df3.duplicated(df3.loc[:,df3.columns != 'df'].columns,keep=False)) & df3['df'] == df1)]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接