在Python中从列表中获取唯一值

1195

我想从以下列表中获取唯一值:

['nowplaying', 'PBS', 'PBS', 'nowplaying', 'job', 'debate', 'thenandnow']
我需要的输出是:
['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']

这段代码有效:

output = []
for x in trends:
    if x not in output:
        output.append(x)
print(output)

有没有更好的解决方案我应该使用?


21
顺序很重要吗?也就是说,你想要第一次出现的顺序,还是["PBS",“辩论”,“工作”,“thenandnow”,“nowplaying”]这个顺序也可以? - DSM
9
所有最佳解决方案都适用于问题示例,但它们并没有回答问题。它们都使用了“set”,这取决于列表中找到的类型。例如:“d = dict();l = list();l.append(d); set(l)”将导致“TypeError: unhashable type: 'dict”。而“frozenset”则无法解决此问题。按照真正Pythonic的方式学习:实现一个嵌套n^2循环来从列表中删除重复项。然后您可以将其优化为n.log n。或者为对象实现真正的哈希。或在创建集之前对对象进行编组处理。 - ribamar
10
如果您需要保留列表的顺序:unique_items = list(dict.fromkeys(list_with_duplicates)) (CPython 3.6+) - user3064538
如何使用多进程在非常大的列表中删除重复项? - Darkonaut
//np.unique(listName) //np.unique(listName) - Tanmoy
30个回答

1477

首先正确声明您的列表,用逗号分隔。通过将列表转换为set来获取唯一值。

mylist = ['nowplaying', 'PBS', 'PBS', 'nowplaying', 'job', 'debate', 'thenandnow']
myset = set(mylist)
print(myset)
如果您要进一步将其用作列表,您应该通过执行以下操作将其转换回列表:
mynewlist = list(myset)

另一个可能更快的选择是从一开始就使用集合而不是列表。然后你的代码应该是:

output = set()
for x in trends:
    output.add(x)
print(output)

正如已经指出的那样,集合不维护原始顺序。如果您需要,您应该寻找一个有序集合实现(请参见此问题以了解更多信息)。


8
如果您需要保持已设定的顺序,PyPI上也有一个库可供使用:https://pypi.python.org/pypi/ordered-set - Jace Browning
11
为什么列表使用 '.append',而集合使用 '.add'? - Antonello
68
"append" 的意思是将内容添加到结尾,这个词用于列表很准确也很合理,但是集合没有顺序概念,因此没有开始或结束,所以对它们来说,“add”更加合适。 - maackle
3
“sets”模块已被弃用,您不需要“import sets”来获取功能。如果您看到“import sets; output = sets.Set()”,则已过时。此答案使用内置的“set”类:https://docs.python.org/2/library/stdtypes.html#set - FlipMcF
11
如果列表中的值不可哈希(例如集合或列表),则此方法无效。 - steffen
显示剩余5条评论

465
为了保持一致,我会使用相同类型:
mylist = list(set(mylist))

137
请注意,结果将无序。 - Aminah Nuraini
43
@Ninjakannon,你的代码会按字母顺序对列表进行排序。这不一定是原始列表的顺序。 - johk95
21
请注意,在Python 3中实现这一点的一个简洁方法是 mylist = [*{*mylist}]。这是一种类似于*arg的集合扩展,后跟一个类似于*arg的列表扩展。 - Luke Davis
5
еҜ№жҲ‘иҖҢиЁҖпјҢ@LukeDavisзҡ„жңҖдҪізӯ”жЎҲжҳҜsorted([*{*c}])жҜ”sorted(list(set(c)))жӣҙеҝ«25%пјҲдҪҝз”Ёtimeit.repeatиҝӣиЎҢдәҶ100,000ж¬ЎжөӢйҮҸпјүгҖӮ - jeannej
6
请注意:如果列表包含不可哈希的元素(例如,元素本身是集合、列表或哈希值),则此方法会失败。 - Heinrich supports Monica
显示剩余2条评论

231

如果我们需要保持元素的顺序,这样如何:

used = set()
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for x in mylist if x not in used and (used.add(x) or True)]

还有一种解决方案,使用reduce而不需要临时变量used
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])

更新 - 2020年12月 - 或许是最佳方法!

从Python 3.7开始,标准的dict保留了插入顺序。

