使用列表推导式的解决方案
我认为更符合Python风格的答案是使用列表推导式:
result = [x for x in data if data.count(x) > 1]
示例列表解决方案时间比较
我已将 C.Nivis 和 Patrick Artner 的答案放入一个函数中,以便更轻松地在其上运行 timeit。
为了考虑调用函数所需的时间,我还将列表推导式包装成一个函数调用。
设置
def remove_singletons(data):
"""Return list with no singleton using for loops."""
res = []
for i in data:
if data.count(i) > 1:
res.append(i)
return res
def remove_singletons_lc(data):
"""Return list with no singleton using for list comprehension."""
return [x for x in data if data.count(x)>1]
from collections import Counter
def remove_singletons_counter(data):
c = Counter(data)
return [x for x in data if c[x] > 1]
import numpy as np
def remove_singletons_numpy(data):
a = np.array(data)
_, ids, counts = np.unique(a, return_counts=True, return_inverse=True)
return a[counts[ids] != 1]
l = [1,2,3,2,1]
循环解决方案
%timeit remove_singletons(l)
>>> 1.42 µs ± 46.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
使用列表推导式的解决方案
%timeit remove_singletons_lc(l)
>>> 1.2 µs ± 17.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit remove_singletons_counter(l)
>>> 6.55 µs ± 143 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
%timeit remove_singletons_numpy(l)
>>> 53.8 µs ± 3.07 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
结论
看起来列表推导式比循环略微但一致地更快,并且比使用小型列表的Counter
要快得多。对于小型列表,Numpy则较慢。
大型列表的解决方案时间比较
假设我们有一个包含n个随机元素的大型列表,范围为[0, n]
import random
n = 10000
l = [random.randint(0, n) for i in range(n)]
循环解决方案
%timeit remove_singletons(l)
>>> 1.5 s ± 64.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
使用列表推导式的解决方案
%timeit remove_singletons_lc(l)
>>> 1.51 s ± 33.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit remove_singletons_counter(l)
>>> 2.65 ms ± 228 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit remove_singletons_numpy(l)
>>> 1.75 ms ± 38.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
大型列表的结论
对于大型列表,毫无争议的胜者是 numpy.unique
,其次是略微落后的 Counter
。
最终结论
对于小型列表,列表推导 似乎很适用,但对于较大的列表,numpy.unique
方法效果最佳。
list.pop
的部分是正确的。但是提出的替代方案在迭代列表时仍然会改变它,这可能会产生不可预测的结果。 - wimfor i, item in enumerate(data[:]):
也可以工作。 这将是最短的代码(无需声明副本),但我不确定是否推荐这种做法。 - michaPau