我有一个数据框df1
,如下所示:
c k l
0 A 1 a
1 A 2 b
2 B 2 a
3 C 2 a
4 C 2 d
和另一个名为df2
的人:
c l
0 A b
1 C a
我想过滤df1
仅保留df2
中不存在的值。要过滤的值应为(A,b)
和(C,a)
元组。到目前为止,我尝试应用isin
方法:
d = df[~(df['l'].isin(dfc['l']) & df['c'].isin(dfc['c']))]
除了在我看来太复杂之外,它还会返回:
c k l
2 B 2 a
4 C 2 d
但我期待:
c k l
0 A 1 a
2 B 2 a
4 C 2 d
答案 0 :(得分:29)
您可以使用isin
在从所需列构建的多索引上有效地执行此操作:
df1 = pd.DataFrame({'c': ['A', 'A', 'B', 'C', 'C'],
'k': [1, 2, 2, 2, 2],
'l': ['a', 'b', 'a', 'a', 'd']})
df2 = pd.DataFrame({'c': ['A', 'C'],
'l': ['b', 'a']})
keys = list(df2.columns.values)
i1 = df1.set_index(keys).index
i2 = df2.set_index(keys).index
df1[~i1.isin(i2)]
我认为这改进了@ IanS的类似解决方案,因为它不假设任何列类型(即它将使用数字和字符串)。
(以上答案是编辑。以下是我最初的答案)
有趣!这是我之前没有遇到的......我可能会通过合并两个数组来解决它,然后删除定义df2
的行。这是一个使用临时数组的例子:
df1 = pd.DataFrame({'c': ['A', 'A', 'B', 'C', 'C'],
'k': [1, 2, 2, 2, 2],
'l': ['a', 'b', 'a', 'a', 'd']})
df2 = pd.DataFrame({'c': ['A', 'C'],
'l': ['b', 'a']})
# create a column marking df2 values
df2['marker'] = 1
# join the two, keeping all of df1's indices
joined = pd.merge(df1, df2, on=['c', 'l'], how='left')
joined
# extract desired columns where marker is NaN
joined[pd.isnull(joined['marker'])][df1.columns]
可能有一种方法可以在不使用临时数组的情况下执行此操作,但我想不到一个。只要您的数据不是很大,上述方法应该是一个快速而充分的答案。
答案 1 :(得分:11)
这非常简洁,效果很好:
df1 = df1[~df1.index.isin(df2.index)]
答案 2 :(得分:4)
DataFrame.merge
和DataFrame.query
:一种更优雅的方法是对参数left join
执行indicator=True
,然后用left_only
过滤query
的所有行:
d = (
df1.merge(df2,
on=['c', 'l'],
how='left',
indicator=True)
.query('_merge == "left_only"')
.drop(columns='_merge')
)
print(d)
c k l
0 A 1 a
2 B 2 a
4 C 2 d
indicator=True
返回带有额外列_merge
的数据帧,该列标记每一行left_only, both, right_only
:
df1.merge(df2, on=['c', 'l'], how='left', indicator=True)
c k l _merge
0 A 1 a left_only
1 A 2 b both
2 B 2 a left_only
3 C 2 a both
4 C 2 d left_only
答案 3 :(得分:1)
怎么样:
df1['key'] = df1['c'] + df1['l']
d = df1[~df1['key'].isin(df2['c'] + df2['l'])].drop(['key'], axis=1)
答案 4 :(得分:1)
当您要基于来自另一个数据框的多个列甚至基于自定义列表过滤数据框时,我认为这是一种非常简单的方法。
df1 = pd.DataFrame({'c': ['A', 'A', 'B', 'C', 'C'],
'k': [1, 2, 2, 2, 2],
'l': ['a', 'b', 'a', 'a', 'd']})
df2 = pd.DataFrame({'c': ['A', 'C'],
'l': ['b', 'a']})
#values of df2 columns 'c' and 'l' that will be used to filter df1
idxs = list(zip(df2.c.values, df2.l.values)) #[('A', 'b'), ('C', 'a')]
#so df1 is filtered based on the values present in columns c and l of df2
df1 = df1[pd.Series(list(zip(df1.c, df1.l)), index=df1.index).isin(idxs)]
答案 5 :(得分:0)
避免创建额外列或进行合并的另一个选项是在df2上执行groupby以获取不同的(c,l)对,然后使用它来过滤df1。
gb = df2.groupby(("c", "l")).groups
df1[[p not in gb for p in zip(df1['c'], df1['l'])]]]
对于这个小例子,它实际上似乎比基于熊猫的方法运行得快一点(在我的机器上为666μs与1.76 ms),但我怀疑它在较大的示例上可能会慢一点,因为它已经落入纯Python