比较 Python Pandas DataFrames 以匹配行

Compare Python Pandas DataFrames for matching rows

我在 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

我想检查 df1 中是否存在来自另一个数据框 (df2) 的任何行(所有列)。这里是 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

是否可以通过将数据帧与另一个数据帧的行进行比较来搜索数据帧中的一组行?

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

您的问题的一个可能解决方案是使用 merge。检查 df1 中是否存在来自另一个数据帧 (df2) 的任何行(所有列)等同于确定两个数据帧的交集。这可以使用以下函数来完成:

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')

编辑:新问题(评论),已经确定了 df2 中也存在于第一个数据帧(df1)中的行,是否可以获取pd.merge() 然后删除 df2 中也存在于 df1

中的行

我不知道有什么直接的方法可以完成从 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: 如何从 df2 中删除 df1 中也存在的行,如@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

使用上述 DataFrame 以从 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)

@Andrew:我相信我找到了一种方法来删除一个数据帧中已经存在于另一个数据帧中的行(即回答我的编辑)而不使用循环 - 如果您不同意 and/or 如果我的 OP + EDIT 没有明确说明这一点:

这行得通

两个数据框的列始终相同 - ABCD。考虑到这一点,主要基于安德鲁的方法,这里是如何从 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'])]

第 3 行执行以下操作:

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

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