我正在寻找一个id列表并返回一个多次出现的id列表。这就是我设置的工作:
singles = list(ids)
duplicates = []
while len(singles) > 0:
elem = singles.pop()
if elem in singles:
duplicates.append(elem)
但是id列表可能会变得很长,而且我实际上不希望在一个昂贵的len调用上使用while循环,如果我可以避免它。 (我可以走一条不优雅的路线,然后再调用len一次,然后在每次迭代时减少它,但如果可以的话,我宁愿避免使用它。)
答案 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.duplicated和https://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))。