从列表中删除唯一值并仅保留重复项

时间:2015-04-24 10:26:22

标签: python

我正在寻找一个id列表并返回一个多次出现的id列表。这就是我设置的工作:

singles = list(ids)
duplicates = []
while len(singles) > 0:
    elem = singles.pop()
    if elem in singles:
        duplicates.append(elem)

但是id列表可能会变得很长,而且我实际上不希望在一个昂贵的len调用上使用while循环,如果我可以避免它。 (我可以走一条不优雅的路线,然后再调用len一次,然后在每次迭代时减少它,但如果可以的话,我宁愿避免使用它。)

5 个答案:

答案 0 :(得分:13)

这样做的明智之举是使用一种简单有效的数据结构,例如Counter

>>> ids = [random.randrange(100) for _ in range(200)]
>>> from collections import Counter
>>> counts = Counter(ids)
>>> dupids = [id for id in ids if counts[id] > 1]

构建Counter需要O(N)时间,而不是O(N log N)时间进行排序,或O(N ^ 2)每次从头开始计算每个元素。

作为旁注:

  

但是ids列表可能会变得很长,而且我实际上并不想要一个基于昂贵的len调用的while循环,如果我可以避免它。

len并不昂贵。这是一个恒定的时间,并且(至少在内置类型列表list上)它与函数一样快,可能在Python中没有做任何事情。

你的代码中很昂贵的部分是在循环中调用elem in singles - 这意味着对于每个元素,你必须将它与潜在的每个元素进行比较,这意味着二次时间。

答案 1 :(得分:5)

你可以这样做,

>>> ids = [1,2,3,2,3,5]
>>> set(i for i in ids if ids.count(i) > 1)
{2, 3}

答案 2 :(得分:1)

我认为这会更快地运作:

occasions = {}
for id in ids:
    try:
        occasions[id] += 1
    except KeyError:
        occasions[id] = 0
result = [id for id in ids if occasions[id] > 1]

答案 3 :(得分:0)

或使用itertools.groupby

>>> l=[1,1,2,2,2,3]
>>> from itertools import groupby
>>> print([key for key,group in groupby(l) if len(list(group)) > 1])
[1, 2]
>>> 

只需检查(循环中的)组是否大于一个,是否保留,否则不要。

或使用pandas

>>> import pandas as pd
>>> s=pd.Series(l)
>>> s[s.duplicated()].unique().tolist()
[1, 2]
>>> 

这非常快,因为pandas超级快。

文档:

  

https://pandas.pydata.org/pandas-docs/stable/10min.html

     

将光标置于黄色部分上以查看链接。

     
    

https://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.duplicated.html#pandas.Series.duplicatedhttps://pandas.pydata.org/pandas-docs/stable/generated/pandas.Series.unique.html#pandas.Series.unique

  

答案 4 :(得分:-1)

如果您不关心检索这些ID的顺序,那么有效的方法将包括排序步骤(即O(N log(N))),然后保留随后的ID(这是O(N))。所以这种方法总体上是O(N log(N))。