删除列表中的重复项

Removing duplicates in lists

提问人:Neemaximo 提问时间:11/1/2011 最后编辑:Karl KnechtelNeemaximo 更新时间:11/1/2023 访问量:2128043

问:

如何检查列表是否有任何重复项并返回没有重复项的新列表?

Python 算法 列表 重复 交集

评论

1赞 Darkonaut 2/1/2020
相关: 如何使用多处理将重复项放入一个非常大的列表中?
1赞 9769953 9/10/2022
有趣的是,这里没有一个顶级答案提供实际问题的答案:创建一个新列表,其中只有原始列表中没有重复的项目。我把它读作 -> ,因为 2 和 4 是重复的。[1, 2, 3, 4, 5, 2, 4][1, 3, 5]
0赞 Mateen Ulhaq 9/12/2022
@9769953鉴于您所说的话,使用 Rev 11 但只保留由最高答案回答的第一个子问题(即)是否有意义?被接受的答案暗示了完成第二个子问题的可能方法(即)。就目前而言,问题和最高答案矛盾地并不完全同步。[1, 2, 3, 1] → [1, 2, 3][1, 2, 3, 1] → [2, 3]
0赞 9769953 9/12/2022
@MateenUlhaq 我宁愿保留原来的问题。此外,修订版 11 更改了问题以更适合答案,但不一定适合原始问题。我想这取决于你希望 SO 的论坛/邮件列表风格有多大,或者离 tips'n'tricks 网站有多近(有非常纯粹的问题和答案)。我认为两者都无法实现。
0赞 Karl Knechtel 1/25/2023
在回过头来阅读第 1 版后,我无法理解这个问题如何被解读为关于是否应该转换为或转换为,或者顺序是否重要,或者其他任何事情。事实上,尽管标题是“Python 删除列表中的重复项”,但 OP 似乎根本不想从同一个列表中删除重复项。相反,看起来 OP 想要获取两个列表,例如 和 ,并从第一个中删除第二个中存在的那些,以获得 .[1, 2, 3, 4, 5, 2, 4][1, 3, 5][1, 2, 3, 4, 5][1, 2, 3, 4][1, 3, 4][2]

答:

2245赞 poke 11/1/2011 #1

获取唯一项目集合的常用方法是使用一组。集合是不同对象的无序集合。要从任何可迭代对象创建集合,只需将其传递给内置的 set() 函数即可。如果你以后再次需要一个真正的列表,你可以同样地将集合传递给 list() 函数。

以下示例应涵盖您尝试执行的任何操作:

>>> t = [1, 2, 3, 1, 2, 3, 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]

从示例结果中可以看出,没有保持原始顺序。如上所述,集合本身是无序集合,因此顺序丢失。将集合转换回列表时,将创建任意顺序。

维持秩序

如果秩序对你很重要,那么你将不得不使用不同的机制。一个非常常见的解决方案是依靠 OrderedDict 在插入过程中保持键的顺序:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

从 Python 3.7 开始,内置字典也保证保持插入顺序,因此如果您使用的是 Python 3.7 或更高版本(或 CPython 3.6),您也可以直接使用它:

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

请注意,这可能会产生一些开销,即先创建字典,然后从中创建列表。如果您实际上不需要保留订单,则通常最好使用集合,尤其是因为它为您提供了更多的操作。请查看此问题,了解更多详细信息以及删除重复项时保留订单的替代方法。


最后请注意,和 / 解决方案都要求您的项目是可散列的。这通常意味着它们必须是不可变的。如果你必须处理不可散列的项目(例如列表对象),那么你将不得不使用一种缓慢的方法,在这种方法中,你基本上必须将每个项目与嵌套循环中的所有其他项目进行比较。setOrderedDictdict

评论

