11
votes

Structure de données efficace des objets en Python pour la recherche sur la base de toute variable d'élément d'objet

J'ai besoin de stocker des objets comptant un numéro (> 2) des variables d'éléments entier et effectuez des recherches rapides à l'aide de toutes les variables de membre comme clé de recherche.

Pour une illustration plus facile, disons que les objets sont des tuples de 3 entiers et je dois faire des recherches rapides en utilisant n'importe quel élément d'un tuple comme clé dans une liste de tels tuples: p> xxx pré>

look-ups serait comme: p >

collection.lookup_by_first_element(1) # Return (1, 200, 9)
collection.lookup_by_second_element(300) # Return (2, 300, 8)
collection.lookup_by_third_element(250) # Return None


5 commentaires

Cela peut être fait avec n dictionnaires pour des valeurs uniques, dans les multi-cartes pour non unique. Mettez-les dans une classe et donnez aux noms descriptifs de l'évaluateur.


Les valeurs sont toujours uniques? Sinon, vous voulez juste le premier tuple pour l'avoir?


Pour clarifier: les valeurs sont uniques dans toutes les colonnes , ou juste une fois? I.e., si (1,2,3) apparaît, peut-il (3,4,5) apparaît?


@Thénatos: dans ma situation (3,4,5) ne peut pas apparaître, mais maintenant je suis aussi curieux d'une solution qui nous permettait également (3,4,5) apparaissent également.


@kaadam, une solution à base de dictionnaire est susceptible d'être plus efficace que toute solution numpy / tableau. Voir ma réponse mise à jour.


3 Réponses :


10
votes

Ceci est une solution simple. Vous pouvez facilement mettre cela dans une classe et fournir une interface NATATER.

using keyed_dict:  1.09672546387e-05
using numpy_lookup:  0.000250101089478
using keyed_dict:  3.00407409668e-05
using numpy_lookup:  0.00193691253662
using keyed_dict:  0.000190019607544
using numpy_lookup:  0.0199580192566
using keyed_dict:  0.00195384025574
using numpy_lookup:  0.317503929138
using keyed_dict:  0.0319399833679
using numpy_lookup:  15.0127439499


2 commentaires

Ah, je vois que les valeurs sont uniques. Eh bien, utilisez simplement un dict régulier au lieu d'un titre de défaut.


J'ai mis en place votre méthode dans une classe et j'ai essayé d'expliquer pourquoi o (1) Les recherches dict sont plus rapides que la recherche basée sur la matrice (c'est O (n)) dans ma réponse ci-dessous (que vous avez bien démontré par l'analyse comparative avec TIMINIT , mais ne pas dire pourquoi il devrait être beaucoup plus rapide).



-1
votes

Utilisation de NUMPY:

>>> import numpy as np
>>> collection = [(1, 200, 9),
...               (2, 300, 8),
...               (3, 400, 7)]
>>> collection = np.array(collection)
>>> def f(d, c, v):
...     # d: collection, c: column, v: value
...     if np.any(d[:, c]==v): return d[d[:, c]==v]
...     return 'None'
...
>>> f(collection, 0, 1)
array([[  1, 200,   9]])
>>> f(collection, 1, 300)
array([[  2, 300,   8]])
>>> f(collection, 2, 250)
'None'


2 commentaires

Désolé d'être une critique, mais cela est de façon plus lente que d'utiliser une dict. Voir ma réponse mise à jour.


Il y a des façons plus rapides. Voir la réponse de Senderle ci-dessous.



6
votes

Envoyer a raison (j'ai suscité), mais je veux élaborer (et plus que juste dans un commentaire).

Les recherches du dictionnaire sont O (1) et vraiment vite (fondamentalement, votre clé est transformée en hachage qui s'adresse un emplacement spécifique en mémoire pour récupérer immédiatement vos données de).

En contraste à la recherche d'une valeur en recherchant une matrice est lente au moins O (n), donc pour les plus grands tableaux qu'il faudra plus longtemps pour trouver la bonne valeur. (Par exemple, vous devez passer à travers toutes les clés N, trouver le bon, puis retourner le tuple.)

Donc, si vos clés sont uniques comme vous le dites, il est logique de créer un grand Dictionnaire indexé en fonction de chaque touche que vous pouvez utiliser pour rechercher. Accordé est que vous devrez représenter chaque tuple d'articles M (m = 3 dans votre cas), M Times dans le dictionnaire, tandis que avec un seul tableau, il suffit de représenter une fois dans le tableau.

SO. Vous souhaitez définir une classe de collection xxx

le c.collection_dict est: xxx

Et vos recherches fonctionnent comme vous avez demandé xxx


3 commentaires

Y a-t-il une raison pour laquelle vous avez utilisé un dict au lieu de defaultDict comme dans la réponse de l'expéditeur? Il a utilisé Liste comme default_factory pour le defaultDict , la structure résultante est donc différente. Votre dict contient des touches de 2 tuple qui pointent sur des valeurs 3 tuple, où Senderle a des touches 2-Toile qui indiquent que la liste des valeurs contenant chacune une 3 tuple. Je suppose que donne des tuples sont immuables, il n'y a pas de valeur par défaut_constructeur gagné lorsque vous souhaitez des valeurs de tuple dans la dict.


@Davos - OP a réclamé chaque clé est Unique et voulait que chaque recherche renvoie une tuple pas une liste de tuples. Je suis d'accord que cela semble être une configuration étrange, mais cela pourrait avoir des cas d'utilisation. E.G., Si chaque colonne est un identifiant unique qui ne sera jamais répété (par exemple, Employee_id_number, adresse e-mail, SSN). D'accord de manière plus générale si c'est quelque chose comme (nom, naissance_date, home_town), alors vous voudriez qu'une recherche renvoie une liste de tuples correspondants.


Merci, j'ai raté ce commentaire à leur sujet d'être unique, cela l'explique. Quand ils sont uniques, l'autre réponse crée une structure dans laquelle une entrée ressemble à ce (0, 1): [(1, 200, 9)], de sorte que le tuple est de sorte que le tuple est en quelque sorte boxé de manière redondante. Une liste (sur 1), qui me perplexe.