如果行是其他行的子集,则 pandas 会删除该行

Chr*_*ris 4 python pandas

我已经搜索了很多,但似乎没有什么可以解决这个问题。

假设什么时候df是这样的:

import pandas as pd
import numpy as np

df = pd.DataFrame([['a','b','c'], ['a',np.nan,'b'], [np.nan, 'b', 'a'], ['a', 'd', 'b']])
df
     0    1  2
0    a    b  c
1    a  NaN  b
2  NaN    b  a
3    a    d  b
Run Code Online (Sandbox Code Playgroud)

期望的输出是:

     0    1  2
0    a    b  c
3    a    d  b
Run Code Online (Sandbox Code Playgroud)

第 1、2 行是第 0 行的子集,因此我想删除它们。当检查一行是否是任何其他行的子集时,NaN不考虑。因此,第 1 行变为{'a','b'},从而成为一个子集。

到目前为止我尝试过的是使sets:

df.ffill(1).bfill(1).apply(set, 1)
Run Code Online (Sandbox Code Playgroud)

产生:

0    {c, a, b}
1       {a, b}
2       {a, b}
3    {d, a, b}
Run Code Online (Sandbox Code Playgroud)

但我被困在这里了。pd.DataFrame.drop_duplicates似乎对我没有帮助。

任何帮助是极大的赞赏 :)

Bra*_*mon 5

这很难。理想情况下,您想要:

  • 坚持使用 Pandas 向量化操作,而不是循环遍历行。(一开始我想到了pd.Index,它的行为类似于集合,并且有一些类似集合操作的方法。)
  • 尽可能使用类似哈希表的数据结构进行集合成员资格测试。

由于特定的条件,这两件事在这里做起来都很棘手,因此时间复杂度可能会变得很复杂。(我并不排除可能有比这个更巧妙的答案。)但一般来说,一旦您从精确重复测试转向子集测试,事情就会变得更加困难。

综上所述,您可以:

  1. 将 DataFrame 转换为嵌套列表 - 通过尽可能迭代 Pandas 数据结构来减少不必要的开销
  2. set.issuperset在贪婪调用中使用来any()查找重复项的索引,利用frozenset可散列的事实(感谢此处的其他答案)。

复杂度仍然是 N^2 或接近它的值,但对于中等大小的数据来说这可能就足够了。

>>> df = pd.DataFrame([['a','b','c'], ['a',np.nan,'b'], [np.nan, 'b', 'a'], ['a', 'd', 'b']])
>>> 
>>> seen = set()
>>> add = seen.add
>>> dupes = []
>>> 
>>> for pos, row in enumerate(df.values.tolist()):
...     vals = frozenset(i for i in row if isinstance(i, str))
...     if any(i.issuperset(vals) for i in seen):
...         dupes.append(pos)
...     add(vals)
... 
>>> dupes
[1, 2]
Run Code Online (Sandbox Code Playgroud)

这会让你通过 下降索引DataFrame.drop()