0赞 sailfish009 10/26/2019
将此添加到示例中,t = [3, 2, 1, 1, 2, 5, 6, 7, 8],清楚地显示了差异!
1赞 millerdev 12/9/2019
"...首先创建字典的开销...如果你实际上不需要保留顺序,你最好使用一个集合。我的时序显示,该集合确实稍快一些:1M 循环中每个循环 1.12 μs(集合)与每个循环 1.53 μs(字典),1M 迭代的绝对时间差约为 4 秒。因此,如果你在一个紧密的内循环中做这件事,你可能会在乎,否则可能不会。
0赞 poke 12/9/2019
@millerdev我本来想说“开销不仅意味着时间”之类的话,但后来我检查了一下,似乎键控字典的内存实际上比具有相同元素的集合要小。至少在当前版本的 Python 中是这样。这真的很令人惊讶——但是的,这是一个很好的观点!谢谢!
4赞 Fredrik Erlandsson 12/11/2019
这解决了不可哈希类型的问题(其中 t 是字典列表):[dict(d) for d in set([frozenset(i.items()) for i in t])]
1赞 poke 8/25/2020
@BigDreamz在线性时间内创建一个字典,并将在线性时间中从中创建一个列表。dict.fromkeys()list()
107赞 Brendan Long 11/1/2011 #2

如果您不关心订单,只需执行以下操作:

def remove_duplicates(l):
    return list(set(l))

保证 A 没有重复项。set

217赞 9000 11/1/2011 #3

这是一句话:可以解决问题。list(set(source_list))

A 是不可能有重复项的东西。set

更新:顺序保留方法分为两行:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

在这里,我们使用记住键的插入顺序的事实,并且在更新特定键的值时不会更改它。我们插入值,但我们可以插入任何东西,值只是不使用。( 的工作方式也与忽略值的 A 非常相似。OrderedDictTruesetdict

评论

0赞 9000 8/22/2019
@AdrianKeister:这是真的。有些对象具有合理的相等语义,但不可散列,例如列表。OTOH 如果我们不能有像 hastable 这样的捷径,我们最终会得到一个二次算法,只需将每个元素与所有当前已知的唯一元素进行比较。对于短输入,这完全可以,尤其是对于大量重复的输入。
1赞 Adrian Keister 8/22/2019
没错,没错。我认为,如果您考虑到这个非常常见的用例,您的答案会更高质量。
485赞 Raymond Hettinger 11/1/2011 #4

在 Python 2.7 中,从可迭代对象中删除重复项同时保持其原始顺序的新方法是:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

在 Python 3.5 中,OrderedDict 有一个 C 实现。我的时间表明,这现在是 Python 3.5 各种方法中最快和最短的。

在 Python 3.6 中,常规字典变得既有序又紧凑。(此功能适用于 CPython 和 PyPy,但可能不存在于其他实现中)。这为我们提供了一种新的、最快的重复数据删除方法,同时保持秩序:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

在 Python 3.7 中,常规字典保证在所有实现中都排序。因此,最短和最快的解决方案是:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

评论

11赞 Herberth Amaral 10/23/2012
我认为这是保持物品井然有序的唯一方法。
22赞 Martijn Pieters 8/15/2013
@HerberthAmaral:这与事实相去甚远,请参阅如何在保持顺序的同时从 Python 列表中删除重复项?
5赞 Herberth Amaral 8/16/2013
@MartijnPieters 纠正:我认为这是保持物品井然有序的唯一简单方法。
16赞 Davide 2/16/2017
为此,原始列表的内容也必须是可散列的
4赞 Asclepius 8/2/2019
如果原始列表不可散列,则 more-itertools 包具有 unique_everseen,它适用于可散列和不可散列的项目。
8赞 Charlie Martin 11/1/2011 #5

尝试使用集:

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1
120赞 Neeraj 5/14/2013 #6
>>> 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]

评论

50赞 dotancohen 9/3/2013
请注意,此方法在 O(n^2) 时间内工作,因此在大型列表上非常慢。
24赞 James Sapam 1/1/2014 #7

另一种做法:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

评论

1赞 Dustin Wyatt 12/22/2017
请注意,在现代 Python 版本(我认为是 2.7+,但我不确定)中,返回一个字典视图对象,而不是列表。keys()
16赞 cchristelis 6/6/2014 #8

我的列表中有一个字典,所以我不能使用上述方法。我收到错误:

TypeError: unhashable type:

因此,如果您关心订单和/或某些项目是不可散列的。然后,您可能会发现这很有用:

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

有些人可能认为有副作用的列表理解不是一个好的解决方案。这里有一个替代方法:

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

评论

