pandas得到不在其他数据框中的行

我有两个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]}) 

一种方法是将内部合并的结果存储在两个dfs中,那么当一列的值不在这个公共中时,我们可以简单地select行:

 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 

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

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

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

 In [77]: df1 = pandas.DataFrame(data = {'col1' : [1, 2, 3, 4, 5], 'col2' : [10, 11, 12, 13, 14]}) 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 

如果要考虑索引,set_index具有关键字参数append以将列附加到现有索引。 如果列不alignment,list(df.columns)可以replace为列规格以alignment数据。

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

也可以用来创build指数,但我怀疑这是更有效的。

假设您有两个数据框,df_1和df_2具有多个字段(column_names),并且您只想根据某些字段(如fields_x,fields_y)在df_1中find不在df_2中的那些条目,请按照以下步骤操作。

步骤1:将列key1和key2分别添加到df_1和df_2。

步骤2.按如下所示组织数据框。 field_x和field_y是我们想要的列。

步骤3:只select那些来自df_1的行,其中key1不等于key2。

Step4.Drop键1和键2。

这种方法将解决您的问题,即使在大数据集的情况下也能快速运行。 我已经尝试了超过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) 

你可以使用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 

有点晚了,但可能值得检查pd.merge的“指标”参数。

有关示例,请参阅这个其他问题: 比较PandaS DataFrames和返回第一个丢失的行

当前select的解决scheme产生不正确的结 要正确地解决这个问题,我们可以执行从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正好1行连接。 使用参数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 

创build一个布尔条件:

 df_all['_merge'] == 'left_only' 0 False 1 False 2 False 3 True 4 True 5 True Name: _merge, dtype: bool 

为什么其他解决scheme是错的

一些解决scheme也犯了同样的错误 – 他们只检查每列中的每个值是独立的,而不是在同一行中。 添加最后一行,这是唯一的,但从df2两列中的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 

此解决scheme得到相同的错误结果

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

我这样做的方法涉及到添加一个新的列是唯一的一个dataframe,并使用它来select是否保留一个条目

 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;如果在两个数据框中,则代码为1。 然后你用这个来限制你想要的

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

这个怎么样:

 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]