pandas获取不在其他数据帧中的行

时间:2015-03-06 15:10:28

标签: python pandas dataframe

我有两个pandas数据框,它们有一些共同的行。

假设dataframe2是dataframe1的子集。

如何获取不在dataframe2中的dataframe1行?

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})

15 个答案:

答案 0 :(得分:125)

一种方法是将内部合并形式的结果存储为dfs,然后我们可以简单地选择当一列的值不在此常见时的行:

In [119]:

common = df1.merge(df2,on=['col1','col2'])
print(common)
df1[(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))]
   col1  col2
0     1    10
1     2    11
2     3    12
Out[119]:
   col1  col2
3     4    13
4     5    14

修改

您发现的另一种方法是使用isin生成NaN行,您可以放弃这些行:

In [138]:

df1[~df1.isin(df2)].dropna()
Out[138]:
   col1  col2
3     4    13
4     5    14

但是如果df2没有以相同的方式启动行,那么这将不起作用:

df2 = pd.DataFrame(data = {'col1' : [2, 3,4], 'col2' : [11, 12,13]})

将产生整个df:

In [140]:

df1[~df1.isin(df2)].dropna()
Out[140]:
   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14

答案 1 :(得分:117)

当前选择的解决方案会产生错误的结果。要正确解决此问题,我们可以执行从df1df2的左连接,确保首先只获取df2的唯一行。

首先,我们需要修改原始DataFrame以添加包含数据[3,10]的行。

df1 = pd.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 
                           'col2' : [10, 11, 12, 13, 14, 10]}) 
df2 = pd.DataFrame(data = {'col1' : [1, 2, 3],
                           'col2' : [10, 11, 12]})

df1

   col1  col2
0     1    10
1     2    11
2     3    12
3     4    13
4     5    14
5     3    10

df2

   col1  col2
0     1    10
1     2    11
2     3    12

执行左连接,消除df2中的重复项,以便df1的每一行与df2的正好行连接。使用参数indicator返回一个额外的列,指示该行所在的表。

df_all = df1.merge(df2.drop_duplicates(), on=['col1','col2'], 
                   how='left', indicator=True)
df_all

   col1  col2     _merge
0     1    10       both
1     2    11       both
2     3    12       both
3     4    13  left_only
4     5    14  left_only
5     3    10  left_only

创建一个布尔条件:

df_all['_merge'] == 'left_only'

0    False
1    False
2    False
3     True
4     True
5     True
Name: _merge, dtype: bool

为什么其他解决方案出错?

一些解决方案犯了同样的错误 - 它们只检查每个值是否在每列中是独立的,而不是在同一行中。添加唯一但具有df2两列值的最后一行会暴露出错误:

common = df1.merge(df2,on=['col1','col2'])
(~df1.col1.isin(common.col1))&(~df1.col2.isin(common.col2))
0    False
1    False
2    False
3     True
4     True
5    False
dtype: bool

此解决方案得到了相同的错误结果:

df1.isin(df2.to_dict('l')).all(1)

答案 2 :(得分:59)

假设索引在数据框中是一致的(不考虑实际的col值):

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

答案 3 :(得分:11)

正如已经暗示的那样,isin要求匹配时列和索引相同。如果匹配只应该在行内容上,那么获取用于过滤存在的行的掩码的一种方法是将行转换为(多)索引:

In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5, 3], 'col2' : [10, 11, 12, 13, 14, 10]})
In [78]: df2 = pandas.DataFrame(data = {'col1' : [1, 3, 4], 'col2' : [10, 12, 13]})
In [79]: df1.loc[~df1.set_index(list(df1.columns)).index.isin(df2.set_index(list(df2.columns)).index)]
Out[79]:
   col1  col2
1     2    11
4     5    14
5     3    10

如果要考虑索引,set_index会附加关键字参数,以便将列附加到现有索引。如果列不对齐,则可以使用列规范替换list(df.columns)以对齐数据。

pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())

可以替代地用于创建索引,但我怀疑这更有效。

答案 4 :(得分:10)

假设您有两个数据框,df_1和df_2有多个字段(column_names),并且您希望在某些字段(例如fields_x,fields_y)的基础上找到df_1中不在df_2中的那些条目,请按照以下步骤操作步骤。

Step1。将列key1和key2分别添加到df_1和df_2。

Step2.Merge数据帧,如下所示。 field_x和field_y是我们想要的列。

步骤3.仅选择df_1中的那些行,其中key1不等于key2。

Step4.Drop key1和key2。