6赞 abarnert 11/8/2014
map有副作用的 ListComp 比有副作用的 listcomp 更具误导性。此外,只是一种更笨拙、更慢的通过方式。lambda x: unique_list.append(x)unique_list.append
0赞 ZLNK 5/25/2017
在一行中附加元素的非常有用的方法,谢谢!
2赞 Eli Korvigo 3/14/2019
@ZLNK请不要使用它。除了在概念上丑陋之外,它的效率也非常低,因为你实际上创建了一个潜在的大列表,然后把它扔掉,只是为了执行基本的迭代。
39赞 G M 7/3/2014 #9

也有使用 Pandas 和 Numpy 的解决方案。它们都返回 numpy 数组,因此如果您想要列表,则必须使用函数 .tolist()。

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

Pandas 解决方案

使用 Pandas 函数 unique():

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Numpy解决方案

使用 numpy 函数 unique()。

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

请注意,numpy.unique() 也会对值进行排序。因此,列表是按排序返回的。如果您想保留顺序,请使用以下答案t2

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

与其他解决方案相比,该解决方案并不那么优雅,但是,与 pandas.unique() 相比,numpy.unique() 还允许您检查嵌套数组是否沿一个选定的轴是唯一的。

评论

0赞 user227666 7/3/2014
这会将列表转换为 numpy 数组,这很混乱,不适用于字符串。
1赞 G M 7/4/2014
@user227666感谢您的评论,但事实并非如此,它甚至可以与字符串一起使用,如果您想获取列表,可以添加 .tolist......
2赞 Debosmit Ray 10/9/2016
我认为这有点像试图用大锤杀死一只蜜蜂。当然有效!但是,仅出于此目的导入库可能有点矫枉过正,不是吗?
0赞 G M 10/10/2016
@DebosmitRay,如果您在数据科学领域工作,通常使用 numpy,并且很多时候您需要使用 numpy 数组,这可能会很有用。
1赞 Egos 2/27/2020
2020年最佳答案 @DebosmitRay 我希望你改变主意,每次都使用Numpy / Pandas
51赞 Richard Fredlund 7/5/2014 #10

要创建一个新列表,请保留 Duplicates 中第一个元素的顺序:L

newlist = [ii for n,ii in enumerate(L) if ii not in L[:n]]

例如:如果 ,那么将是L = [1, 2, 2, 3, 4, 2, 4, 3, 5]newlist[1, 2, 3, 4, 5]

这将检查每个新元素在添加之前未出现在列表中。 此外,它不需要导入。

评论

5赞 blubberdiblub 4/13/2017
其时间复杂度为 O(n ^ 2)。答案 和 可能具有较低的摊销时间复杂度。setOrderedDict
0赞 Gerasimos Ragavanis 4/26/2018
我在我的代码中使用了这个解决方案并且效果很好,但我认为这很耗时
0赞 ilias iliadis 1/14/2019
@blubberdiblub您能解释一下 set 和 OrderedDict 中存在哪些更高效的代码机制来减少它们的时间消耗?(不包括加载它们的开销)
2赞 blubberdiblub 1/14/2019
@iliasiliadis setdict 的通常实现使用哈希或(某种形式的平衡)树。您必须考虑构建集合字典并在其中搜索(多次),但它们的摊销复杂度通常仍低于 O(n ^ 2)。简单来说,“摊销”意味着平均而言(它们可能具有比平均情况更复杂的最坏情况)。仅当您有大量项目时,这才有意义。
0赞 Keta 6/1/2022
很好的答案,如果元素不可散列,它就会起作用。但是,如果元素是 Numpy 数组,您可能会感到惊讶,因为运算符没有像人们预期的那样工作(至少正如我所期望的那样)。in
7赞 cgf 9/2/2014 #11

这个关心订单没有太多麻烦(OrderdDict 等)。可能不是最 Python 的方式,也不是最短的方式,但可以解决问题:

