簡體   English   中英

如何從列表中刪除相同的項目並在Python中對其進行排序?

[英]How do I remove identical items from a list and sort it in Python?

如何從列表中最佳地刪除相同的項目並在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小組提出了這個問題,很少有人有這樣的問題。很好的答案, 所以我正在回答它的問答風格,正如Stackoverflow所建議的那樣

從列表中刪除冗余元素的最佳方法是將其轉換為集合,並且由於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))

將項目放入集合然后排序將是有效的,但它依賴於可清洗的項目:

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

它是python中的兩個簡單函數:

my_list = ['a', 'a', 'b', 'c', 'd', 'a', 'e', 'd', 'f', 'e']
print sorted(set(my_list))

你得到你想要的東西;)

如果你想了解有關集合的更多信息,請查看此處 ,以及有關在python中進行排序的信息

希望這可以幫助。

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

暫無
暫無

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

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