2013-02-27 10 views
28

Jeśli mam listę python, która ma wiele duplikatów i chcę iterować przez każdy element, ale nie przez duplikaty, najlepiej jest użyć zestawu (jak w set(mylist), lub znaleźć inny sposób, aby utworzyć listę bez duplikaty? Myślałam po prostu zapętlenie poprzez listy i sprawdzanie duplikatów, ale pomyślałem, że to, co set() robi kiedy to zainicjowany.Lepiej/Szybciej przechodzić przez zestaw lub listę?

Więc jeśli mylist = [3,1,5,2,4,4,1,4,2,5,1,3] i naprawdę chcą się pętli [1,2,3,4,5] (kolejność nie ma znaczenia) należy użyć set(mylist) lub coś innego?

Alternatywą jest możliwe w ostatnim przykładzie, ponieważ lista zawiera wszystkie liczby całkowite między jej min i m wartość osi, mogłem przechodzić przez range(min(mylist),max(mylist)) lub przez set(mylist). Czy ogólnie powinienem starać się unikać używania zestawu w tym przypadku? Ponadto, czy znalezienie min i max będzie wolniejsze niż samo tworzenie set?


W przypadku w ostatnim przykładzie, set jest szybsze:

from numpy.random import random_integers 
ids = random_integers(1e3,size=1e6) 

def set_loop(mylist): 
    idlist = [] 
    for id in set(mylist): 
     idlist.append(id) 
    return idlist 

def list_loop(mylist): 
    idlist = [] 
    for id in range(min(mylist),max(mylist)): 
     idlist.append(id) 
    return idlist 

%timeit set_loop(ids) 
#1 loops, best of 3: 232 ms per loop 

%timeit list_loop(ids) 
#1 loops, best of 3: 408 ms per loop 
+0

Dlaczego nie przetestować go? –

+2

@JoelCornett done :) – askewchan

+0

Czy spodziewasz się, że ta różnica prędkości ma znaczenie w każdym programie, jaki kiedykolwiek napisałeś? Przechowywanie rzeczy w 'numpy', używając genexp zamiast budowania listy' milionowej 'elementu tylko po to, aby powtórzyć (i używając 'xrange' zamiast' range' jeśli to Py2), próbując zrobić ciasne pętle w C zamiast Pythona (np. 'idlist = range (...)' zamiast pętli 'for', która robi to samo), itd., wszystkie będą powodowały większą różnicę wielkości rzędów. – abarnert

Odpowiedz

33

Wystarczy użyć set. Jego semantyka jest dokładnie tym, czego potrzebujesz: zbiorem unikalnych przedmiotów.

Pod względem technicznym będziesz dwukrotnie przeglądać listę: raz, aby utworzyć zestaw, raz dla rzeczywistej pętli. Ale będziesz robił tyle samo pracy lub więcej z każdym innym podejściem.

+0

używając generatora i zestawu będzie pętla tylko jeden raz, spójrz na moją odpowiedź Chciałbym twojej opinii. @ Eevee – Cherif

3

Dla uproszczenia: newList = list(set(oldList))

ale są lepsze opcje tam, jeśli chcesz uzyskać prędkość/zamówienie/optymalizacja Zamiast: http://www.peterbe.com/plog/uniqifiers-benchmark

+3

Nie ma powodu, aby powrócić do listy. Już stracił kolejność elementów podczas konwersji do zestawu, więc nie ma powodu, aby nie zostać z zestawem. – ThiefMaster

+0

@ ThiefMaster Istnieją powody, aby chcieć wrócić do listy, głównie wydajności. Listy są znacznie szybsze w przypadku iteracji niż zestawu, a dzięki zachowaniu wewnętrznego atrybutu dla każdego elementu można z łatwością przekonwertować z powrotem na listę i posortować ją w odpowiedniej kolejności. – Flipper

9

set to, co chcesz, więc należy użyć set . Próba bycia sprytnym wprowadza subtelne błędy, takie jak zapomnienie o dodaniu jednego do max(mylist)! Kodeks obronny. Martw się o to, co jest szybsze, gdy stwierdzisz, że jest zbyt wolny.

range(min(mylist), max(mylist) + 1) # <-- don't forget to add 1 
+0

Chciałbym tutaj swoją opinię na moją odpowiedź jest to szybkie, gdy mamy do czynienia z dużą listę. – Cherif

4

Choć set może być to, co chcesz struktura mądry, pytanie brzmi, co jest szybsze. Lista jest szybsza. Twój przykład kod nie dokładnie porównać set vs list bo jesteś konwersji z listy do zestawu wset_loop, a następnie jesteś tworzeniu list będziesz przelotowego wlist_loop. Zbiór i listy iterację powinny być skonstruowane w pamięci z wyprzedzeniem i po prostu przelotowe, aby zobaczyć, jakie dane struktura jest szybszy w iteracji:

ids_list = range(1000000) 
sids_set = set(ids) 
def f(x): 
    for i in x: 
     pass 

%timeit f(ids_set) 
#1 loops, best of 3: 214 ms per loop 
%timeit f(ids_list) 
#1 loops, best of 3: 176 ms per loop 
1

I wykaz znajduje się zmieniać dużej pętli dwa czasu na to będzie Poświęć dużo czasu i więcej, gdy po raz drugi zapętlasz zestaw, a nie listę, a jak wiemy, iteracja po zbiorze jest wolniejsza niż lista.

Myślę, że potrzebujesz mocy: generator i set.

def first_test(): 

    def loop_one_time(my_list): 
     # create a set to keep the items. 
     iterated_items = set() 
     # as we know iterating over list is faster then list. 
     for value in my_list: 
      # as we know checking if element exist in set is very fast not 
      # metter the size of the set. 
      if value not in iterated_items: 
       iterated_items.add(value) # add this item to list 
       yield value 


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

    for v in loop_one_time(mylist):pass 



def second_test(): 
    mylist = [3,1,5,2,4,4,1,4,2,5,1,3] 
    s = set(mylist) 
    for v in s:pass 


import timeit 

print(timeit.timeit('first_test()', setup='from __main__ import first_test', number=10000)) 
print(timeit.timeit('second_test()', setup='from __main__ import second_test', number=10000)) 

out put:

0.024003583388435043 
    0.010424674188938422 

Uwaga: technika ta kolejność jest gwarantowana

Powiązane problemy