如何从列表中最佳地删除相同的项目并在Python中对其进行排序?
说我有一个清单:
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
我可以遍历列表的副本(因为你不应该在迭代时改变列表),item for item,并删除除了一个项目之外的所有项目:
for item in my_list[:]: # must iterate over a copy because mutating it
count = my_list.count(item) # see how many are in the list
if count > 1:
for _ in range(count-1): # remove all but one of the element
my_list.remove(item)
删除多余的项目:
['b', 'c', 'a', 'd', 'f', 'e']
然后对列表进行排序:
my_list.sort()
所以my_list现在是:
['a', 'b', 'c', 'd', 'e', 'f']
但是,删除相同元素并对此列表进行排序的最有效和直接(即高效)方法是什么?
* 这个问题出现在工作中(我非常想回答这个问题,但是我们之前的一位高级Python开发人员在我之前就已经开始讨论了这个问题),而且我还在我的本地Python Meetup小组中提出了这个问题。很少有人对它有一个很好的答案,so I'm answering it Q&A style, as suggested by Stackoverflow。
答案 0 :(得分:15)
从列表中删除冗余元素的最佳方法是将其转换为集合,并且由于sorted接受任何iterable并返回列表,因此这比分段执行更有效。
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
def sorted_set(a_list):
return sorted(set(a_list))
new_list = sorted_set(my_list)
和new_list是:
['a', 'b', 'c', 'd', 'e', 'f']
这种方法的缺点是赋予set的元素必须是可清除的,因此如果元素不可删除,则会出现错误:
>>> my_list = [['a'], ['a'], ['b'], ['c']]
>>> sorted(set(my_list))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
这个简单的案例可以通过将子列表作为元组进行处理来解决,这可能比答案中的解决方案更高效,这可能意味着更加昂贵的平等测试:
>>> my_list = [tuple(i) for i in my_list]
>>> sorted(set(my_list))
[('a',), ('b',), ('c',)]
但是其他情况需要找到不同的解决方法。对于其他解决方案,这不是必需的(但同样,计算成本可能更高):
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
适用于子列表:
>>> my_list = [['a'], ['a'], ['b'], ['c']]
>>> remove_extras_and_sort(my_list)
[['a'], ['b'], ['c']]
import timeit
setup = '''
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
def sorted_set(a_list):
return sorted(set(a_list))
'''
timeit.timeit('sorted_set(my_list[:])', setup=setup)
timeit.timeit('remove_extras_and_sort(my_list[:])', setup=setup)
分别在我的系统上测量时返回次数:
1.5562372207641602
4.558010101318359
这意味着问题中给出的方法可能花费的时间是计算时间的3倍,因为每次复制列表需要花费必要的开销(如果我们不复制列表,我们就会这样做)只是排序已经排序的列表,因为设置只运行一次。)
import dis
def remove_extras_and_sort(my_list):
for item in my_list[:]:
count = my_list.count(item)
if count > 1:
for _ in range(count-1):
my_list.remove(item)
my_list.sort()
return my_list
def sorted_set(a_list):
return sorted(set(a_list))
通过查看输出,我们看到第一个函数的字节码长度超过六倍:
>>> dis.dis(remove_extras_and_sort)
2 0 SETUP_LOOP 85 (to 88)
3 LOAD_FAST 0 (my_list)
6 SLICE+0
7 GET_ITER
>> 8 FOR_ITER 76 (to 87)
11 STORE_FAST 1 (item)
3 14 LOAD_FAST 0 (my_list)
17 LOAD_ATTR 0 (count)
20 LOAD_FAST 1 (item)
23 CALL_FUNCTION 1
26 STORE_FAST 2 (count)
4 29 LOAD_FAST 2 (count)
32 LOAD_CONST 1 (1)
35 COMPARE_OP 4 (>)
38 POP_JUMP_IF_FALSE 8
5 41 SETUP_LOOP 40 (to 84)
44 LOAD_GLOBAL 1 (range)
47 LOAD_FAST 2 (count)
50 LOAD_CONST 1 (1)
53 BINARY_SUBTRACT
54 CALL_FUNCTION 1
57 GET_ITER
>> 58 FOR_ITER 19 (to 80)
61 STORE_FAST 3 (_)
6 64 LOAD_FAST 0 (my_list)
67 LOAD_ATTR 2 (remove)
70 LOAD_FAST 1 (item)
73 CALL_FUNCTION 1
76 POP_TOP
77 JUMP_ABSOLUTE 58
>> 80 POP_BLOCK
81 JUMP_ABSOLUTE 8
>> 84 JUMP_ABSOLUTE 8
>> 87 POP_BLOCK
7 >> 88 LOAD_FAST 0 (my_list)
91 LOAD_ATTR 3 (sort)
94 CALL_FUNCTION 0
97 POP_TOP
8 98 LOAD_FAST 0 (my_list)
101 RETURN_VALUE
推荐的方法有更短的字节码:
>>> dis.dis(sorted_set)
2 0 LOAD_GLOBAL 0 (sorted)
3 LOAD_GLOBAL 1 (set)
6 LOAD_FAST 0 (a_list)
9 CALL_FUNCTION 1
12 CALL_FUNCTION 1
15 RETURN_VALUE
所以我们看到使用Python的内置功能比尝试重新发明轮子更加有效和高效。
附录:需要更全面探索的其他选择:
def groupby_sorted(my_list):
"""if items in my_list are unhashable"""
from itertools import groupby
return [e for e, g in groupby(sorted(my_list))]
def preserve_order_encountered(my_list):
"""elements in argument must be hashable - preserves order encountered"""
from collections import OrderedDict
return list(OrderedDict.fromkeys(my_list))
答案 1 :(得分:2)
将这些项目放入一个集合然后排序将是有效的,但它确实依赖于可清洗的项目:
def sorted_set(a_list):
return sorted(set(a_list))
timeit sorted_set(my_list)
100000 loops, best of 3: 3.19 µs per loop
获取排序的唯一元素列表的经典方法是先排序,然后对列表执行第二次传递,消除相同的元素(保证在排序后相邻):
def sorted_unique(a_list):
l = sorted(a_list)
return l[:1] + [b for a, b in zip(l, l[1:]) if a != b]
与使用set
:
timeit sorted_unique(my_list)
100000 loops, best of 3: 6.6 µs per loop
我们实际上可以使用itertools.groupby
:
def sorted_group(a_list):
return [k for k, _ in groupby(sorted(a_list))]
timeit sorted_group(my_list)
100000 loops, best of 3: 5.3 µs per loop
最后,如果这些项目是原始值,那么值得考虑numpy;在这种情况下(在一个小的列表中)开销超过任何好处,但它在较大的问题集上表现良好:
def sorted_np(a_list):
return np.unique(np.sort(a_list))
timeit sorted_np(my_list)
10000 loops, best of 3: 42 µs per loop
my_list = [random.randint(0, 10**6) for _ in range(10**6)]
timeit sorted_set(my_list)
1 loops, best of 3: 454 ms per loop
timeit sorted_np(my_list)
1 loops, best of 3: 333 ms per loop
答案 2 :(得分:1)
这是python中的两个简单函数:
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
print sorted(set(my_list))
你得到你想要的东西;)
如果您想了解有关集合的更多信息,请查看here,有关在python中进行排序的信息,请查看here。
希望这会有所帮助。
答案 3 :(得分:-1)
my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
b=[]
for x in my_list:
try:
z=b.index(x)
except:
b.append(x)
b.sort()
output
['a', 'b', 'c', 'd', 'e', 'f']