4
votes

Comment obtenir un mappage inversé dans numpy dans O (1)?

J'ai un tableau numpy, dont les éléments sont uniques, par exemple:

b = np.array ([5, 4, 6, 8, 1, 2]) p >

(Edit2: b peut avoir de grands nombres, et des nombres flottants. L'exemple ci-dessus est là pour simplifier) ​​

J'obtiens des nombres, qui sont des éléments de b.

Je veux trouver leur index dans b , ce qui signifie Je veux un mappage inversé, de la valeur à l'index, dans b strong >.

Je pourrais faire

mapping = np.in1d(b, b).nonzero()[0]

>> [0, 1, 2, 3, 4]

qui itérerait sur tout le tableau à chaque appel à where .

Je pourrais aussi créer un dictionnaire,

b = np.array([1, 2, 3, 10, 4])

Je pourrais créer ce dictionnaire au moment du "prétraitement", mais je me retrouverais quand même avec un dictionnaire étrange, dans un principalement du code numpy, ce qui ne me semble pas (à mon avis) comment numpy est censé être utilisé.

Comment puis-je faire ce reverse mapping dans numpy?

utilisation (O (1) temps et mémoire requis):

print("index of 8 is: ", foo(b, 8))

  • Edit1: pas une copie de ceci

Utiliser in1d comme expliqué ici ne résout pas mon problème. En utilisant leur exemple:

d = {}
for i, element in enumerate(list(b)):
    d[element] = i

Je veux pouvoir trouver par exemple l'index de 10 en b, à l'exécution, en O (1) .

Faire un déplacement de prétraitement

for number in input:
    ind = np.where(number==b)

(qui pourrait être accompli en utilisant np.arange (len (b)) code >)

n'aide pas vraiment, car lorsque 10 entre en entrée, il n'est pas possible de dire son index en temps O (1) avec cette méthode. p>


3 commentaires

Est-ce un tableau 1D? Et les doublons? Etc


peut-être un bon candidat pour le code golf ?


@uhoh J'appuie la suggestion de golf, mais l'OP doit d'abord définir la structure de données dans laquelle il souhaite réellement stocker la table de recherche


4 Réponses :


0
votes

Si vous souhaitez effectuer plusieurs recherches, vous pouvez les faire dans O (1) après une première traversée O (n) pour créer un dictionnaire de recherche.

>>> print('index of 8 is:', foo(8))
index of 8 is:  3

Et cela fonctionne pour votre test:

b = np.array([5, 4, 6, 8, 1, 2])
lookup_dict = {e:i for i,e in enumerate(b)}
def foo(element):
    return lookup_dict[element]

Notez que s'il y a une possibilité que b peut avoir changé depuis le dernier appel de foo () , nous devons recréer le dictionnaire.


2 commentaires

le tableau ne change jamais. Quant à la réponse, n'avez-vous pas fait exactement ce que j'ai écrit dans ma question?


@Gulzar Mon mauvais, je n'ai pas complètement lu la question! Après un examen plus approfondi, je soutiens cela comme le meilleur moyen de créer une cartographie inversée. Ceci ne peut pas être réalisé avec numpy car le module ne prend pas en charge les structures de type "lookup-like"; seulement des tableaux. Par conséquent, le seul moyen d'y parvenir dans numpy serait de créer un tableau massif où l'index de chaque élément est la valeur de chaque élément dans b et la valeur de chaque élément est l'index de l'index de cette valeur dans b . Cela utiliserait beaucoup de mémoire (car les éléments seraient 0 ) et ne prendrait pas non plus en compte les négatifs ou les flottants.



1
votes

Vous pouvez utiliser dict , zip et numpy.arrange pour créer votre recherche inversée:

{5: 0, 4: 1, 6: 2, 8: 3, 1: 4, 2: 5}

donne:

import numpy 

b = np.array([5, 4, 6, 8, 1, 2])
d = dict(zip(b, np.arange(0,len(b))))
print(d)


0 commentaires

2
votes

C'est plus simple que vous ne le pensez, en exploitant l'indexation avancée de numpy.

Ce que nous faisons est de créer notre tableau cible et d'attribuer simplement usign b comme index. Nous attribuerons les indices que nous voulons en utilisant arange.

h = np.int32(b * 100.0) % 101  # Typically some prime number
t = np.zeros((101,))
t[h] = np.arange(0, h.size)

# Retrieving a value v; keep in mind v can be an ndarray itself.
t[np.int32(v * 100.0) % 101]

Vous pouvez utiliser nan s ou -1 au lieu de zéros pour construire la cible vers aide à détecter les recherches non valides.

Utilisation de la mémoire : elle offre des performances optimales dans l'espace et dans le temps car elle est entièrement gérée par numpy.

Si vous pouvez tolérer les collisions , vous pouvez implémenter la table de hachage d'un pauvre homme. Supposons que nous ayons des devises, par exemple:

>>> t = np.zeros((np.max(b) + 1,))
>>> t[b] = np.arange(0, b.size)
>>> t
array([0., 4., 5., 0., 1., 0., 2., 0., 3.])

Vous pouvez effectuer d'autres étapes pour modifier l'adresse si vous savez à quoi ressemble votre ensemble de données.

Ceci est à peu près la limite de ce qui est utile à faire avec numpy.


3 commentaires

et si b a un élément dont la valeur est 99999.4 ?


@Gulzar Voir mon commentaire sur ma réponse. Je pense que ce n'est tout simplement pas pratique à réaliser avec numpy.


Je passe par la question, qui indique des nombres entiers et un tableau compact. Si vous avez besoin d'une cartographie à usage général, utilisez un dictionnaire, mais ce n'était pas ce que la question posait. Si vous essayez de rechercher des flottants, vous voudrez les normaliser afin de gérer les erreurs d'arrondi.



2
votes

Solution

Si vous voulez un temps constant (c'est-à-dire O (1) ), vous devrez précalculer une table de recherche d'une sorte. Si vous voulez créer votre table de recherche en utilisant un autre tableau Numpy, il faudra en fait un tableau clairsemé, dans lequel la plupart des valeurs sont «vides». Voici une approche pratique dans laquelle les valeurs vides sont marquées comme -1:

ix = {k:v for v,k in enumerate(b.flat)}

Test:

index of 8 is: [3]
index of 0,5,1,8 is: [-1  0  4  3]

Output:

print("index of 8 is: %s" % foo(8))
print("index of 0,5,1,8 is: %s" % foo(0,5,1,8))

Caveat

Dans le code de production, vous devez absolument utiliser un dictionnaire pour résoudre ce problème, comme d'autres répondants l'ont souligné. Pourquoi? Eh bien, pour une chose, disons que votre tableau b contient des valeurs float , ou toute valeur non- int . Ensuite, une table de recherche basée sur Numpy ne fonctionnera pas du tout.

Par conséquent, vous ne devriez utiliser la réponse ci-dessus que si vous avez une opposition philosophique profonde à l'utilisation d'un dictionnaire (par exemple, un dict a écrasé votre chat). Voici une bonne façon de générer un dict de recherche inversée:

b = np.array([5, 4, 6, 8, 1, 2])

_b_ix = np.array([-1]*(b.max() + 1))
_b_ix[b] = np.arange(b.size)
# _b_ix: array([-1,  4,  5, -1,  1,  0,  2, -1,  3])

def foo(*val):
    return _b_ix[list(val)]

1 commentaires

Dans le cas où b contient des entiers, y a-t-il une différence de temps CPU entre la solution entièrement numpy et celle du dictionnaire?