【问题标题】:How do I get a list of the duplicate rows in pandas?如何获取熊猫中重复行的列表?
【发布时间】:2020-12-10 22:39:15
【问题描述】:

我有一个大数据框,在 358 个样本中有超过 10 万个变量(行)。

我想知道所有样本中哪些变量相同(重复)。

示例数据框如下:

         Sample1 Sample2 Sample3 Sample4 Sample5
1000084  0.0     0.0     0.0     0.0     0.0    
1000092  0.0     0.0     0.0     0.0     0.0
1000096  0.0     0.0     1.0     0.0     0.0
1000110  0.0     0.0     1.0     0.0     0.0
1000116  0.0     0.0     0.0     0.0     0.0

我需要的结果可能是这样的:或相同行列表的列表

 {1000084:[1000092, 1000116], 1000096:[1000110]}

我尝试了 pandas 的复制方法,但它只会留下唯一项目或唯一项目加上第一个或最后一个重复项。

我尝试使用此代码,但需要很长时间:

duplicated_index = set()
duplicates = {}

for i, pos in enumerate(df.index, 0):
    #check if the row has marked as duplicate, if so, ignore it
    if i in duplicated_index:
        continue
    for j in range(i+1, df.shape[0]):
        if all(df.iloc[i] == df.iloc[j]):
             duplicated_index.add(j)
             tmp = duplicates.setdefault(pos, [])
             duplicates[pos].append(df.iloc[j].name)

有没有更合适的方法来获取这个列表并识别哪些行与其他行相同?

【问题讨论】:

  • {1000084:[1000092, 1000116], 1000096:[1000110]} 什么?这里的关键是什么?
  • 按所有列分组。
  • 您可能想要选择其他解决方案之一。它们的执行时间并不受列数的影响,而我的执行时间与列数和行数成线性关系。对于具有 no 重复项的 (100000,300) DataFrame,它们的速度更快。
  • @wwii,您的运行良好,速度也非常快,不到一分钟。此外,你有一个更全面、明确和清晰的答案,帮助我更好地理解 groupby 方法是如何工作的。谢谢!

标签: python pandas


【解决方案1】:

按所有列分组;查找具有多个项目的组并将其放入列表中。使用 for 循环。

>>> gb = df.groupby(df.columns.to_list())
>>> d = {}
>>> for a,b in gb:
...     if len(b) > 1:
...         d[b.index[0]] = b.index[1:].to_list()

        
>>> d
{1000084: [1000092, 1000116], 1000096: [1000110]}
>>>

使用与上面相同的 groupby,编写一个函数来返回组的索引并使用 aggregate method 构造一个字典。

def f(thing):
    return thing.index.to_list()

>>> {key:val for key,*val in gb.aggregate(f) if val}
{1000084: [1000092, 1000116], 1000096: [1000110]}

看起来执行时间与列行数(项目数)成线性关系。


这是一个用于测试的大型 DataFrame。不幸的是,它不想产生重复的行——也许这是 groupby 然后迭代的最坏情况?

import itertools,string
import numpy as np
nrows,ncols = 100000,300

a = np.random.randint(1,3,(nrows,ncols))
# or using the new random stuff
#from numpy.random import default_rng
#rng = default_rng()
#a = rng.integers(1,3,(nrows,ncols))

index = np.arange(1000000,1000000+nrows,dtype=np.int64)
cols = [''.join(thing) for thing in itertools.combinations(string.ascii_letters,3)]
df2 = pd.DataFrame(data=a,index=index,columns=cols[:ncols])

【讨论】:

    【解决方案2】:

    reset_index 然后groupby 添加agg

    l = df.reset_index().groupby(list(df))['index'].agg(list).tolist()
    Out[291]: [[1000084, 1000092, 1000116], [1000096, 1000110]]
    

    【讨论】:

    • 然后迭代制作字典。您是否感觉您的解决方案与我的解决方案之间是否存在显着的速度差异?
    • 没关系,对小型 DataFrame 进行了快速测试 - 类似。
    • 虽然你的在较大的 DataFrame 上比我的要好得多,但我的与列数呈线性关系,而你的则几乎不受列数的影响。
    【解决方案3】:

    pandas 有自己的函数duplicated(),它将返回所有重复的行。

    duplicated_rows = df[df.duplicated(subset=['col1', 'col2', 'col3'], keep=False)]
    

    根据documentation

    • subset 可以是您选择的需要检查重复的列的列表。默认情况下,它使用所有列。
    • keep 设置为 False 以保留所有次出现。

    如果您希望将结果作为列表列表,可能对您上面起草的代码稍作修改可能会解决您的问题。

    【讨论】:

      【解决方案4】:

      使用pd.factorizeIndex.groupby 添加另一种方法

      idx = pd.factorize(list(map(tuple,df.to_numpy().tolist())))[0]
      
      d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}
      

      {1000084: [1000092, 1000116], 1000096: [1000110]}
      

      或使用df.to_records(),但它可能比以前的方法慢:

      idx = pd.factorize(df.to_records(index=False))[0]
      d = {g[0]: [*g[1:]] for _,g in df.index.groupby(idx).items() if len(g)>1}
      

      【讨论】:

      • 最终字典包括 rows 没有重复 - 不知道 OP 是否关心。
      • @wwii 谢谢指点,现在应该修好了:)
      猜你喜欢
      • 2019-09-24
      • 2021-10-22
      • 1970-01-01
      • 2019-09-29
      • 1970-01-01
      • 2019-03-27
      • 2013-04-03
      • 2021-01-03
      • 2017-06-28
      相关资源
      最近更新 更多