web-dev-qa-db-fra.com

Numpy: Pour chaque élément d'un tableau, recherchez l'index dans un autre tableau.

J'ai deux tableaux 1D, x & y, l'un plus petit que l'autre. J'essaie de trouver l'index de chaque élément de y dans x.

J'ai trouvé deux moyens naïfs de procéder, le premier est lent et le second, gourmand en mémoire.

Le chemin lent

indices= []
for iy in y:
    indices += np.where(x==iy)[0][0]

Le cochon de la mémoire

xe = np.outer([1,]*len(x), y)
ye = np.outer(x, [1,]*len(y))
junk, indices = np.where(np.equal(xe, ye))

Existe-t-il une méthode plus rapide ou moins gourmande en mémoire? Idéalement, la recherche tirerait parti du fait que nous ne cherchons pas une chose dans une liste, mais beaucoup de choses, et que nous sommes donc légèrement plus enclins à la parallélisation . Des points en prime si vous ne supposez pas que est en fait dans x. 

31
Chris

Comme Joe Kington l'a dit, searchsorted () peut rechercher un élément très rapidement. Pour traiter les éléments qui ne sont pas dans x, vous pouvez vérifier le résultat recherché avec y et créer un tableau masqué:

import numpy as np
x = np.array([3,5,7,1,9,8,6,6])
y = np.array([2,1,5,10,100,6])

index = np.argsort(x)
sorted_x = x[index]
sorted_index = np.searchsorted(sorted_x, y)

yindex = np.take(index, sorted_index, mode="clip")
mask = x[yindex] != y

result = np.ma.array(yindex, mask=mask)
print result

le résultat est:

[-- 3 1 -- -- 6]
20
HYRY

Je veux suggérer une solution en une ligne:

indices = np.where(np.in1d(x, y))[0]

Le résultat est un tableau avec des indices pour x tableau qui correspond aux éléments de y trouvés dans x.

On peut l'utiliser sans numpy.where si besoin est.

23
RomanS

Que dis-tu de ça? 

Il suppose que chaque élément de y est dans x (et retournera les résultats même pour les éléments qui ne le sont pas!), Mais c'est beaucoup plus rapide.

import numpy as np

# Generate some example data...
x = np.arange(1000)
np.random.shuffle(x)
y = np.arange(100)

# Actually preform the operation...
xsorted = np.argsort(x)
ypos = np.searchsorted(x[xsorted], y)
indices = xsorted[ypos]
16
Joe Kington

Je voudrais juste faire ceci:

indices = np.where(y[:, None] == x[None, :])[1]

Contrairement à votre mémoire, cette méthode utilise broadcast pour générer directement un tableau booléen 2D sans créer de tableaux 2D pour x et y.

3
Jun Saito

Le paquet numpy_indexed package (disclaimer: je suis son auteur) contient une fonction qui remplit exactement cette fonction:

import numpy_indexed as npi
indices = npi.indices(x, y, missing='mask')

Il soulève actuellement une KeyError si tous les éléments de y ne sont pas présents dans x; mais peut-être devrais-je ajouter un kwarg pour que l'on puisse choisir de marquer de tels objets avec un -1 ou quelque chose comme ça.

Il devrait avoir la même efficacité que la réponse actuellement acceptée, puisque la mise en œuvre va dans le même sens. numpy_indexed est toutefois plus flexible et permet également de rechercher des index de rangées de tableaux multidimensionnels, par exemple.

EDIT: ive a modifié le traitement des valeurs manquantes. le kwarg "manquant" peut maintenant être défini avec "augmenter", "ignorer" ou "masque". Dans ce dernier cas, vous obtenez un tableau masqué de la même longueur de y, sur lequel vous pouvez appeler .compressed () pour obtenir les index valides. Notez qu'il existe également npi.contains (x, y) si c'est tout ce que vous devez savoir. 

3
Eelco Hoogendoorn

Je pense que ceci est une version plus claire:

np.where(y.reshape(y.size, 1) == x)[1]

que indices = np.where(y[:, None] == x[None, :])[1]. Vous n'avez pas besoin de diffuser x en 2D. 

Ce type de solution m'a semblé préférable car, contrairement aux solutions basées sur searchsorted () ou in1d () qui ont été publiées ici ou ailleurs, les solutions ci-dessus fonctionnent avec des doublons et il est indifférent que tout soit trié. C'était important pour moi parce que je voulais que x soit dans un ordre personnalisé. 

1
hermidalc

Une solution plus directe qui n'attend pas le tableau à trier. 

import pandas as pd
A = pd.Series(['amsterdam', 'delhi', 'chromepet', 'tokyo', 'others'])
B = pd.Series(['chromepet', 'tokyo', 'tokyo', 'delhi', 'others'])

# Find index position of B's items in A
B.map(lambda x: np.where(A==x)[0][0]).tolist()

Le résultat est:

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