def remove_duplicates(item_list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in item_list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

评论

0赞 Eli Korvigo 1/8/2018
1. 你永远不应该隐藏内置名称(至少,和 );2. 你的方法缩放得非常糟糕:它的元素数是二次的。listlist
1赞 cgf 3/20/2018
1. 正确,但这是一个例子;2. 正确,这正是我提供它的原因。此处发布的所有解决方案都有优点和缺点。有些牺牲了简单性或秩序,有些牺牲了可扩展性。
31赞 volcano 9/17/2014 #12

一位同事今天将接受的答案作为他代码的一部分发送给我进行代码审查。 虽然我当然钦佩所讨论的答案的优雅,但我对性能并不满意。 我已经尝试了这个解决方案(我使用set来减少查找时间)

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 个是唯一的

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

以下是测量结果

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

那么,如果从解决方案中删除 set 会发生什么?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

结果没有 OrderedDict 那么糟糕,但仍然比原始解决方案的 3 倍多

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

评论

0赞 Joop 9/17/2014
很好使用设置快速查找来加快循环比较。如果顺序无关紧要,list(set(x)) 仍然比这快 6 倍
0赞 volcano 9/17/2014
@Joop,这是我向同事提出的第一个问题——顺序确实很重要;否则,这将是一个微不足道的问题
0赞 DrD 2/17/2020
有序集的优化版本,适合任何感兴趣的人:def unique(iterable): ;seen = set(); seen_add = seen.add; return [item for item in iterable if not item in seen and not seen_add(item)]
23赞 Nima Soroush 4/15/2015 #13

简单易行:

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

输出:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]

评论

5赞 jermenkoo 3/24/2016
尽管如此,二次复杂度 - 是 O(n) 运算,并且 u 最多将有数字 => 最坏情况 ~O(n^2)incleanlistn
7赞 Jean-François Fabre 12/8/2018
列表推导式不应用于副作用。
6赞 Sergey Bershadsky 4/27/2015 #14

使用订购保留减少变体:

假设我们有列表:

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

减少变体(效率低下):

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

速度提高 5 倍,但更复杂

>>> 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]

解释:

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]
4赞 vinay hegde 8/14/2015 #15

下面的代码对于删除列表中的重复项很简单

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]

评论

2赞 dylnmc 9/24/2016
如果你不关心订单,那么这需要更长的时间。 (超过 100 万次通过)将比这个解决方案快大约 10 秒 - 而这种方法大约需要 12 秒,只需要大约 2 秒!list(set(..))list(set(..))
0赞 Eli Korvigo 1/8/2018
@dylnmc这也是一个明显更古老的答案的重复
6赞 Cyphase 8/26/2015 #16

还有许多其他答案建议不同的方法来做到这一点,但它们都是批处理操作,其中一些会丢弃原始订单。根据您的需要,这可能没问题,但是如果您想按照每个值的第一个实例的顺序迭代值,并且想要动态删除重复项而不是一次删除所有重复项,则可以使用此生成器:

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

这将返回一个生成器/迭代器,因此您可以在任何可以使用迭代器的地方使用它。

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

输出:

1 2 3 4 5 6 7 8

如果你确实想要一个 ,你可以这样做:list

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

输出:

[1, 2, 3, 4, 5, 6, 7, 8]

评论

0赞 dylnmc 9/24/2016
seen = set(iterable); for item in seen: yield item几乎可以肯定更快。(我没有尝试过这个具体案例,但这是我的猜测。
3赞 Cyphase 10/26/2016
@dylnmc,这是一个批处理操作,它也会丢失排序。我的回答是专门针对即时的,并按照第一次出现的顺序。:)
13赞 Eli Korvigo 1/14/2016 #17

到目前为止,我在这里看到的所有保序方法要么使用朴素的比较(充其量是 O(n^2) 时间复杂度),要么使用仅限于可哈希输入的重量级 /+ 组合。下面是一个与哈希无关的 O(nlogn) 解决方案:OrderedDictssetlist

更新添加了参数、文档和 Python 3 兼容性。key

# 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]]))] 

评论

