Python 熊猫获取不在其他数据帧中的行

Python 熊猫获取不在其他数据帧中的行,python,pandas,dataframe,Python,Pandas,Dataframe,我有两个熊猫数据帧,它们有一些共同的行 假设dataframe2是dataframe1的子集 如何获取dataframe1中不在dataframe2中的行? 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]}) df1 d

我有两个熊猫数据帧,它们有一些共同的行

假设dataframe2是dataframe1的子集

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

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

df2

预期结果:

   col1  col2
3     4    13
4     5    14

一种方法是将内部合并表单的结果存储在两个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

正如已经暗示的,isin要求匹配的列和索引相同。如果匹配只针对行内容,则获取筛选当前行的掩码的一种方法是将行转换为(多)索引:

若应该考虑索引,则set_index将关键字参数append附加到现有索引的附加列。如果列没有对齐,可以用列规范替换列表(df.columns)以对齐数据

pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())
pandas.MultiIndex.from_元组(df.to_记录(index=False).tolist())

也可用于创建索引,但我怀疑这是否更有效。

假设您有两个数据帧,df_1和df_2具有多个字段(列名称),并且您希望根据某些字段(例如字段x、字段y)查找df_1中不在df_2中的唯一条目,请遵循以下步骤

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

步骤2.合并数据帧,如下所示。字段x和字段y是我们需要的列

步骤3.仅选择df_1中key1不等于key2的行

步骤4.放下键1和键2

这种方法将解决您的问题,即使在大数据集上也能快速工作。我已经为超过1000000行的数据帧尝试过了

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)
您可以使用以下方法执行此操作:

说明:

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的“indicator”参数

有关示例,请参见其他问题:

我的方法是添加一个数据帧特有的新列,并使用它来选择是否保留条目

df2[col3] = 1
df1 = pd.merge(df_1, df_2, on=['field_x', 'field_y'], how = 'outer')
df1['Empt'].fillna(0, inplace=True)
这使得df1中的每个条目都有一个代码-0(如果它是df1独有的),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]

当前选择的解决方案产生不正确的结果。为了正确解决这个问题,我们可以执行从
df1
df2
的左连接,确保首先只获取
df2
的唯一行

首先,我们需要修改原始数据框以添加包含数据的行[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)

您还可以连接
df1
df2

x = pd.concat([df1, df2])
然后删除所有重复项:

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

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

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


使用merge函数提取不同的行
df = df.merge(same.drop_duplicates(), on=['col1','col2'], 
               how='left', indicator=True)
在CSV中保存不同的行
df[df['_merge'] == 'left_only'].to_csv('output.csv')
这是最好的方法: 请注意,drop duplicated用于最小化比较。没有他们也行。最好的方法是比较行内容本身,而不是索引或一列/两列,相同的代码可用于其他筛选器,如“两者”和“仅限右”以获得类似的结果。对于这种语法,数据帧可以有任意数量的列,甚至可以有不同的索引。两个数据帧中只应出现列

为什么这是最好的方法?
  • index.difference仅适用于基于唯一索引的比较
  • pandas.concat()
    drop\u duplicated()
    结合使用并不理想,因为它还将删除可能仅位于您想要保留的数据帧中的行,并且这些行是出于正当理由而复制的

  • 简单、简单、优雅

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

    我认为这些答案非常缓慢。因此,我建议采用另一种方法获取两个数据帧之间不同的行:

    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]})
    
    ids_in_df2 = df2.col1.unique()
    not_found_ids = df[~df['col1'].isin(ids_in_df2 )]
    
    免责声明:如果您对两个数据帧不同的一个特定列感兴趣,我的解决方案是有效的。如果您只对所有列相等的行感兴趣,请不要使用这种方法

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

    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]})
    
    ids_in_df2 = df2.col1.unique()
    not_found_ids = df[~df['col1'].isin(ids_in_df2 )]
    

    就这样。您将得到一个数据帧,其中只包含col1在两个数据帧中都不出现的行。

    df1[~df1.isin(df2)]。dropna(how='all')
    似乎可以做到这一点。无论如何,谢谢你-你的回答帮助我找到了一个解决方案。你能解释一下
    ~
    在你的代码
    df1[~df1.isin(df2)]
    中做了什么吗?因为它只是一个符号,所以不能用谷歌搜索任何东西。谢谢。@BowenLiu它否定了表达式,基本上它说的是选择所有不在中的内容,而不是在中。@ThinkNiceSthings,它可以更简单:
    df1[~df1.index.isin(df2.index)]
    我认为这在技术上不是他想要的——他想知道哪些行是哪个df所独有的。但是,我认为这个解决方案返回的行的df要么是唯一的,要么是唯一的
    df[df['_merge'] == 'left_only'].to_csv('output.csv')
    
    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']
    
    uncommon_indices = np.setdiff1d(df1.index.values, df2.index.values)
    new_df = df1.loc[uncommon_indices,:]
    
    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]})
    
    ids_in_df2 = df2.col1.unique()
    not_found_ids = df[~df['col1'].isin(ids_in_df2 )]