pandas - 按行元素

时间:2015-10-22 13:24:20

标签: python pandas

我有一个数据框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

6 个答案:

答案 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)]

enter image description here

我认为这改进了@ 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

enter image description here

# extract desired columns where marker is NaN
joined[pd.isnull(joined['marker'])][df1.columns]

enter image description here

可能有一种方法可以在不使用临时数组的情况下执行此操作,但我想不到一个。只要您的数据不是很大,上述方法应该是一个快速而充分的答案。

答案 1 :(得分:11)

这非常简洁,效果很好:

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

答案 2 :(得分:4)

使用DataFrame.mergeDataFrame.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