在python中,从列表中删除重复项的最快算法是什么,以便所有元素都是唯一的*,同时保持顺序*?

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