0赞 loxaxs 5/19/2016
然而,这种解决方案需要可订购的元素。我将用它来统一我的列表列表:列表和哈希处理它们很痛苦。| | | |- 一般来说,哈希过程需要的时间与整个数据的大小成正比,而这个解决方案需要的时间 O(nlog(n)),仅取决于列表的长度。tuple()
0赞 9000 6/6/2017
我认为基于集合的方法与排序 + 唯一性检测同样便宜 (O(n log n)) 或更便宜。(不过,这种方法并行化会好得多。它也不能完全保留初始顺序,但它给出了一个可预测的顺序。
0赞 Eli Korvigo 6/7/2017
@9000这是真的。我从未提到过基于哈希表的方法的时间复杂度,这显然是 O(n)。在这里,您可以找到许多包含哈希表的答案。但是,它们不是通用的,因为它们要求对象是可散列的。此外,它们的内存密集型要大得多。
0赞 Brayoni 5/1/2020
需要时间来阅读和理解这个答案。在不使用索引时枚举有意义吗?已经在进行分类集合了,你为什么再次申请?reduce()srt_enumsorted
0赞 Eli Korvigo 5/1/2020
@Brayoni第一种排序是为了对相等的值进行分组,那么第二种排序是为了恢复初始顺序。需要枚举来跟踪原始相对顺序。
4赞 thodnev 4/2/2017 #18

这是回复中列出的最快的pythonic解决方案。

使用短路评估的实现细节允许使用列表推导,这足够快。 总是作为结果返回,其计算结果为 ,因此 的右侧始终是此类表达式的结果。visited.add(item)NoneFalseor

自己定时

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
10赞 Atonal 6/6/2017 #19

您也可以这样做:

>>> 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]

上述工作的原因是该方法仅返回元素的第一个索引。重复元素具有更高的索引。参考这里index

list.index(x[, start[, end]])
返回 值为 x 的第一个项目。如果没有,则引发 ValueError 这样的项目。

评论

0赞 Eli Korvigo 4/14/2018
这是非常低效的。 是线性时间运算,使解二次。list.index
0赞 Atonal 10/13/2018
你是对的。但我相信很明显,解决方案旨在成为保留秩序的单行。其他一切都已经在这里了。
5赞 Nurul Akter Towhid 7/29/2017 #20

使用

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

使用唯一

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a
10赞 Anurag Misra 8/17/2017 #21

从列表中删除重复项的最佳方法是使用 python 中提供的 set() 函数,再次将该集合转换为列表

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

评论

0赞 Anurag Misra 5/2/2018
@MeetZaveri高兴。!
0赞 Z4-tier 12/24/2019
实例化新列表和集不是免费的。如果我们快速连续地多次这样做(即在一个非常紧密的循环中),并且列表非常小,会发生什么?
5赞 Suresh Gupta 10/12/2017 #22

不使用 set

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) 
10赞 Flavio Wuensche 9/18/2018 #23

您可以使用以下方法删除重复项:set

mylist = list(set(mylist))

但请注意,结果将是无序的。如果这是一个问题:

mylist.sort()

评论

1赞 Erik Campobadal 1/3/2019
你可以做: mylist = sorted(list(set(mylist)))
7赞 Akarsh Jain 10/6/2018 #24

另一种更好的方法是,

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]

并且订单保持不变。

评论

2赞 Glutexo 3/20/2019
虽然这可能很有效,但为此目的使用像 pandas 这样的重型库似乎有点矫枉过正。
6赞 Cybernetic 10/24/2018 #25

您可以使用以下功能:

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

示例

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

用法:

rem_dupes(my_list)

['this', 'is', 'a', 'list', 'with', 'dupicates', 'in', 'the']

评论

0赞 ingyhere 3/30/2021
不适合大型列表,因为它会创建重复项。
0赞 Cybernetic 3/30/2021
@ingyhere OP 没有提出任何建议:大名单。每种类型的实现总是有一个权衡,因此每个答案必须默认为“最具可伸缩性”的前提是错误的。
14赞 HEEL_caT666 2/24/2019 #26

如果您想保留顺序,并且不使用任何外部模块,这里有一种简单的方法可以做到这一点:

>>> 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]

注意:此方法保留了出现的顺序,因此,如上所示,九个将出现在一个之后,因为这是它第一次出现。但是,这与您这样做的结果相同

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

但它要短得多,运行速度也快得多。

这是有效的,因为每次函数尝试创建新键时,如果该值已经存在,它只会覆盖它。但是,这根本不会影响字典,因为会创建一个所有键都具有值的字典,因此它以这种方式有效地消除了所有重复项。fromkeysfromkeysNone

评论

