我有两个有一些共同行的熊猫数据框。
假设 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]})
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
当前选择的解决方案产生不正确的结果。为了正确解决这个问题,我们可以执行从 df1
到 df2
的左连接,确保首先获得 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
创建一个布尔条件:
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)
一种方法是将内部合并的结果存储在两个 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
df1[~df1.isin(df2)].dropna(how = 'all')
似乎可以解决问题。无论如何,谢谢-您的回答帮助我找到了解决方案。
~
在您的代码 df1[~df1.isin(df2)]
中的作用吗?不能用谷歌搜索任何东西,因为它只是一个符号。谢谢。
df1[~df1.index.isin(df2.index)]
假设索引在数据帧中是一致的(不考虑实际的 col 值):
df1[~df1.index.isin(df2.index)]
正如已经暗示的那样, 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 具有关键字参数 append 以将列附加到现有索引。如果列不对齐,可以将 list(df.columns) 替换为列规范以对齐数据。
pandas.MultiIndex.from_tuples(df<N>.to_records(index = False).tolist())
也可以用来创建索引,尽管我怀疑这更有效。
假设您有两个数据帧 df_1 和 df_2 具有多个字段(column_names),并且您想根据某些字段(例如 fields_x、fields_y)找到 df_1 中唯一不在 df_2 中的条目,请按照以下步骤操作。
Step1.分别在df_1和df_2中添加列key1和key2。
Step2.合并数据框,如下所示。 field_x 和 field_y 是我们想要的列。
Step3. 仅选择 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)
这是最好的方法:
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 duplicated 用于最小化比较。没有它们也可以。最好的方法是比较行内容本身,而不是索引或一/两列,相同的代码也可用于其他过滤器,如“both”和“right_only”,以获得类似的结果。对于这种语法,数据帧可以有任意数量的列,甚至可以有不同的索引。只有列应该出现在两个数据框中。
为什么这是最好的方法?
index.difference 仅适用于基于唯一索引的比较 pandas.concat() 加上 drop_duplicated() 并不理想,因为它还会删除可能仅在您想要保留的数据帧中并且出于正当理由而重复的行。
有点晚了,但可能值得检查 pd.merge 的“indicator”参数。
有关示例,请参见其他问题:Compare PandaS DataFrames and return rows that are missing from the first one
我认为那些包含合并的答案非常慢。因此,我建议另一种方法来获取两个数据帧之间不同的行:
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 在两个数据框中都没有出现的那些行。
您可以使用 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
这是解决此问题的另一种方法:
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)]
我有一个更简单的方法,只需两个简单的步骤:正如 OP 提到的假设 dataframe2 是 dataframe1 的子集,这两个数据帧中的列是相同的,
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
您还可以连接 df1
、df2
:
x = pd.concat([df1, df2])
然后删除所有重复项:
y = x.drop_duplicates(keep=False, inplace=False)
df = df.merge(same.drop_duplicates(), on=['col1','col2'],
how='left', indicator=True)
df[df['_merge'] == 'left_only'].to_csv('output.csv')
我这样做的方法包括添加一个对一个数据框唯一的新列,并使用它来选择是否保留一个条目
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]
更简单、更简单、更优雅
uncommon_indices = np.setdiff1d(df1.index.values, df2.index.values)
new_df = df1.loc[uncommon_indices,:]
不定期副业成功案例分享
df_all[df_all['_merge'] == 'left_only']
获得结果的 dfhow='outer'
,以便_merge
列具有左/右/两者,当未来的读者尝试将解决方案应用于他们的问题时,这更容易理解。