從 Python 中的列表(數組)中刪除和提取重複元素

商業

本節介紹如何在 Python 中通過從列表(數組)中刪除或提取重複元素來生成新列表。

此處描述了以下詳細信息。

  • 刪除重複元素並生成新列表
    • 不保留原listing的順序:set()
    • 保留原始列表的順序:dict.fromkeys(),sorted()
    • 二維數組(列表列表)
  • 提取重複元素並生成新列表
    • 不保留原listing的順序
    • 保留原始列表的順序
    • 二維數組(列表列表)

相同的概念可以應用於元組而不是列表。

請參閱以下文章

  • 如果要確定列表或元組是否有重複元素
  • 如果要提取多個列表而不是單個列表中常見或不常見的元素

請注意,列表可以存儲不同類型的數據,並且與數組完全不同。如果要在需要內存大小和內存地址的進程中處理數組或對大數據進行數值處理,請使用數組(標準庫)或 NumPy。

刪除重複元素並生成新列表

不保留原listing的順序:set()

如果不需要保留原始列表的順序,使用 set(),它會生成一個集合類型集合。

集合類型是沒有重複元素的數據類型。當將列表或其他數據類型傳遞給 set() 時,將忽略重複值並返回一個 set 類型的對象,其中只有唯一值是元素。

如果要使其成為元組,請使用 tuple()。

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

print(set(l))
# {1, 2, 3, 4, 5}

print(list(set(l)))
# [1, 2, 3, 4, 5]

當然,它也可以保持不變。有關集類型集的更多信息,請參閱以下文章。

保留原始列表的順序:dict.fromkeys(),sorted()

如果要保留原始列表的順序,請使用字典類型的類方法 fromkeys() 或內置函數 sorted()。

dict.fromkeys() 創建一個新的字典對象,其鍵是參數中指定的列表、元組等。如果省略第二個參數,則值為 None。

由於字典鍵沒有重複元素,因此重複值將被忽略,就像在 set() 中一樣。此外,可以將字典對像作為參數傳遞給 list() 以獲得其元素是字典鍵的列表。

print(dict.fromkeys(l))
# {3: None, 2: None, 1: None, 5: None, 4: None}

print(list(dict.fromkeys(l)))
# [3, 2, 1, 5, 4]

自 Python 3.7(CPython 為 3.6)以來,已經保證 dict.fromkeys() 保留參數序列的順序。早期版本使用內置函數 sorted() 如下。

為 sorted 的參數鍵指定列表元組方法 index(),它返回一個已排序的元素列表。

index() 是返回值的索引(列表中元素的編號)的方法,可以指定為 sorted() 的鍵,以按照原始列表的順序對列表進行排序。參數鍵被指定為可調用(callable)對象,所以不要寫()。

print(sorted(set(l), key=l.index))
# [3, 2, 1, 5, 4]

二維數組(列表列表)

對於二維數組(列表的列表),使用 set() 或 dict.fromkeys() 的方法會導致 TypeError。

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]

# l_2d_unique = list(set(l_2d))
# TypeError: unhashable type: 'list'

# l_2d_unique_order = dict.fromkeys(l_2d)
# TypeError: unhashable type: 'list'

這是因為諸如列表之類的不可散列對像不能是 set 類型的元素或 dict 類型的鍵。

定義以下函數 原始列表的順序被保留並適用於一維列表和元組。

def get_unique_list(seq):
    seen = []
    return [x for x in seq if x not in seen and not seen.append(x)]

print(get_unique_list(l_2d))
# [[1, 1], [0, 1], [0, 0], [1, 0]]

print(get_unique_list(l))
# [3, 2, 1, 5, 4]

使用列表理解符號。

在這裡,我們使用以下

  • 如果 “X and Y” 中的 X 在 and 運算符的短路評估中為假,則不評估(不執行)Y。
  • append() 方法返回 None。

如果原始列表 seq 的元素不存在於 seen 中,則評估 then 和 after。
執行 see.append(x) 並將元素添加到 see.
因為 append() 方法返回 None 並且 None 為 False,所以 not seen.append(x) 的計算結果為 True。
列表理解符號中的條件表達式變為 True 並作為最終生成列表的元素添加。

如果原始列表 seq 的元素存在於 seen 中,則 x not in seen 為 False,列表推導表達式的條件表達式為 False。
因此,它們不會作為最終生成列表的元素添加。

另一種方法是在 NumPy 的函數 np.unique() 中設置參數軸,儘管結果會被排序。

提取重複元素並生成新列表

不保留原listing的順序

要僅從原始列表中提取重複元素,請使用 collections.Counter()。
返回一個 collections.Counter(字典的子類),其中元素作為鍵,元素的數量作為值。

import collections

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

print(collections.Counter(l))
# Counter({3: 3, 2: 2, 1: 2, 5: 1, 4: 1})

由於它是字典的子類,因此 items() 可用於檢索鍵和值。提取數量為兩個或更多的鍵就足夠了。

print([k for k, v in collections.Counter(l).items() if v > 1])
# [3, 2, 1]

保留原始列表的順序

如上例所示,從 Python 3.7 開始,collections.Counter 的鍵保持原列表的順序,以此類推。

在早期版本中,使用 sorted() 進行排序就足夠了,刪除重複元素也是如此。

print(sorted([k for k, v in collections.Counter(l).items() if v > 1], key=l.index))
# [3, 2, 1]

如果您希望按原樣提取重複項,只需將原始列表中的元素保留為 2 或更多。訂單也被保留。

cc = collections.Counter(l)
print([x for x in l if cc[x] > 1])
# [3, 3, 2, 1, 1, 2, 3]

二維數組(列表列表)

對於二維數組(列表的列表),在不保留原始列表的順序和保留原始列表的順序時,分別可以使用以下功能。它也適用於一維列表和元組。

l_2d = [[1, 1], [0, 1], [0, 1], [0, 0], [1, 0], [1, 1], [1, 1]]
def get_duplicate_list(seq):
    seen = []
    return [x for x in seq if not seen.append(x) and seen.count(x) == 2]

def get_duplicate_list_order(seq):
    seen = []
    return [x for x in seq if seq.count(x) > 1 and not seen.append(x) and seen.count(x) == 1]

print(get_duplicate_list(l_2d))
# [[0, 1], [1, 1]]

print(get_duplicate_list_order(l_2d))
# [[1, 1], [0, 1]]

print(get_duplicate_list(l))
# [3, 1, 2]

print(get_duplicate_list_order(l))
# [3, 2, 1]

如果要提取重複項,請將原始列表中的元素保留為兩個或更多。

print([x for x in l_2d if l_2d.count(x) > 1])
# [[1, 1], [0, 1], [0, 1], [1, 1], [1, 1]]

請注意,由於 count() 的計算複雜度為 O(n),因此上面顯示的重複執行 count() 的函數效率非常低。可能有更聰明的方法。

Counter 是字典的子類,因此如果將元素為列表或其他不可散列對象的列表或元組傳遞給 collections.Counter(),則會發生錯誤,您將無法使用它。

# print(collections.Counter(l_2d))
# TypeError: unhashable type: 'list'