0赞 vineeshvs 5/2/2019
也可以在这里尝试一下
5赞 Milo Chen 9/18/2019 #27

Python 内置类型的魔力

在 python 中,处理这样的复杂情况非常容易,并且只能通过 python 的内置类型。

让我告诉你怎么做!

方法一:一般情况

删除列表中重复元素并仍保持排序顺序的方法(1 行代码

line = [1, 2, 3, 1, 2, 5, 6, 7, 8]
new_line = sorted(set(line), key=line.index) # remove duplicated element
print(new_line)

你会得到结果

[1, 2, 3, 5, 6, 7, 8]

方法2:特殊情况

TypeError: unhashable type: 'list'

处理不可哈希(3 行代码)的特殊情况)

line=[['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['16.4966155686595', '-27.59776154691', '52.3786295521147']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['17.6508629295574', '-27.143305738671', '47.534955022564']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['18.8051102904552', '-26.688849930432', '42.6912804930134']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['19.5504702331098', '-26.205884452727', '37.7709192714727']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']
,['20.2929416861422', '-25.722717575124', '32.8500163147157']]

tuple_line = [tuple(pt) for pt in line] # convert list of list into list of tuple
tuple_new_line = sorted(set(tuple_line),key=tuple_line.index) # remove duplicated element
new_line = [list(t) for t in tuple_new_line] # convert list of tuple into list of list

print (new_line)

你会得到结果:

[
  ['16.4966155686595', '-27.59776154691', '52.3786295521147'], 
  ['17.6508629295574', '-27.143305738671', '47.534955022564'], 
  ['18.8051102904552', '-26.688849930432', '42.6912804930134'], 
  ['19.5504702331098', '-26.205884452727', '37.7709192714727'], 
  ['20.2929416861422', '-25.722717575124', '32.8500163147157']
]

因为元组是可散列的,你可以轻松地在列表和元组之间转换数据

35赞 Corman 10/6/2019 #28

在这个答案中,将有两个部分:两个独特的解决方案,以及特定解决方案的速度图。

删除重复项目

这些答案中的大多数只删除了可散列的重复项目,但这个问题并不意味着它不仅需要可散列项目,这意味着我将提供一些不需要可散列项目的解决方案。

collections.Counter是标准库中的一个强大工具,可以完美地做到这一点。只有另一种解决方案甚至包含计数器。但是,该解决方案也仅限于可哈希密钥。

为了允许在 Counter 中使用不可哈希的键,我制作了一个 Container 类,它将尝试获取对象的默认哈希函数,但如果失败,它将尝试其标识函数。它还定义了一个 eq 和一个哈希方法。这应该足以在我们的解决方案中允许不可哈希的项目。不可散列的对象将被视为可散列的。但是,此哈希函数对不可哈希对象使用标识,这意味着两个不可哈希的相等对象将不起作用。我建议你覆盖它,并改变它以使用等效可变类型的哈希值(比如使用if是一个列表)。hash(tuple(my_list))my_list

我还提出了两个解决方案。另一种解决方案,它使用名为“OrderedCounter”的 OrderedDict 和 Counter 的子类来保持项目的顺序。现在,这里是功能:

from collections import OrderedDict, Counter

class Container:
    def __init__(self, obj):
        self.obj = obj
    def __eq__(self, obj):
        return self.obj == obj
    def __hash__(self):
        try:
            return hash(self.obj)
        except:
            return id(self.obj)

class OrderedCounter(Counter, OrderedDict):
     'Counter that remembers the order elements are first encountered'

     def __repr__(self):
         return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))

     def __reduce__(self):
         return self.__class__, (OrderedDict(self),)
    
def remd(sequence):
    cnt = Counter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

def oremd(sequence):
    cnt = OrderedCounter()
    for x in sequence:
        cnt[Container(x)] += 1
    return [item.obj for item in cnt]

remd是无序排序,而是有序排序。你可以清楚地分辨出哪个更快,但我还是会解释的。无序排序速度稍快,因为它不存储项目的顺序。oremd

现在,我还想显示每个答案的速度比较。所以,我现在就去做。

哪个功能最快?

为了删除重复项,我从几个答案中收集了 10 个函数。我计算了每个函数的速度,并使用 matplotlib.pyplot 将其放入图表中。