从版本3.7开始:字典顺序保证为插入顺序。这个行为在CPython 3.6中是一个实现细节。

因此,我们可以使用dict.fromkeys()进行去重!

注意:感谢@rlat在评论中给出了这个方法!

mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = list(dict.fromkeys(mylist))

从速度方面来说-对我来说,它足够快且易读,成为我新的最爱方式!
更新-2019年3月
还有第三个解决方案,很巧妙,但由于.index是O(n),所以有点慢。
mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = [x for i, x in enumerate(mylist) if i == mylist.index(x)]

更新 - 2016年10月

另一种解决方案是使用reduce,但这次没有使用.append,使得代码更易读和理解。

mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
unique = reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])
#which can also be writed as:
unique = reduce(lambda l, x: l if x in l else l+[x], mylist, [])

注意:请记住,我们越易读,脚本的性能就越差。除了适用于Python 3.7+的dict.fromkeys()方法外。
import timeit

setup = "mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']"

#10x to Michael for pointing out that we can get faster with set()
timeit.timeit('[x for x in mylist if x not in used and (used.add(x) or True)]', setup='used = set();'+setup)
0.2029558869980974

timeit.timeit('[x for x in mylist if x not in used and (used.append(x) or True)]', setup='used = [];'+setup)
0.28999493700030143

# 10x to rlat for suggesting this approach!   
timeit.timeit('list(dict.fromkeys(mylist))', setup=setup)
0.31227896199925453

timeit.timeit('reduce(lambda l, x: l.append(x) or l if x not in l else l, mylist, [])', setup='from functools import reduce;'+setup)
0.7149233570016804

timeit.timeit('reduce(lambda l, x: l+[x] if x not in l else l, mylist, [])', setup='from functools import reduce;'+setup)
0.7379565160008497

timeit.timeit('reduce(lambda l, x: l if x in l else l+[x], mylist, [])', setup='from functools import reduce;'+setup)
0.7400134069976048

timeit.timeit('[x for i, x in enumerate(mylist) if i == mylist.index(x)]', setup=setup)
0.9154880290006986

回答评论
因为@monica提出了一个关于“这是如何工作的?”的好问题。对于所有在弄清楚它的人来说,我将尝试给出更深入的解释,解释一下这是如何工作的,以及这里发生了什么神奇的事情 ;)
所以她首先问道:
“我试图理解为什么unique = [used.append(x) for x in mylist if x not in used]不起作用。”
嗯,实际上它是起作用的。
>>> used = []
>>> mylist = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
>>> unique = [used.append(x) for x in mylist if x not in used]
>>> print used
[u'nowplaying', u'PBS', u'job', u'debate', u'thenandnow']
>>> print unique
[None, None, None, None, None]

问题在于我们只能在used变量中得到期望的结果,而无法在unique变量中得到。这是因为在列表推导中,.append修改了used变量并返回None
为了将结果放入unique变量中,并仍然使用.append(x) if x not in used的逻辑,我们需要将这个.append调用移到列表推导的右侧,并在左侧只返回x
但是如果我们太天真,只是简单地进行以下操作:
>>> unique = [x for x in mylist if x not in used and used.append(x)]
>>> print unique
[]

我们将得不到任何回报。

再次,这是因为.append方法返回None,从而在我们的逻辑表达式中产生了以下效果:

x not in used and None

这基本上总是会发生以下情况:
  1. xused 中时,评估结果为 False
  2. x 不在 used 中时,评估结果为 None
而在这两种情况下(False/None),这将被视为假值,并且我们将得到一个空列表作为结果。
但是为什么当 x 不在 used 中时,它会评估为 None?有人可能会问。
嗯,这是因为这是 Python 的短路运算符的工作方式

表达式 x and y 首先评估 x;如果 x 是假值,则返回其值;否则,评估 y 并返回结果值。

所以当 x 没有被使用时(即当它是 True 时),下一部分或表达式将会被计算(used.append(x)),并且它的值(None)将被返回。

但这正是我们想要的,为了从一个包含重复元素的列表中获取唯一的元素,我们只想在第一次遇到它们时才将其.append到新列表中。

所以我们真正希望只有在 x 不在 used 中时才计算 used.append(x)。也许如果有办法将这个 None 值转换为一个 truthy 值,那么我们就会没问题,对吗?

