Removing duplicates in lists
我需要编写一个程序来检查一个列表是否有任何重复项,如果有,它将删除它们并返回一个新的列表,其中包含被复制/删除的项。这是我有的,但老实说,我不知道该怎么做。
1 2 3 4 5 6 | def remove_duplicates(): t = ['a', 'b', 'c', 'd'] t2 = ['a', 'c', 'd'] for t in t2: t.append(t.remove()) return t |
获得唯一项目集合的常见方法是使用
以下示例应涵盖您尝试执行的操作:
1 2 3 4 5 6 7 8 | >>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8] >>> t [1, 2, 3, 1, 2, 5, 6, 7, 8] >>> list(set(t)) [1, 2, 3, 5, 6, 7, 8] >>> s = [1, 2, 3] >>> list(set(t) - set(s)) [8, 5, 6, 7] |
正如您从示例结果中看到的,原始订单不被维护。如上所述,集合本身是无序集合,因此顺序丢失。将集合转换回列表时,将创建任意顺序。
如果订单对你很重要,那么你必须使用不同的机制。一个非常常见的解决方案是依靠
1 2 3 | >>> from collections import OrderedDict >>> list(OrderedDict.fromkeys(t)) [1, 2, 3, 5, 6, 7, 8] |
请注意,这有一个开销,首先创建一个字典,然后从中创建一个列表。所以如果你实际上不需要保持秩序,你最好使用一套。请查看此问题,了解删除重复项时保留顺序的更多详细信息和其他方法。
最后要注意,
在python 2.7中,从iterable中删除重复项的新方法是:
1 2 3 | >>> from collections import OrderedDict >>> list(OrderedDict.fromkeys('abracadabra')) ['a', 'b', 'r', 'c', 'd'] |
在Python3.5中,ordereddict有一个C实现。我的计时显示,现在这是Python3.5各种方法中最快和最短的。
在python 3.6中,常规dict既成了有序的又紧凑的。(此功能适用于cpython和pypy,但可能不存在于其他实现中)。这为我们提供了一种新的快速除尘方法,同时保持订单:
1 2 | >>> list(dict.fromkeys('abracadabra')) ['a', 'b', 'r', 'c', 'd'] |
在Python3.7中,常规dict保证在所有实现中都按顺序排列。因此,最短和最快的解决方案是:
1 2 | >>> list(dict.fromkeys('abracadabra')) ['a', 'b', 'r', 'c', 'd'] |
这是一条单行线:
一个
更新:订单保留方法有两行:
1 2 | from collections import OrderedDict OrderedDict((x, True) for x in source_list).keys() |
这里我们使用这样一个事实:
1 2 3 4 5 6 7 8 9 | >>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8] >>> t [1, 2, 3, 1, 2, 5, 6, 7, 8] >>> s = [] >>> for i in t: if i not in s: s.append(i) >>> s [1, 2, 3, 5, 6, 7, 8] |
如果您不关心订单,只需执行以下操作:
1 2 | def remove_duplicates(l): return list(set(l)) |
保证
制定新的清单,保留
例如,
这将检查每个新元素在添加之前是否在列表中出现过。而且它不需要进口。
今天,一位同事将接受的答案作为代码的一部分发送给我进行代码审查。虽然我当然很钦佩这个问题的答案的优雅,但我对这个表现并不满意。我尝试过这个解决方案(我使用set来减少查找时间)
1 2 3 4 5 6 7 8 | def ordered_set(in_list): out_list = [] added = set() for val in in_list: if not val in added: out_list.append(val) added.add(val) return out_list |
为了比较效率,我使用了100个整数的随机样本-62个是唯一的
1 2 3 4 5 | from random import randint x = [randint(0,100) for _ in xrange(100)] In [131]: len(set(x)) Out[131]: 62 |
这是测量结果
1 2 3 4 5 | In [129]: %timeit list(OrderedDict.fromkeys(x)) 10000 loops, best of 3: 86.4 us per loop In [130]: %timeit ordered_set(x) 100000 loops, best of 3: 15.1 us per loop |
那么,如果从解决方案中移除集合,会发生什么?
1 2 3 4 5 6 | def ordered_set(inlist): out_list = [] for val in inlist: if not val in out_list: out_list.append(val) return out_list |
结果并没有订购的ICT差,但仍然是原来解决方案的3倍以上。
1 2 | In [136]: %timeit ordered_set(x) 10000 loops, best of 3: 52.6 us per loop |
另一种方法:
1 2 3 | >>> seq = [1,2,3,'a', 'a', 1,2] >> dict.fromkeys(seq).keys() ['a', 1, 2, 3] |
还有一些解决方案使用熊猫和麻木。它们都返回numpy数组,因此如果需要列表,必须使用函数
1 2 | t=['a','a','b','b','b','c','c','c'] t2= ['c','c','b','b','b','a','a','a'] |
熊猫解决方案
使用熊猫功能
1 2 3 4 5 | import pandas as pd pd.unique(t).tolist() >>>['a','b','c'] pd.unique(t2).tolist() >>>['c','b','a'] |
努米溶液
使用numpy函数
1 2 3 4 5 | import numpy as np np.unique(t).tolist() >>>['a','b','c'] np.unique(t2).tolist() >>>['a','b','c'] |
注意numpy.unique()也会对值进行排序。所以列表
1 2 3 | _, idx = np.unique(t2, return_index=True) t2[np.sort(idx)].tolist() >>>['c','b','a'] |
与其他方法相比,该解决方案并不那么优雅,但是与pandas.unique()相比,numpy.unique()还允许您检查嵌套数组在一个选定轴上是否唯一。
简单易行:
1 2 3 | myList = [1, 2, 3, 1, 2, 5, 6, 7, 8] cleanlist = [] [cleanlist.append(x) for x in myList if x not in cleanlist] |
输出:
1 2 | >>> cleanlist [1, 2, 3, 5, 6, 7, 8] |
我的单子里有个口述,所以我不能用上面的方法。我得到了错误:
1 | TypeError: unhashable type: |
所以如果你关心订单和/或一些物品是不可清洗的。然后你会发现这很有用:
1 2 3 4 | def make_unique(original_list): unique_list = [] [unique_list.append(obj) for obj in original_list if obj not in unique_list] return unique_list |
有些人可能认为清单理解的副作用不是一个好的解决方案。还有一种选择:
1 2 3 4 | def make_unique(original_list): unique_list = [] map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list) return unique_list |
尝试使用集合:
1 2 3 4 5 6 | import sets t = sets.Set(['a', 'b', 'c', 'd']) t1 = sets.Set(['a', 'b', 'c']) print t | t1 print t - t1 |
到目前为止,我在这里看到的所有顺序保持方法要么使用简单的比较(最好是O(n^2)时间复杂度)要么使用重的
更新添加了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | # from functools import reduce <-- add this import on Python 3 def uniq(iterable, key=lambda x: x): """ Remove duplicates from an iterable. Preserves order. :type iterable: Iterable[Ord => A] :param iterable: an iterable of objects of any orderable type :type key: Callable[A] -> (Ord => B) :param key: optional argument; by default an item (A) is discarded if another item (B), such that A == B, has already been encountered and taken. If you provide a key, this condition changes to key(A) == key(B); the callable must return orderable objects. """ # Enumerate the list to restore order lately; reduce the sorted list; restore order def append_unique(acc, item): return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1])) return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] |
你也可以这样做:
1 2 3 4 | >>> t = [1, 2, 3, 3, 2, 4, 5, 6] >>> s = [x for i, x in enumerate(t) if i == t.index(x)] >>> s [1, 2, 3, 4, 5, 6] |
上述工作的原因是
list.index(x[, start[, end]])
Return zero-based index in the list of
the first item whose value is x. Raises a ValueError if there is no
such item.
从列表中删除重复项的最佳方法是使用在Python中可用的set()函数,再次将该集合转换为列表。
1 2 3 | In [2]: some_list = ['a','a','v','v','v','c','c','d'] In [3]: list(set(some_list)) Out[3]: ['a', 'c', 'd', 'v'] |
不使用集合
1 2 3 4 5 6 7 | data=[1, 2, 3, 1, 2, 5, 6, 7, 8] uni_data=[] for dat in data: if dat not in uni_data: uni_data.append(dat) print(uni_data) |
通过订购保留减少变量:
假设我们有以下列表:
1 | l = [5, 6, 6, 1, 1, 2, 2, 3, 4] |
减少变量(非官方):
1 2 | >>> reduce(lambda r, v: v in r and r or r + [v], l, []) [5, 6, 1, 2, 3, 4] |
速度快5倍,但更复杂
1 2 | >>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0] [5, 6, 1, 2, 3, 4] |
说明:
1 2 3 4 5 6 7 8 9 10 11 | default = (list(), set()) # user list to keep order # use set to make lookup faster def reducer(result, item): if item not in result[1]: result[0].append(item) result[1].add(item) return result reduce(reducer, l, default)[0] |
您可以使用以下功能:
1 2 3 4 5 6 | def rem_dupes(dup_list): yooneeks = [] for elem in dup_list: if elem not in yooneeks: yooneeks.append(elem) return yooneeks |
例子:
1 | my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list'] |
用途:
1 | rem_dupes(my_list) |
["this"、"is"、"a"、"list"、"with"、"dupites"、"in"、"the"]
这个人关心订单,没有太多麻烦(orderddict&others)。可能不是最简单的方式,也不是最短的方式,但技巧是:
1 2 3 4 5 6 7 | def remove_duplicates(list): ''' Removes duplicate items from a list ''' singles_list = [] for element in list: if element not in singles_list: singles_list.append(element) return singles_list |
另一个更好的方法是,
1 2 3 4 5 6 7 | import pandas as pd myList = [1, 2, 3, 1, 2, 5, 6, 7, 8] cleanList = pd.Series(myList).drop_duplicates().tolist() print(cleanList) #> [1, 2, 3, 5, 6, 7, 8] |
秩序仍然保持着。
下面的代码很容易删除列表中的重复项
1 2 3 4 5 6 7 8 | def remove_duplicates(x): a = [] for i in x: if i not in a: a.append(i) return a print remove_duplicates([1,2,2,3,3,4]) |
返回[1,2,3,4]
还有许多其他的答案建议了不同的方法来实现这一点,但它们都是批处理操作,其中一些会丢弃原始订单。这也许可以,具体取决于您需要什么,但是如果您希望按每个值的第一个实例的顺序迭代这些值,并且希望一次删除所有重复的值,则可以使用此生成器:
1 2 3 4 5 6 | def uniqify(iterable): seen = set() for item in iterable: if item not in seen: seen.add(item) yield item |
这将返回一个生成器/迭代器,因此您可以在任何可以使用迭代器的地方使用它。
1 2 3 4 | for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]): print(unique_item, end=' ') print() |
输出:
1 | 1 2 3 4 5 6 7 8 |
如果你想要一个
1 2 3 | unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8])) print(unique_list) |
输出:
1 | [1, 2, 3, 4, 5, 6, 7, 8] |
如果您想保留订单,而不使用任何外部模块,这里是一个简单的方法:
1 2 3 | >>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9] >>> list(dict.fromkeys(t)) [1, 9, 2, 3, 4, 5, 6, 7, 8] |
注:此方法保留外观顺序,因此,如上所示,9将在1之后出现,因为它是第一次出现。然而,这和你做的结果是一样的。
1 2 | from collections import OrderedDict ulist=list(OrderedDict.fromkeys(l)) |
但它要短得多,而且跑得更快。
这是因为每次
使用集合:
1 2 3 | a = [0,1,2,3,4,3,3,4] a = list(set(a)) print a |
使用独特:
1 2 3 4 | import numpy as np a = [0,1,2,3,4,3,3,4] a = np.unique(a).tolist() print a |
以下是与回复中列出的其他人交流的最快的Python疗法。
使用短路评估的实现细节可以使用列表理解,这是足够快的。
自己计时
1 2 3 4 5 | def deduplicate(sequence): visited = set() adder = visited.add # get rid of qualification overhead out = [adder(item) or item for item in sequence if item not in visited] return out |
在python 3中非常简单的方法:
1 2 3 4 5 6 | >>> n = [1, 2, 3, 4, 1, 1] >>> n [1, 2, 3, 4, 1, 1] >>> m = sorted(list(set(n))) >>> m [1, 2, 3, 4] |
如果不使用inbuilt set、dict.keys、uniqify、counter删除重复项(就地编辑而不是返回新列表),请选中此项。
1 2 3 4 5 6 7 | >>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8] >>> for i in t: ... if i in t[t.index(i)+1:]: ... t.remove(i) ... >>> t [3, 1, 2, 5, 6, 7, 8] |
您可以使用
1 | mylist = list(set(mylist)) |
但请注意,结果将是无序的。如果这是个问题:
1 | mylist.sort() |
下面是一个例子,返回列表时不重复保存顺序。不需要任何外部导入。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | def GetListWithoutRepetitions(loInput): # return list, consisting of elements of list/tuple loInput, without repetitions. # Example: GetListWithoutRepetitions([None,None,1,1,2,2,3,3,3]) # Returns: [None, 1, 2, 3] if loInput==[]: return [] loOutput = [] if loInput[0] is None: oGroupElement=1 else: # loInput[0]<>None oGroupElement=None for oElement in loInput: if oElement<>oGroupElement: loOutput.append(oElement) oGroupElement = oElement return loOutput |
我认为转换为set是删除重复项的最简单方法:
1 2 3 | list1 = [1,2,1] list1 = list(set(list1)) print list1 |
只需使用集合就可以做到这一点。
步骤1:获取列表的不同元素步骤2获取列表的公共元素第3步:组合它们
1 2 3 4 5 6 | In [1]: a = ["apples","bananas","cucumbers"] In [2]: b = ["pears","apples","watermelons"] In [3]: set(a).symmetric_difference(b).union(set(a).intersection(b)) Out[3]: {'apples', 'bananas', 'cucumbers', 'pears', 'watermelons'} |
要删除重复项,请将其设置为集合,然后再次将其设置为列表并打印/使用。一套保证有独特的元素。例如:
1 2 3 4 5 | a = [1,2,3,4,5,9,11,15] b = [4,5,6,7,8] c=a+b print c print list(set(c)) #one line for getting unique elements of c |
输出如下(在python 2.7中检查)
1 2 | [1, 2, 3, 4, 5, 9, 11, 15, 4, 5, 6, 7, 8] #simple list addition with duplicates [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 15] #duplicates removed!! |
1 2 3 | def remove_duplicates(A): [A.pop(count) for count,elem in enumerate(A) if A.count(elem)!=1] return A |
用于删除重复项的列表压缩
如果你不在乎秩序,想要一些不同于上面提到的Python式的方法(也就是说,它可以用于面试),那么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | def remove_dup(arr): size = len(arr) j = 0 # To store index of next unique element for i in range(0, size-1): # If current element is not equal # to next element then store that # current element if(arr[i] != arr[i+1]): arr[j] = arr[i] j+=1 arr[j] = arr[size-1] # Store the last element as whether it is unique or repeated, it hasn't stored previously return arr[0:j+1] if __name__ == '__main__': arr = [10, 10, 1, 1, 1, 3, 3, 4, 5, 6, 7, 8, 8, 9] print(remove_dup(sorted(arr))) |
时间复杂度:O(N)
辅助空间:O(N)
参考:http://www.geeksforgeks.org/remove-duplicates-sorted-array/
这里有很多答案使用
我建议的函数是混合函数:我们对可散列的项使用
最后,我们还可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | def uniq(iterable, key=lambda x: x): seens = set() seenl = [] for item in iterable: k = key(item) try: seen = k in seens except TypeError: seen = k in seenl if not seen: yield item try: seens.add(k) except TypeError: seenl.append(k) |
例如,我们现在可以这样使用:
1 2 3 4 5 6 7 8 9 10 | >>> list(uniq(["apple","pear","banana","lemon"], len)) ['apple', 'pear', 'banana'] >>> list(uniq(["apple","pear","lemon","banana"], len)) ['apple', 'pear', 'banana'] >>> list(uniq(["apple","pear", {},"lemon", [],"banana"], len)) ['apple', 'pear', {}, 'banana'] >>> list(uniq(["apple","pear", {},"lemon", [],"banana"])) ['apple', 'pear', {}, 'lemon', [], 'banana'] >>> list(uniq(["apple","pear", {},"lemon", {},"banana"])) ['apple', 'pear', {}, 'lemon', 'banana'] |
因此,它是一个uniqeness过滤器,可以处理任何iterable并过滤掉uniques,不管这些uniques是否可以散列。
它做了一个假设:如果一个对象是可散列的,而另一个对象是不可散列的,那么这两个对象就永远不会相等。严格来说,这是可能发生的,尽管这是非常罕见的。
另一个解决方案可能是:从列表中创建一个以项为键、索引为值的字典,然后打印字典键。
1 2 3 4 5 | >>> lst = [1, 3, 4, 2, 1, 21, 1, 32, 21, 1, 6, 5, 7, 8, 2] >>> >>> dict_enum = {item:index for index, item in enumerate(lst)} >>> print dict_enum.keys() [32, 1, 2, 3, 4, 5, 6, 7, 8, 21] |
不幸的是。这里的大多数答案要么不维持秩序要么太长。这是一个简单的、有序的答案。
1 2 3 4 5 | s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5] x=[] [x.append(i) for i in s if i not in x] print(x) |
这将为您提供删除重复项但保留顺序的X。
为了完整性,由于这是一个非常流行的问题,toolz库提供了一个
1 2 3 4 | >>> tuple(unique((1, 2, 3))) (1, 2, 3) >>> tuple(unique((1, 2, 1, 3))) (1, 2, 3) |
1 2 3 4 5 6 7 8 9 10 11 | def remove_duplicates(input_list): if input_list == []: return [] #sort list from smallest to largest input_list=sorted(input_list) #initialize ouput list with first element of the sorted input list output_list = [input_list[0]] for item in input_list: if item >output_list[-1]: output_list.append(item) return output_list |
这只是一个可读的函数,很容易理解,我使用了dict数据结构,使用了一些内置函数和更复杂的o(n)
1 2 3 4 5 6 7 | def undup(dup_list): b={} for i in dup_list: b.update({i:1}) return b.keys() a=["a",'b','a'] print undup(a) |
免责声明:您可能会得到一个缩进错误(如果复制和粘贴),在粘贴之前使用上面的代码并适当缩进
python内置了许多函数,可以使用set()删除列表中的重复项。根据您的示例,下面有两个列表t和t2
1 2 3 4 | t = ['a', 'b', 'c', 'd'] t2 = ['a', 'c', 'd'] result = list(set(t) - set(t2)) result |
答:[ B′]
有时需要在不创建新列表的情况下,就地删除重复项。例如,列表很大,或者将其保留为卷影副本
1 2 3 4 5 | from collections import Counter cntDict = Counter(t) for item,cnt in cntDict.items(): for _ in range(cnt-1): t.remove(item) |
它需要安装第三方模块,但包
1 2 3 4 | >>> from iteration_utilities import unique_everseen >>> list(unique_everseen(['a', 'b', 'c', 'd'] + ['a', 'c', 'd'])) ['a', 'b', 'c', 'd'] |
如果要避免列表添加操作的开销,可以使用
1 2 3 | >>> from itertools import chain >>> list(unique_everseen(chain(['a', 'b', 'c', 'd'], ['a', 'c', 'd']))) ['a', 'b', 'c', 'd'] |
如果列表中有不可显示的项目(例如列表),那么
1 2 3 | >>> from iteration_utilities import unique_everseen >>> list(unique_everseen([['a'], ['b'], 'c', 'd'] + ['a', 'c', 'd'])) [['a'], ['b'], 'c', 'd', 'a'] |
但是,如果这些项是可散列的,则速度会慢得多。
披露:我是
1 | list_with_unique_items = list(set(list_with_duplicates)) |