我有两个熊猫数据帧,它们有一些相同的行。
假设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
col1 col2
0 1 10
1 2 11
2 3 12
3 4 13
4 5 14
df2
col1 col2
0 1 10
1 2 11
2 3 12
预期结果:
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
EDIT
你已经发现的另一个方法是使用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要求列和索引必须相同才能进行匹配。如果match只适用于行内容,一种获取掩码的方法是将行转换为(Multi)Index:
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有关键字参数append来将列追加到现有索引。如果列没有对齐,list(df.columns)可以替换为列规范以对齐数据。
pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())
也可以用来创建指数,但我怀疑这是否更有效。
假设你有两个数据框架,df_1和df_2有多个字段(column_names),你想在df_1中找到那些不在df_2中的条目。Fields_x, fields_y),执行以下步骤。
步骤1。将列key1和key2分别添加到df_1和df_2中。
步骤2。合并数据帧,如下所示。Field_x和field_y是我们想要的列。
步骤3。只选择df_1中key1不等于key2的行。
第四。删除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)
我这样做的方法包括添加一个新的列,该列对一个数据框架是唯一的,并使用它来选择是否保留一个条目
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]
当前选择的解决方案产生不正确的结果。为了正确地解决这个问题,我们可以执行从df1到df2的左连接,确保首先只获得df2的唯一行。
首先,我们需要修改原始的DataFrame,添加有data的行[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的一行连接。使用参数指示符返回一个额外的列,指示该行来自哪个表。
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)
这是最好的方法:
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']
注意,drop duplicate用于最小化比较。没有他们也可以。最好的方法是比较行内容本身,而不是索引或一/两列,同样的代码也可以用于其他过滤器,如'both'和'right_only',以达到类似的结果。对于这种语法,数据帧可以有任意数量的列,甚至可以有不同的索引。只有列应该出现在两个数据框架中。
为什么这是最好的方法?
索引。差异只适用于基于唯一索引的比较
Pandas.concat()与drop_duplication()结合使用并不理想,因为它还会删除可能只存在于你想保留的数据帧中的行,并出于合理的原因进行复制。
我认为那些包含合并的答案是极其缓慢的。因此,我建议另一种方法来获得两个数据框架之间不同的行:
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在两个数据框架中都不明显的行。
我有一个简单的2步更简单的方法:
如OP所述,假设dataframe2是dataframe1的子集,两个dataframe中的列是相同的,
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]})
### Step 1: just append the 2nd df at the end of the 1st df
df_both = df1.append(df2)
### Step 2: drop rows which contain duplicates, Drop all duplicates.
df_dif = df_both.drop_duplicates(keep=False)
## mission accompliched!
df_dif
Out[20]:
col1 col2
3 4 13
4 5 14
5 3 10