web-dev-qa-db-fra.com

Mieux/Plus rapide à parcourir ensemble ou liste?

Si j'ai une liste python qui contient de nombreux doublons et que je souhaite parcourir chaque élément, mais pas les doublons, est-il préférable d'utiliser un ensemble (comme dans set(mylist) ou de trouver un autre moyen de créer une liste sans doublons? Je pensais simplement parcourir la liste et vérifier les doublons, mais j’ai pensé que c’est ce que set() fait quand il est initialisé.

Donc, si mylist = [3,1,5,2,4,4,1,4,2,5,1,3] et que je veux vraiment juste parcourir en boucle [1,2,3,4,5] (l'ordre n'a pas d'importance), devrais-je utiliser set(mylist) ou autre chose?

Une alternative est possible dans le dernier exemple, puisque la liste contient chaque entier compris entre ses valeurs min et max, je pourrais parcourir en boucle range(min(mylist),max(mylist)) ou set(mylist). Devrais-je généralement essayer d'éviter d'utiliser set dans ce cas? De plus, trouver min et max serait-il plus lent que de simplement créer set?


Dans le cas du dernier exemple, la set est plus rapide:

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
33
askewchan

Utilisez simplement une set. Sa sémantique correspond exactement à ce que vous souhaitez: une collection d’articles uniques.

Techniquement, vous parcourez la liste deux fois: une fois pour créer le jeu, une fois pour votre boucle réelle. Mais vous feriez autant de travail ou plus avec une autre approche.

37
Eevee

set est ce que vous voulez, vous devriez donc utiliser set. Essayer d'être intelligent introduit des bugs subtils comme l'oubli d'ajouter un àmax(mylist)! Code défensivement. Inquiétez-vous de ce qui est plus rapide quand vous déterminez que c'est trop lent.

range(min(mylist), max(mylist) + 1)  # <-- don't forget to add 1
9
John La Rooy

Par souci de simplicité: newList = list(set(oldList))

Mais il existe de meilleures options si vous souhaitez obtenir plutôt rapidité/commande/optimisation: http://www.peterbe.com/plog/uniqifiers-benchmark

5
GordonsBeard

Bien que set puisse être ce que vous voulez au niveau de la structure, la question est de savoir ce qui est plus rapide. Une liste est plus rapide. Votre exemple de code ne compare pas exactement set avec list car vous convertissez une liste en un ensemblein set_loop et vous créez ensuite la list que vous allez parcourir en boucle in list_loop. L'ensemble et la liste que vous parcourez doivent être construits et en mémoire à l'avance, et simplement parcourus pour voir quelle structure de données itérera plus rapidement:

ids_list = range(1000000)
ids_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
5
hamx0r

Si la liste est longue et longue, cela prend beaucoup de temps et plus lors de la deuxième boucle. Vous bouclez un ensemble et non une liste et, comme nous le savons, itérer sur un ensemble est plus lent que la liste.

je pense que vous avez besoin du pouvoir de generator et 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 mis: 

   0.024003583388435043
   0.010424674188938422

Note: cette technique est garantie

1
EasyOdoo