本节描述了如何在 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]
使用了列表理解的符号。
- 相关的。如何使用Python的列表理解法
在此,我们使用以下内容
- 如果 “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'