web-dev-qa-db-fra.com

Python: List vs Dict pour une table de correspondance

J'ai environ 10 millions de valeurs que j'ai besoin de mettre dans une sorte de table de consultation, alors je me demandais ce qui serait plus efficace liste ou dict?

Je sais que vous pouvez faire quelque chose comme ça pour les deux:

if something in dict_of_stuff:
    pass

et

if something in list_of_stuff:
    pass

Ma pensée est que le dict sera plus rapide et plus efficace.

Merci de votre aide.

EDIT 1
Un peu plus d’informations sur ce que j’essaie de faire. problème d'Euler 92 . Je fais une table de correspondance pour voir si une valeur calculée a déjà été calculée.

EDIT 2
Efficacité pour la recherche.

EDIT 3
Il n’existe aucune valeur associée à la valeur ... de même un ensemble être meilleur?

154
Nope

La vitesse

Les recherches dans les listes sont O (n), les recherches dans les dictionnaires sont amorties O (1), en ce qui concerne le nombre d'éléments de la structure de données. Si vous n'avez pas besoin d'associer des valeurs, utilisez des ensembles.

Mémoire

Les dictionnaires et les ensembles utilisent le hachage et utilisent beaucoup plus de mémoire que seulement pour le stockage d'objets. Selon A.M. Kuchling in Beautiful Code, l'implémentation tente de garder le hash complet, afin de ne pas gaspiller assez de mémoire.

Si vous n'ajoutez pas de nouvelles entrées à la volée (ce que vous faites en fonction de votre question mise à jour), il pourrait être intéressant de trier la liste et d'utiliser la recherche binaire. C'est O (log n), et est susceptible d'être plus lent pour les chaînes, impossible pour les objets qui n'ont pas un ordre naturel.

202
Torsten Marek

Un dict est une table de hachage, il est donc très rapide de trouver les clés. Donc, entre dict et list, dict serait plus rapide. Mais si vous n'avez pas de valeur à associer, il est même préférable d'utiliser un ensemble. C'est une table de hachage, sans la partie "table".


EDIT: pour votre nouvelle question, OUI, un ensemble serait mieux. Il suffit de créer 2 ensembles, un pour les séquences terminées en 1 et un autre pour les séquences terminées en 89. J'ai résolu ce problème avec succès en utilisant des ensembles.

40
nosklo

set() est exactement ce que vous voulez. O(1) recherches, et plus petit qu'un dict.

31
recursive

J'ai fait quelques tests d'évaluation et il s'est avéré que dict est plus rapide que les deux listes et défini pour les grands ensembles de données, en cours d'exécution python 2.7.3 sur un processeur i7 sous Linux:

  • python -mtimeit -s 'd=range(10**7)' '5*10**6 in d'

    10 boucles, le meilleur de 3: 64,2 ms par boucle

  • python -mtimeit -s 'd=dict.fromkeys(range(10**7))' '5*10**6 in d'

    10000000 boucles, meilleur de 3: 0,0759 usec par boucle

  • python -mtimeit -s 'from sets import Set; d=Set(range(10**7))' '5*10**6 in d'

    1000000 boucles, meilleur de 3: 0.262 usec par boucle

Comme vous pouvez le constater, dict est considérablement plus rapide que la liste et environ 3 fois plus rapide que celui défini. Dans certaines applications, vous voudrez peut-être quand même choisir un ensemble pour la beauté de celui-ci. Et si les ensembles de données sont vraiment petits (<1000 éléments), les listes fonctionnent assez bien.

27
EriF89

si les données sont uniques, set () sera le plus efficace, mais de deux - dict (ce qui requiert également unicité, oops :)

6
SilentGhost

Tu veux un dict.

Pour les listes (non triées) en Python, l'opération "in" nécessite O(n) time --- pas utile si vous avez une grande quantité de données. Un dict, d'autre part, est une table de hachage, vous pouvez donc vous attendre O(1) temps de recherche.

Comme d'autres l'ont noté, vous pouvez choisir un ensemble (un type spécial de dict) à la place, si vous n'avez que des clés plutôt que des paires clé/valeur.

Apparenté, relié, connexe:

  • wiki Python : informations sur la complexité temporelle des opérations de conteneur Python.
  • SO : Python complexité du temps et de la mémoire du conteneur
6
zweiterlinde

En tant que nouvelle série de tests pour montrer que @ EriF89 a toujours raison après toutes ces années:

$ python -m timeit -s "l={k:k for k in xrange(5000)}"    "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.84 msec per loop
$ python -m timeit -s "l=[k for k in xrange(5000)]"    "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 573 msec per loop
$ python -m timeit -s "l=Tuple([k for k in xrange(5000)])"    "[i for i in xrange(10000) if i in l]"
10 loops, best of 3: 587 msec per loop
$ python -m timeit -s "l=set([k for k in xrange(5000)])"    "[i for i in xrange(10000) if i in l]"
1000 loops, best of 3: 1.88 msec per loop

Ici, nous comparons également un Tuple, réputé plus rapide que lists (et utilisant moins de mémoire) dans certains cas d'utilisation. Dans le cas d'une table de correspondance, le Tuple ne s'est pas mieux comporté.

dict et set se sont très bien comportés. Cela soulève un point intéressant qui rejoint la réponse de @SilentGhost à propos de l'unicité: si le PO a 10 millions de valeurs dans un ensemble de données et si on ne sait pas s'il y a des doublons, il serait intéressant de garder un ensemble/dict de ses éléments en parallèle avec le jeu de données réel et tester son existence dans cet ensemble/dict. Il est possible que les 10 millions de points de données ne possèdent que 10 valeurs uniques, ce qui représente un espace beaucoup plus petit pour la recherche!

L’erreur de SilentGhost à propos de dict est éclairante car on pourrait utiliser un dict pour corréler des données dupliquées (en valeurs) à un ensemble non dupliqué (clés), et ainsi conserver un objet de données pour contenir toutes les données, tout en restant rapide comme table de recherche. Par exemple, une clé dict peut être la valeur recherchée et la valeur peut être une liste d’index dans une liste imaginaire où cette valeur s’est produite.

Par exemple, si la liste de données source à rechercher était l=[1,2,3,1,2,1,4], il pourrait être optimisé pour la recherche et la mémoire en le remplaçant par ce dict:

>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> l=[1,2,3,1,2,1,4]
>>> for i, e in enumerate(l):
...     d[e].append(i)
>>> d
defaultdict(<class 'list'>, {1: [0, 3, 5], 2: [1, 4], 3: [2], 4: [6]})

Avec ce dict, on peut savoir:

  1. If une valeur était dans le jeu de données d'origine (ie 2 in d retourne True)
  2. la valeur était dans le jeu de données d'origine (ie d[2] Retourne la liste des index où des données ont été trouvées dans la liste de données originale: [1, 4])
2
hamx0r

Vous n'avez pas besoin de stocker 10 millions de valeurs dans le tableau, ce n'est donc pas grave.

Astuce: songez à l'ampleur de votre résultat après la première somme d'opérations sur les carrés. Le plus gros résultat possible sera bien inférieur à 10 millions ...

0
Kiv