此方法可以解决您的问题,即使使用大数据集也可以快速运行。我已经尝试过超过1,000,000行的数据帧。

df_1['key1'] = 1
df_2['key2'] = 1
df_1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'left')
df_1 = df_1[~(df_1.key2 == df_1.key1)]
df_1 = df_1.drop(['key1','key2'], axis=1)

答案 5 :(得分:5)

有点晚了,但可能值得检查&#34;指标&#34; pd.merge的参数。

查看另一个问题的例子: Compare PandaS DataFrames and return rows that are missing from the first one

答案 6 :(得分:4)

您也可以连结df1df2

x = pd.concat([df1, df2])

然后删除所有重复项:

y = x.drop_duplicates(keep=False, inplace=False)

答案 7 :(得分:3)

您可以使用isin(dict)方法执行此操作:

In [74]: df1[~df1.isin(df2.to_dict('l')).all(1)]
Out[74]:
   col1  col2
3     4    13
4     5    14

说明:

In [75]: df2.to_dict('l')
Out[75]: {'col1': [1, 2, 3], 'col2': [10, 11, 12]}

In [76]: df1.isin(df2.to_dict('l'))
Out[76]:
    col1   col2
0   True   True
1   True   True
2   True   True
3  False  False
4  False  False

In [77]: df1.isin(df2.to_dict('l')).all(1)
Out[77]:
0     True
1     True
2     True
3    False
4    False
dtype: bool

答案 8 :(得分:1)

这个怎么样:

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 
                               'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 
                               'col2' : [10, 11, 12]})
records_df2 = set([tuple(row) for row in df2.values])
in_df2_mask = np.array([tuple(row) in records_df2 for row in df1.values])
result = df1[~in_df2_mask]

答案 9 :(得分:1)

以下是解决此问题的另一种方法:

df1[~df1.index.isin(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

或者:

df1.loc[df1.index.difference(df1.merge(df2, how='inner', on=['col1', 'col2']).index)]

答案 10 :(得分:1)

这是最好的方法:

df = df1.drop_duplicates().merge(df2.drop_duplicates(), on=df2.columns.to_list(), 
                   how='left', indicator=True)
df.loc[df._merge=='left_only',df.columns!='_merge']

请注意,使用重复复制来最小化比较。没有他们,它也会工作。最好的方法是比较行内容本身,而不是索引或一列或两列,并且相同的代码也可用于其他过滤器(例如“ both”和“ right_only”),以达到相似的结果。对于这种语法,数据帧可以具有任意数量的列,甚至可以具有不同的索引。在这两个数据框中都应该只出现列。

为什么这是最好的方法?

  1. index.difference仅适用于基于索引的唯一比较
  2. pandas.concat()drop_duplicated()结合使用并不理想,因为它还会摆脱可能仅在您要保留的数据帧中并出于有效原因而重复的行。

答案 11 :(得分:1)

我认为那些包含合并的答案非常缓慢。因此,我建议另一种方法来获取两个数据帧之间不同的行:

df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 
df2 = pandas.DataFrame(data = {'col1' : [1, 2, 3], 'col2' : [10, 11, 12]})

免责声明:如果您对两个数据帧不同的特定列感兴趣,我的解决方案有效。如果您只对那些所有列都相等的行感兴趣,请不要使用这种方法。

比方说,col1 是一种 ID,您只想获取那些不包含在两个数据帧中的行:

ids_in_df2 = df2.col1.unique()
not_found_ids = df[~df['col1'].isin(ids_in_df2 )]

就是这样。您将获得一个仅包含 col1 未出现在两个数据帧中的那些行的数据帧。

答案 12 :(得分:0)

我这样做的方法是添加一个对一个数据帧唯一的新列,并使用它来选择是否保留一个条目

df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)

这使得df1中的每个条目都有一个代码 - 如果df1是唯一的,则为0,如果它在两个dataFrame中,则为1。然后,您可以使用它来限制您想要的内容

answer = nonuni[nonuni['Empt'] == 0]

答案 13 :(得分:0)

使用合并功能提取不相似的行
df = df.merge(same.drop_duplicates(), on=['col1','col2'], 
               how='left', indicator=True)
将不同的行保存为CSV
df[df['_merge'] == 'left_only'].to_csv('output.csv')

答案 14 :(得分:0)

更简单、更简单、更优雅

uncommon_indices = np.setdiff1d(df1.index.values, df2.index.values)
new_df = df1.loc[uncommon_indices,:]