我把它分成三轮图表。哈希对象是任何可以哈希处理的对象,不可哈希对象是任何不能哈希处理的对象。有序序列是保持秩序的序列,无序序列不保留秩序。现在,这里还有一些术语:

无序 Hashable 适用于任何删除重复项的方法,这些方法不一定必须保留顺序。它不必为不可hashables工作,但它可以。

Ordered Hashable 适用于任何保持列表中项目顺序的方法,但它不必适用于不可散列的,但它可以。

Ordered Unhashable 是任何保持列表中项目顺序并适用于 unhashable 的方法。

y 轴上是它所花费的秒数。

x 轴上是应用函数的数字。

我为无序哈希函数和有序哈希函数生成了序列,其理解如下:[list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]

对于有序的 unhashables:[[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]

请注意,该范围内有一个,因为如果没有它,这将花费 10 倍的时间。也因为在我个人看来,我认为它可能看起来更容易阅读。step

另请注意,图例上的键是我试图猜测的函数实现中最重要的部分。至于什么功能最差或最好?图表不言自明。

解决了这个问题,这里是图表。

无序哈希函数

Unordered Hashables(放大)Unordered Hashables Zoomed

有序哈希布尔

Ordered Hashables(放大)Ordered Hashables Zoomed

有序的 Unhashables

Ordered Unhashables(放大)Ordered Unhashables Zoomed

评论

1赞 questionto42 9/14/2021
很难读。最好在底部有一个顶部列表,并将结果包装起来。因此,对于无序的哈希值:如果 ii 不在 seq[:n] #- cnt = Counter();cnt[Container(x)] += 1 #- cnt = OrderedCounter();cnt[Container(x)) += 1 #- if i not in new for i in seq. better use: #- list(set(seq)) #- dict.fromkeys(seq) #- added = set();for in seq: if not val in added #- OrderedDict.fromkeys(seq) #- OrderedDict((x, True) for x in seq).keys() #- functools.reduce(lambda r, v: v in r[1] and r or ...或者 ..., ([], set[]))[0]
39赞 Pedro Lobito 3/4/2020 #29

超级迟到的答案

如果你不关心列表顺序,你可以使用具有唯一性的扩展来删除重复项,即:*argset

l = [*{*l}]

Python3 演示

评论

11赞 mike rodent 10/18/2021
好。。。一个问题是它太聪明了,你必须添加一个评论来说明它的作用。
13赞 Nico Schlömer 8/5/2021 #30

我已经将各种建议与 perfplot 进行了比较。事实证明,如果输入数组没有重复的元素,则所有方法或多或少都一样快,无论输入数据是 Python 列表还是 NumPy 数组。

enter image description here

如果输入数组很大,但只包含一个唯一元素,则 和 方法是成本时间的,如果输入数据是列表。如果它是 NumPy 数组,则比其他替代方案快约 10 倍。setdictnp.uniquenp.unique

enter image description here

令我惊讶的是,这些操作也不是恒定时间操作。


用于重现绘图的代码:

import perfplot
import numpy as np
import matplotlib.pyplot as plt


def setup_list(n):
    # return list(np.random.permutation(np.arange(n)))
    return [0] * n


def setup_np_array(n):
    # return np.random.permutation(np.arange(n))
    return np.zeros(n, dtype=int)


def list_set(data):
    return list(set(data))


def numpy_unique(data):
    return np.unique(data)


def list_dict(data):
    return list(dict.fromkeys(data))


b = perfplot.bench(
    setup=[
        setup_list,
        setup_list,
        setup_list,
        setup_np_array,
        setup_np_array,
        setup_np_array,
    ],
    kernels=[list_set, numpy_unique, list_dict, list_set, numpy_unique, list_dict],
    labels=[
        "list(set(lst))",
        "np.unique(lst)",
        "list(dict(lst))",
        "list(set(arr))",
        "np.unique(arr)",
        "list(dict(arr))",
    ],
    n_range=[2 ** k for k in range(23)],
    xlabel="len(array)",
    equality_check=None,
)
# plt.title("input array = [0, 1, 2,..., n]")
plt.title("input array = [0, 0,..., 0]")
b.save("out.png")
b.show()