是的,在这里就是第二种类型的 short-circuit 运算符发挥作用的地方。

表达式 x or y 首先计算 x;如果 x 为真,则返回其值;否则,计算 y 并返回结果值。

我们知道.append(x)总是为falsy,所以如果我们只是在旁边添加一个or,我们将始终得到下一部分。这就是为什么我们写成:
x not in used and (used.append(x) or True)

这样我们可以评估 used.append(x) 并且得到 True 作为结果,仅当 表达式的第一部分 (x not in used)True 时。

类似的方式也可以在第二种方法中通过 reduce 方法看到。

(l.append(x) or l) if x not in l else l
#similar as the above, but maybe more readable
#we return l unchanged when x is in l
#we append x to l and return l when x is not in l
l if x in l else (l.append(x) or l)

我们的操作步骤如下:
  1. x 不在 l 中时,将 x 添加到 l 并返回该 l。由于使用了 or 语句,.append 在执行后会返回 l
  2. xl 中时,直接返回原始的 l

我试图理解为什么 unique = [used.append(x) for x in mylist if x not in used] 不起作用。为什么我们必须在列表推导式的末尾加上 and (used.append(x) or True) - Monica
3
基本上,因为used.append(x)x添加到used中,但此函数的返回值为None,所以如果我们跳过or True部分,则得到:x not in used and None,这将始终计算为False,并且unique列表将保持为空。 - Todor
2
不用担心,没有愚蠢的问题,只有愚蠢的回答 :) 我更新了我的答案,试图更好地解释它的工作原理,希望我现在讲得清楚,你能够理解它。 - Todor
1
更快的方法是使用集合:timeit.timeit('[x for x in mylist if x not in used and not used.add(x)]', setup='used = set();'+setup) - Michael
5
另一个值得一提的选项是使用 Python 3.7 开始可用的 dict,它保留了键的顺序,但同时也去除了重复项:list(dict.fromkeys(mylist))。就时间来看,它排名第三。 - rlat
显示剩余7条评论

122
一个Python列表:
>>> a = ['a', 'b', 'c', 'd', 'b']

要获取唯一的项,只需将其转换为集合(如果需要,您可以将其转回列表):

>>> b = set(a)
>>> print(b)
{'b', 'c', 'd', 'a'}

64
好的,所以a = list(set(a))会得到唯一的项。 - Brian Burns
11
Brian,set(a)就足以“获得唯一的元素”。只有在你特别需要一个列表时才需要构建另一个列表。 - jbg
7
结果会是无序的。 - Timothy Aaron

111

你的输出变量是什么类型?

Python 集合是你需要的。像这样声明输出:

output = set()  # initialize an empty set

接下来,您只需使用output.add(elem)添加元素并确保它们是唯一的即可。

警告:集合不会保留列表的原始顺序。


86

去重的选项可能包括以下通用数据结构:

下面是关于如何在 Python 中快速获取两者中的任意一个的摘要。

给定

from collections import OrderedDict


seq = [u"nowplaying", u"PBS", u"PBS", u"nowplaying", u"job", u"debate", u"thenandnow"]

代码

选项1 - 一个set(无序):

list(set(seq))
# ['thenandnow', 'PBS', 'debate', 'job', 'nowplaying']
    

Python 没有有序集合,但这里有一些方法可以模拟一个:some ways

选项 2 - 使用 OrderedDict(插入顺序):

list(OrderedDict.fromkeys(seq))
# ['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']

选项3 - 一个dict(插入有序),在Python 3.6+中默认。更多详细信息请参见此post

list(dict.fromkeys(seq))
# ['nowplaying', 'PBS', 'job', 'debate', 'thenandnow']

注意:列出的元素必须是可哈希的。有关后一个示例的详细信息,请参见此博客文章。此外,请查看R. Hettinger的文章介绍相同的技术;有序字典扩展自他早期的一种实现。还可以参阅更多关于全序集的内容。


4
@Henry Henrinson,感谢您在对此答案进行负评时表达出您的原因。然而,您声称“Python 3.6解决方案不保留顺序”的观点和声明没有参考资料支持。明确一下,在Python 3.6中,字典保留插入顺序,这是CPython实现中的语言特性。这是Python 3.7+中的一项新功能。此外,请参见正在进行中的博客文章,该方法声称在当时是Python 3.6中最快的有序选项。 - pylang

