簡體   English   中英

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

[英]Checking if all elements in a list are unique

檢查列表中所有元素是否唯一的最佳方法(最好是傳統方法)是什么?

我目前使用Counter的方法是:

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

我可以做得更好嗎?

不是最有效的,但直截了當和簡潔:

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

可能不會對短名單產生太大影響。

這是一個兩班輪,也將提前退出:

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

一個提前退出的解決方案可能是

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))是最快的方法。

速度:

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

如何將所有條目添加到集合並檢查其長度?

len(set(x)) == len(x)

替代set ,您可以使用dict

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

完全使用 sorted 和 groupby 的另一種方法:

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

它需要排序,但在第一個重復值上退出。

這是一個有趣的遞歸 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

這是一個遞歸的提前退出函數:

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)    

它對我來說已經足夠快了,而無需使用怪異(緩慢)的轉換,同時具有功能風格的方法。

這個怎么樣

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

上面的所有答案都很好,但我更喜歡使用30 秒 python 中的all_unique示例

您需要在給定列表上使用set()來刪除重復項,將其長度與列表的長度進行比較。

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

如果平面列表中的所有值都是unique ,則返回True ,否則返回False

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

當且僅當您的依賴項中有數據處理庫 pandas 時,有一個已經實現的解決方案可以提供您想要的布爾值:

import pandas as pd
pd.Series(lst).is_unique

在 Pandas 數據框中使用類似的方法來測試列的內容是否包含唯一值:

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

對我來說,這在包含超過一百萬行的日期幀中的 int 變量上是瞬時的。

您可以使用 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

它並不完全適合這個問題,但是如果你用谷歌搜索我讓你得到的這個問題排名第一的任務,它可能會引起用戶的興趣,因為它是問題的擴展。 如果要調查每個列表元素是否唯一,可以執行以下操作:

import timeit
import numpy as np

def get_unique(mylist):
    # sort the list and keep the index
    sort = sorted((e,i) for i,e in enumerate(mylist))
    # check for each element if it is similar to the previous or next one    
    isunique = [[sort[0][1],sort[0][0]!=sort[1][0]]] + \
               [[s[1], (s[0]!=sort[i-1][0])and(s[0]!=sort[i+1][0])] 
                for [i,s] in enumerate (sort) if (i>0) and (i<len(sort)-1) ] +\
               [[sort[-1][1],sort[-1][0]!=sort[-2][0]]]     
    # sort indices and booleans and return only the boolean
    return [a[1] for a in sorted(isunique)]


def get_unique_using_count(mylist):
     return [mylist.count(item)==1 for item in mylist]

mylist = list(np.random.randint(0,10,10))
%timeit for x in range(10): get_unique(mylist)
%timeit for x in range(10): get_unique_using_count(mylist)

mylist = list(np.random.randint(0,1000,1000))
%timeit for x in range(10): get_unique(mylist)
%timeit for x in range(10): get_unique_using_count(mylist)

對於一些答案中建議的簡短列表, get_unique_using_count很快。 但是,如果您的列表已經超過 100 個元素,則 count 函數需要很長時間。 因此, get_unique函數中顯示的方法要快得多,盡管它看起來更復雜。

如果列表仍然排序,您可以使用:

not any(sorted_list[i] == sorted_list[i + 1] for i in range(len(sorted_list) - 1))

非常有效,但不值得為此目的進行排序。

我將建議的解決方案與perfplot進行了比較,發現

len(lst) == len(set(lst))

確實是最快的解決方案。 如果列表中有早期重復項,則首選一些恆定時間解決方案。

在此處輸入圖像描述

在此處輸入圖像描述


重現 plot 的代碼:

import perfplot
import numpy as np
import pandas as pd


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


def set_add(lst):
    seen = set()
    return not any(i in seen or seen.add(i) for i in lst)


def list_append(lst):
    seen = list()
    return not any(i in seen or seen.append(i) for i in lst)


def numpy_unique(lst):
    return np.unique(lst).size == len(lst)


def set_add_early_exit(lst):
    s = set()
    for item in lst:
        if item in s:
            return False
        s.add(item)
    return True


def pandas_is_unique(lst):
    return pd.Series(lst).is_unique


def sort_diff(lst):
    return not np.any(np.diff(np.sort(lst)) == 0)


b = perfplot.bench(
    setup=lambda n: list(np.arange(n)),
    title="All items unique",
    # setup=lambda n: [0] * n,
    # title="All items equal",
    kernels=[
        len_set,
        set_add,
        list_append,
        numpy_unique,
        set_add_early_exit,
        pandas_is_unique,
        sort_diff,
    ],
    n_range=[2**k for k in range(18)],
    xlabel="len(lst)",
)

b.save("out.png")
b.show()

對於初學者:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM