检查列表中的所有元素是否唯一


104

检查列表中所有元素是否唯一的最佳方法(与传统方法一样最佳)是什么?

我目前使用的方法Counter是:

>>> x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
>>> counter = Counter(x)
>>> for values in counter.itervalues():
        if values > 1: 
            # do something

我可以做得更好吗?

Answers:


164

不是最高效的,而是简单明了的:

if len(x) > len(set(x)):
   pass # do something

短名单可能不会有太大的不同。


这也是我所做的。虽然对于大型列表而言可能效率不高。
tkerwin 2011年

如果列表包含重复的元素(示例中的“#做某事”),则不一定会执行条件的主体。
yan

2
足够公平,很好的解决方案。我几乎处理不到500个元素,所以这应该做我想要的。
user225312 2011年

4
对于那些担心有一长串的效率,这有效的长期列出实际上是唯一的(其中所有元素都需要检查)。对于实际唯一的列表,提早退出解决方案需要更长的时间(在我的测试中大约需要2倍的时间)。所以...如果您希望大多数列表都是唯一的,请使用此简单的设置长度检查解决方案。如果您希望大多数列表不是唯一的,请使用提前退出解决方案。使用哪个取决于您的用例。
拉斯

这个答案很好。但是,请注意此处:len(x) > len(set(x))当其中的元素x不是唯一时为True 。这个问题的标题恰好相反:“检查列表中的所有元素是否唯一”
WhyWhat

95

这里有两个班轮,它们也会提前退出:

>>> def allUnique(x):
...     seen = set()
...     return not any(i in seen or seen.add(i) for i in x)
...
>>> allUnique("ABCDEF")
True
>>> allUnique("ABACDEF")
False

如果x的元素不可散列,那么您将不得不使用以下列表seen

>>> def allUnique(x):
...     seen = list()
...     return not any(i in seen or seen.append(i) for i in x)
...
>>> allUnique([list("ABC"), list("DEF")])
True
>>> allUnique([list("ABC"), list("DEF"), list("ABC")])
False

5
+1清理,如果不需要,则不遍历整个列表。
科斯2012年

@ paul-mcguire:您是否愿意在与Apache 2.0兼容的许可证(例如Apache 2、2 / 3行BSD,MIT,X11,zlib)下许可此代码段。我想在我正在使用的Apache 2.0项目中使用它,并且因为StackOverflow的许可条款是fubar,所以我要求您作为原始作者。
瑞安·帕曼

我已经使用MIT许可证发布了其他代码,因此适用于该代码段。我需要做什么特别的事情?
PaulMcG

21

提前退出的解决方案可能是

def unique_values(g):
    s = set()
    for x in g:
        if x in s: return False
        s.add(x)
    return True

但是对于小情况或如果提早退出并不常见,那么我希望len(x) != len(set(x))这是最快的方法。


我接受了其他答案,因为我并不是特别在寻找优化。
user225312 2011年

2
您可以通过在s = set()... 之后添加以下行来缩短此时间return not any(s.add(x) if x not in s else True for x in g)
Andrew Clark

您能解释一下为什么len(x) != len(set(x))不早退的情况会比现在更快吗?这两个操作不是O(len(x))吗?(x原始列表在哪里)
克里斯·雷德福

哦,我知道:您的方法不是O(len(x)),因为您检查if x in sO(len(x))的内部循环。
克里斯·雷德福

14

为了速度:

import numpy as np
x = [1, 1, 1, 2, 3, 4, 5, 6, 2]
np.unique(x).size == len(x)


9

替代a set,您可以使用dict

len({}.fromkeys(x)) == len(x)

9
我认为在集合上使用字典绝对没有优势。似乎不必要地使事情复杂化。
太酷了'17

3

完全使用排序和分组方式的另一种方法:

from itertools import groupby
is_unique = lambda seq: all(sum(1 for _ in x[1])==1 for x in groupby(sorted(seq)))

它需要排序,但是在第一个重复值上退出。


散列比排序更快
IceArdor 2014年

来到这里发布使用相同的解决方案,groupby并找到了这个答案。我觉得这是最优雅的,因为这是一个表达式,可以与内置工具一起使用,而无需任何额外的变量或循环语句。
拉尔斯·布伦伯格

1
如果您的列表包含无法排序的任意对象,则可以使用该id()函数对它们进行排序,因为这是工作的先决条件groupby()groupby(sorted(seq), key=id)
Lars Blumberg

3

这是一个有趣的递归O(N 2)版本:

def is_unique(lst):
    if len(lst) > 1:
        return is_unique(s[1:]) and (s[0] not in s[1:])
    return True

2

这是递归的提前退出函数:

def distinct(L):
    if len(L) == 2:
        return L[0] != L[1]
    H = L[0]
    T = L[1:]
    if (H in T):
            return False
    else:
            return distinct(T)    

对于我来说,它足够快,而无需使用怪异的(慢速)转换,同时具有功能样式的方法。


1
H in T进行线性搜索,并T = L[1:]复制列表的切片部分,因此,这比在大列表上建议的其他解决方案要慢得多。我认为是O(N ^ 2),而其他大多数是O(N)(集合)或O(N log N)(基于排序的解决方案)。
Blckknght

1

这个怎么样

def is_unique(lst):
    if not lst:
        return True
    else:
        return Counter(lst).most_common(1)[0][1]==1

0

您可以使用Yan的语法(len(x)> len(set(x))),但可以定义一个函数来代替set(x):

 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

并做len(x)> len(f5(x))。这样会很快,而且还能保留订单。

此处的代码来自:http//www.peterbe.com/plog/uniqifiers-benchmark


此f5函数的速度将比使用set的速度慢,set的速度更好。由于昂贵的“追加”操作,当列表变得很大时,此代码开始中断。对于大型列表(如)x = range(1000000) + range(1000000),运行set(x)比f5(x)快。顺序不是问题的要求,但即使运行sorted(set(x))仍比f5(x)快
OkezieE 2014年

0

在Pandas数据框中使用类似的方法来测试列的内容是否包含唯一值:

if tempDF['var1'].size == tempDF['var1'].unique().size:
    print("Unique")
else:
    print("Not unique")

对我来说,这在包含一百万行的日期框架中的int变量上是瞬时的。


0

以上所有答案都很好,但我更喜欢使用30秒内的Pythonall_unique示例

您需要set()在给定列表上使用来删除重复项,并将其长度与列表的长度进行比较。

def all_unique(lst):
  return len(lst) == len(set(lst))

True如果平面列表中的所有值均为unique,则返回,False否则返回

x = [1,2,3,4,5,6]
y = [1,2,2,3,4,5]
all_unique(x) # True
all_unique(y) # False

-3

对于初学者:

def AllDifferent(s):
    for i in range(len(s)):
        for i2 in range(len(s)):
            if i != i2:
                if s[i] == s[i2]:
                    return False
    return True

我喜欢这个答案,只是因为它很好地显示了使用集合时无需编写哪些代码。我不会将其标记为“面向初学者”,因为我相信初学者应该学会预先正确地进行操作。但是我遇到了一些经验不足的开发人员,他们习惯于用其他语言编写此类代码。
cessor
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.