56

维护秩序:

# oneliners
# slow -> . --- 14.417 seconds ---
[x for i, x in enumerate(array) if x not in array[0:i]]

# fast -> . --- 0.0378 seconds ---
[x for i, x in enumerate(array) if array.index(x) == i]

# multiple lines
# fastest -> --- 0.012 seconds ---
uniq = []
[uniq.append(x) for x in array if x not in uniq]
uniq

顺序无关紧要:

# fastest-est -> --- 0.0035 seconds ---
list(set(array))

1
这个程序性能很差(O(n^2)),对于大型列表而言,它既不比“list(set(array))”更简单易读,也没有任何优势,除了保留顺序,但这并不是要求的。 - jlh
2
这对于简单的脚本非常有用,您想保持顺序并且不关心速度。 - JeffCharter
@JeffCharter- 添加了一个保持顺序且速度更快的 :) - daino3
在 Python 中,这个“东西”/操作 [uniq.append(x) for x in array if x not in uniq] 叫什么? - MMT
1
@MMT - 列表推导式 - daino3
3
我很感激您花时间分解时间戳。 - Lotus

22

从列表中获取唯一元素

mylist = [1,2,3,4,5,6,6,7,7,8,8,9,9,10]

使用集合的简单逻辑 - 集合是一组唯一的项目

mylist=list(set(mylist))

In [0]: mylist
Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

运用简单逻辑

newList=[]
for i in mylist:
    if i not in newList:
        newList.append(i)

In [0]: mylist
Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

使用pop方法 -> pop方法可以移除最后一个或者索引位置的元素,并将其展示给用户。 视频

k=0
while k < len(mylist):
    if mylist[k] in mylist[k+1:]:
        mylist.pop(mylist[k])
    else:
        k=k+1

In [0]: mylist
Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

使用Numpy

import numpy as np
np.unique(mylist)

In [0]: mylist
Out[0]: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

参考资料


1
这个回答值得更多的点赞:对于不可哈希类型,如果你想检查_value_的唯一性而不是_identity_的唯一性,那么简单的逻辑是正确的 - 这意味着它在一般情况下更正确。 - ocket8888

17

set是一个无序的、不含重复元素的集合。可以将元素列表传递给set的构造函数。因此,如果传递了包含重复元素的列表,我们将得到一个只包含唯一元素的set,并将其转换回列表,那么我们就会得到一个只包含唯一元素的列表。我无法对性能和内存开销做出评论,但我希望对于小型列表来说这不是很重要。

list(set(my_not_unique_list))

简单明了。


1
你能为 OP 在你的代码中添加一些解释吗? - Paco
我试过了你的答案,这是一个很好的答案,但如果有说明的话,它将会变成一个更好的答案 :) - Papouche Guinslyzinho
1
集合 - 无序的唯一元素集合。可以将元素列表传递给集合的构造函数。因此,如果传递具有重复元素的列表,则会得到具有唯一元素的集合,并将其转换回列表,然后获取具有唯一元素的列表。我无法对性能和内存开销做出任何评论,但我希望在小型列表中这不是很重要。 - MultiTeemer

17
如果您在编程中使用numpy(对于大量数据可能是一个不错的选择),请查看numpy.unique:
>>> import numpy as np
>>> wordsList = [u'nowplaying', u'PBS', u'PBS', u'nowplaying', u'job', u'debate', u'thenandnow']
>>> np.unique(wordsList)
array([u'PBS', u'debate', u'job', u'nowplaying', u'thenandnow'], 
      dtype='<U10')

(http://docs.scipy.org/doc/numpy/reference/generated/numpy.unique.html)

正如你所看到的,NumPy不仅支持数字数据,还支持字符串数组。当然,结果是一个NumPy数组,但这并不重要,因为它仍然像一个序列一样运作。
>>> for word in np.unique(wordsList):
...     print word
... 
PBS
debate
job
nowplaying
thenandnow

如果你真的想要一个纯粹的Python列表,你可以随时调用list()。
然而,结果会自动排序,正如上面的代码片段所示。如果需要保留列表顺序,请查看numpy unique without sort

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接