Python 从列表中删除唯一值并仅保留重复值
我希望运行一个ID列表,并返回一个出现多次的ID列表。这就是我设置的工作:Python 从列表中删除唯一值并仅保留重复值,python,Python,我希望运行一个ID列表,并返回一个出现多次的ID列表。这就是我设置的工作: singles = list(ids) duplicates = [] while len(singles) > 0: elem = singles.pop() if elem in singles: duplicates.append(elem) 但是ids列表可能会变得很长,实际上,如果可以避免的话,我不希望在昂贵的len调用上进行while循环。(我可以走不雅观的路线,给le
singles = list(ids)
duplicates = []
while len(singles) > 0:
elem = singles.pop()
if elem in singles:
duplicates.append(elem)
但是ids列表可能会变得很长,实际上,如果可以避免的话,我不希望在昂贵的len调用上进行while循环。(我可以走不雅观的路线,给len打一次电话,然后每次迭代都减少它,但如果可以的话,我宁愿避免这种情况)。你可以这样做
>>> ids = [1,2,3,2,3,5]
>>> set(i for i in ids if ids.count(i) > 1)
{2, 3}
如果您不关心检索这些ID的顺序,那么一种有效的方法将包括一个排序步骤(即O(N log(N)),然后保留后面跟着的ID(即O(N))。因此,这种方法总体上是O(N log(N))。我认为这会更快:
occasions = {}
for id in ids:
try:
occasions[id] += 1
except KeyError:
occasions[id] = 0
result = [id for id in ids if occasions[id] > 1]
要做到这一点,明智的方法是使用一种简单高效的数据结构,如: 构建
计数器
需要O(N)时间,而不是O(N log N)时间进行排序,或者O(N^2)时间用于每次从头开始计算每个元素
作为旁注: 但是ids列表可能会变得很长,实际上,如果可以避免的话,我不希望在昂贵的len调用上进行while循环
len
不贵。这是一个固定的时间,而且(至少在内置类型列表list
)它的速度几乎与Python中的函数所能达到的速度一样快,根本不做任何事情
代码中代价高昂的部分是在循环中调用单元素中的元素,这意味着对于每个元素,您必须将其与其他元素进行比较,这意味着二次时间。或使用
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]
>>>
只需检查组(在循环中)是否大于一,如果是,则保留它,否则不要
或使用熊猫:
>>> import pandas as pd
>>> s=pd.Series(l)
>>> s[s.duplicated()].unique().tolist()
[1, 2]
>>>
它非常快,因为熊猫的速度非常快
文件:
将光标放在黄色部分以查看链接
,及
但是排序本身是O(N logn),所以O(N)步并不重要。另外,这破坏了顺序,我们不知道这是否可以接受,所以你不能仅仅假设它是可以接受的。是的,如果结果必须是相同的顺序,它是不起作用的。我认为使用
itertools.ifilter
会更有效。(我用计数器和ifilter
写了一个答案,但你写得更快;)@Kasra:为什么?使用filter
/ifilter
而不是生成器表达式/列表理解意味着您必须将测试包装在函数中,这会增加额外的成本。(如果你只是指如果我们不需要列表,就不要构建列表,更简单的方法是将listcomp更改为genexpr,但由于他明确表示需要返回列表,我认为你无法避免构建列表。)@Kasra:第一个列表只是创建测试数据。您需要从某个地方获取输入数据。@Kasra:无论如何,不要猜测,从对10000个具有100个唯一值的元素进行的快速测试来看,[listcomp]
需要1.88ms,list(genexpr)
需要2.25ms,filter
需要2.71ms,list(ifilter)
需要2.91ms。因此,使用ifilter
可以使速度降低55%。无论如何,这种微观优化很少值得担心;一旦你从二次行为变为线性,通常你可以停止优化…为什么不[i代表i,j计数.items(),如果j>1]
?如果你想以一个列表结束,只需要一个小的加法:duplicates=list(set(id中的i代表i,如果id.count(i)>1))列表(set(id中的i代表i,如果id.count(i)>1))
这甚至比他现有的代码还要慢(尽管只有2-ish的常数因子)。它确实删除了len
调用,但那部分并不重要;用ID.code
替换singles中的元素
意味着您现在每次都在搜索每个元素,而不是只搜索重复项中的第一个匹配项。此外,这会破坏顺序,并将所有重复项压缩为1,而他的原始代码都没有这样做,所以我不认为这是可以接受的。是的,这基本上只是手动实现的计数器的一个简单版本,所以速度也一样快。(或者至少非常接近;计数器
使用\uuuu missing\uuuu
而不是,除了KeyError:
,可能会稍微快一点,但这只会产生一个小的恒定差异。)我认为场合=计数器(ID)
更容易阅读,更难出错,但这也很好,特别是对于一个还不习惯用口述来思考为什么它有用的新手来说,这可能更清楚。好吧,我完全同意你的看法。谢谢你的回答,我不知道有这样一个功能。
>>> import pandas as pd
>>> s=pd.Series(l)
>>> s[s.duplicated()].unique().tolist()
[1, 2]
>>>