In Python, what is the fastest algorithm for removing duplicates from a list so that all elements are unique *while preserving order*?
本问题已经有最佳答案,请猛点这里访问。
例如:
1 2 3
| >>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3] |
假设列表元素是可哈希的。 澄清:结果应保留清单中的第一份副本。例如,[1,2,3,2,3,1]变为[1,2,3]。
1 2 3 4 5 6 7 8 9 10 11 12
| def unique(items):
found = set([])
keep = []
for item in items:
if item not in found:
found.add(item)
keep.append(item)
return keep
print unique([1, 1, 2, 'a', 'a', 3]) |
使用:
1
| lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5] |
使用Timeit模块:
1
| $ python -m timeit -s 'import uniquetest' 'uniquetest.etchasketch(uniquetest.lst)' |
对于各种其他功能(我以它们的海报命名),我有以下结果(在我的第一代Intel MacBook Pro上):
1 2 3 4 5 6 7 8 9
| Allen: 14.6 μs per loop [1]
Terhorst: 26.6 μs per loop
Tarle: 44.7 μs per loop
ctcherry: 44.8 μs per loop
Etchasketch 1 (short): 64.6 μs per loop
Schinckel: 65.0 μs per loop
Etchasketch 2: 71.6 μs per loop
Little: 89.4 μs per loop
Tyler: 179.0 μs per loop |
[1]请注意,Allen在适当的位置修改了列表-我认为这已经扭曲了时间,因为timeit模块运行代码100000次,其中99999次使用无重复列表。 总结:用集合直接实现胜过混淆一行程序:—)
这里的解决方案fastest太远了(*以下输入:) P / < >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| def del_dups(seq):
seen = {}
pos = 0
for item in seq:
if item not in seen:
seen[item] = True
seq[pos] = item
pos += 1
del seq[pos:]
lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18,
13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1,
5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9,
9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
del_dups(lst)
print(lst)
# -> [8, 9, 7, 15, 2, 20, 13, 24, 6, 11, 12, 4, 10, 18, 23, 3, 5, 22, 19, 14,
# 21, 1, 0, 16, 17] |
词典lookup也更快slightly那么"设置"一个在Python 3。 P / < >
最快的速度取决于你的列表中有多少是重复的。如果几乎是所有重复项,只有很少的唯一项,那么创建新列表可能会更快。如果它主要是唯一的项目,从原始列表(或副本)中删除它们将更快。 下面是一个用于就地修改列表的列表:
1 2 3 4 5 6 7 8
| def unique(items):
seen = set()
for i in xrange(len(items)-1, -1, -1):
it = items[i]
if it in seen:
del items[i]
else:
seen.add(it) |
向后迭代索引可以确保移除项不会影响迭代。
这是我找到的最快的就地方法(假设有大量重复):
1 2 3 4 5
| def unique(l):
s = set(); n = 0
for x in l:
if x not in s: s.add(x); l[n] = x; n += 1
del l[n:] |
这比Allen的实现快10%,它是基于这个实现的(用timeit.repeat计时,由psyco编译的jit)。它保留任何副本的第一个实例。 雷普顿:如果你能确认我的时间,我会很感兴趣的。
这是可能的simplest方式: P / < >
1
| list(OrderedDict.fromkeys(iterable)) |
20世纪的Python 3.5,ordereddict现在implemented在C,所以这会是现在的shortest,cleanest,和fastest。 P / < >
基于发电机的强制性变化:
1 2 3 4 5 6
| def unique(seq):
seen = set()
for x in seq:
if x not in seen:
seen.add(x)
yield x |
一班轮:
1
| new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, []) |
这就是一个fastest,comparing所有的东西从这lengthy discussion和其他的回答给了睾丸,refering给该基准。它的另一个25 %的速度比的fastest功能从discussion,f8。谢谢到戴维科比为理念。 P / < >
1 2 3 4
| def uniquify(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)] |
一定的时间比较: P / < >
1 2 3 4 5 6
| $ python uniqifiers_benchmark.py
* f8_original 3.76
* uniquify 3.0
* terhorst 5.44
* terhorst_localref 4.08
* del_dups 4.76 |
在一个地方的一个liner为这样: P / < >
1 2 3
| >>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3] |
摘自http://www.peterbe.com/plog/uniqifiers-benchmark
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def f5(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result |
实际上,您可以在Python中做一些非常酷的事情来解决这个问题。您可以创建一个列表理解,在构建时引用它自己。如下:
1 2 3
| # remove duplicates...
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]'].__self__] |
编辑:我删除了"self",它在mac os x,python 2.5.1上工作。 _[1]是python对新列表的"秘密"引用。当然,上面有点混乱,但是你可以根据需要调整它。例如,您实际上可以编写一个返回对理解的引用的函数;它看起来更像:
1
| return [x for x in my_list if x not in this_list()] |
remove duplicates和维护秩序。 P / < >
这是一个快速的2 liner,leverages建在functionality of comprehensions列表和dicts。 P / < >
1 2 3 4 5 6 7
| x = [1, 1, 2, 'a', 'a', 3]
tmpUniq = {} # temp variable used below
results = [tmpUniq.setdefault(i,i) for i in x if i not in tmpUniq]
print results
[1, 2, 'a', 3] |
"dict.setdefaults(功能)的价值returns号好号添加到高温dict直接在comprehension列表。用"建在功能和hashes之dict将工作要maximize效率为过程。 P / < >
是否必须首先将重复项放在列表中?在向上查找元素时没有开销,但是在添加元素时会有一些开销(尽管开销应该是O(1))。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| >>> x = []
>>> y = set()
>>> def add_to_x(val):
... if val not in y:
... x.append(val)
... y.add(val)
... print x
... print y
...
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> |
在python中has_key是o(1)。哈希的插入和检索也是O(1)。两次循环N个项目,所以o(n)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| def unique(list):
s = {}
output = []
for x in list:
count = 1
if(s.has_key(x)):
count = s[x] + 1
s[x] = count
for x in list:
count = s[x]
if(count > 0):
s[x] = 0
output.append(x)
return output |
o(n)如果dict是hash,o(nlogn)如果dict是tree,并且简单、固定。感谢马修的建议。抱歉,我不知道底层类型。
1 2 3 4 5 6 7 8 9 10 11
| def unique(x):
output = []
y = {}
for item in x:
y[item] =""
for item in x:
if item in y:
output.append(item)
return output |
这里有一些伟大的,有效的解决方案。但是,对于不关心绝对最有效的O(n)解决方案的任何人,我将使用简单的单行O(n^2*log(n))解决方案:
1 2
| def unique(xs):
return sorted(set(xs), key=lambda x: xs.index(x)) |
或更有效的双内衬O(n*log(n))解决方案:
1 2 3
| def unique(xs):
positions = dict((e,pos) for pos,e in reversed(list(enumerate(xs))))
return sorted(set(xs), key=lambda x: positions[x]) |
这里是两个recipes从itertools文件: P / < >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return imap(next, imap(itemgetter(1), groupby(iterable, key))) |
我对python没有经验,但是一种算法是对列表进行排序,然后删除重复项(通过与列表中以前的项进行比较),最后通过与旧列表进行比较在新列表中找到位置。 更长的答案:http://aspn.activestate.com/aspn/cookbook/python/recipe/52560
1 2 3 4 5 6
| >>> def unique(list):
... y = []
... for x in list:
... if x not in y:
... y.append(x)
... return y |
如果从terhost的答案中的call to set()中去掉空列表,您会得到一点速度提升。 变化:发现=集([])到:SED= SET() 不过,你根本不需要这套。
1 2 3 4 5 6 7 8
| def unique(items):
keep = []
for item in items:
if item not in keep:
keep.append(item)
return keep |
使用Timeit,我得到了以下结果: 带套件([])--4.97210427363带set()--4.65712377445不带套件--3.44865284975
1 2 3 4 5 6 7 8 9 10
| x = [] # Your list of items that includes Duplicates
# Assuming that your list contains items of only immutable data types
dict_x = {}
dict_x = {item : item for i, item in enumerate(x) if item not in dict_x.keys()}
# Average t.c. = O(n)* O(1) ; furthermore the dict comphrehension and generator like behaviour of enumerate adds a certain efficiency and pythonic feel to it.
x = dict_x.keys() # if you want your output in list format |
1 2 3 4
| >>> x=[1,1,2,'a','a',3]
>>> y = [ _x for _x in x if not _x in locals()['_[1]'] ]
>>> y
[1, 2, 'a', 3] |
"locals()["[1]"]是正在创建的列表的"秘密名称"。
我不知道这是不是很快,但至少很简单。 简单地说,先把它转换成一个集合,然后再转换成一个列表
1 2
| def unique(container):
return list(set(container)) |
在1,2,3,4,5,7,7,8,8,9,9,3,45 = [ ] P / < >
独特的DEF(L): P / < >
1 2 3 4 5
| ids={}
for item in l:
if not ids.has_key(item):
ids[item]=item
return ids.keys() |
打印的 P / < >
打印独特(的) P / < > ----------------------------
inserting元素将以θ(n) 如果retrieving元也exiting或不会把时间常数 所有的测试项目也将把θ(n) 所以我们可以看到,这种解决方案将把θ(n) 熊在所有的词典,在Python implemented by哈希表 P / < >
我没有做任何测试,但是一个可能的算法是创建第二个列表,并遍历第一个列表。如果项目不在第二个列表中,请将其添加到第二个列表中。
1 2 3 4 5
| x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
if each not in y:
y.append(each) |
一次传球。
1 2 3 4 5 6 7 8 9 10 11 12 13
| a = [1,1,'a','b','c','c']
new_list = []
prev = None
while 1:
try:
i = a.pop(0)
if i != prev:
new_list.append(i)
prev = i
except IndexError:
break |
|