在Python中从列表(数组)中删除和提取重复的元素

商业

本节描述了如何在 Python 中通过从一个列表 (数组) 中删除或提取重复的元素来生成一个新的列表。

这里介绍一下以下细节。

  • 删除重复的元素并生成新的列表
    • 不保留原始清单的顺序:set()
    • 保留了原始清单的顺序: dict.fromkeys(),sorted()
    • 二维数组(列表的列表)。
  • 提取重复的元素并生成一个新的列表
    • 不保留原始清单的顺序
    • 保留了原始清单的顺序
    • 二维数组(列表的列表)。

同样的概念也可以应用于图元而不是列表。

参见以下文章,了解

  • 如果你想确定一个列表或元组是否有重复的元素
  • 如果你想提取多个列表中常见或不常见的元素,而不是单一的列表

注意,列表可以存储不同类型的数据,与数组有严格的区别。如果你想在需要内存大小和内存地址的过程中处理数组,或者对大数据进行数值处理,请使用array(标准库)或NumPy。

删除重复的元素并生成新的列表

不保留原始清单的顺序: set()

如果不需要保留原始列表的顺序,可以使用set(),它生成一个集合类型的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]

当然,它也可以留作set。关于集合类型set的更多信息,请看下面的文章。

保留了原始清单的顺序: dict.fromkeys(),sorted()

如果你想保留原始列表的顺序,请使用 dictionary 类型的类方法 fromkeys() 或内置函数 sorted()。

dict.fromkeys() 创建一个新的 dictionary 对象,其键是参数中指定的 list、tuples 等。如果第二个参数被省略,其值为 None。

由于 dictionary key 没有重复的元素,重复的值会像 set() 一样被忽略。此外,一个 dictionary 对象可以作为参数传给 list() ,以获得一个元素是 dictionary key 的 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的参数key指定列表元组方法index(),它返回一个排序的元素列表。

index()是一个返回值的索引(列表中元素的编号)的方法,它可以被指定为 sorted()的键,以根据原始列表的顺序对列表进行排序。参数key被指定为可调用(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和Y “中的X在and运算符的短路评估中是假的,那么Y就不会被评估(不执行)。
  • append()方法返回无。

如果原始列表seq中的元素不存在于所看到的,那么就会对和后进行评估。
seen.append(x)被执行,该元素被添加到seed中。
因为append()方法返回None,而None是假的,所以没有看到.append(x)的评估结果是真。
列表理解符号中的条件表达式变成了 “真”,并被添加为最终生成的列表的一个元素。

如果原始列表seq中的元素存在于see中,那么不在see中的x就是假的,列表理解表达的条件表达式就是假的。
因此,它们不会被添加为最终生成的列表中的元素。

另一种方法是在NumPy的函数np.unique()中设置参数轴,尽管结果会被排序。

提取重复的元素并生成一个新的列表

不保留原始清单的顺序

要从原始列表中只提取重复的元素,请使用collection.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})

由于它是 dictionary 的一个子类,items() 可以用来检索键和值。只需提取数量为两个或更多的键即可。

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

保留了原始清单的顺序

如上面的例子所示,从Python 3.7开始,collection.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]

如果你想用重复的东西来提取,就从原始列表中留下计数为2或更多的元素。

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 是 dictionary 的子类,所以如果你把一个元素是列表或其他非哈希对象的列表或元组传给 collections.Counter(),将会发生错误,你将无法使用它。

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