2010-10-21 21 views
6

J'essaye de créer un 'masque' d'un.numpy.array en spécifiant certains critères. Python a même belle syntaxe pour quelque chose comme ceci:Question d'indexation de Numpy.array

>> A = numpy.array([1,2,3,4,5]) 
>> A > 3 
array([False, False, False, True, True]) 

Mais si j'ai une liste de critères à la place d'une gamme:

>> A = numpy.array([1,2,3,4,5]) 
>> crit = [1,3,5] 

Je ne peux pas le faire:

>> A in crit 

Je dois faire quelque chose basé sur les compréhensions de liste, comme ceci:

>> [a in crit for a in A] 
array([True, False, True, False, True]) 

Ce qui est correct.

Maintenant, le problème est que je travaille avec de grands tableaux et que le code ci-dessus est très lent. Y a-t-il une façon plus naturelle de faire cette opération qui pourrait l'accélérer?

EDIT: J'ai été capable d'obtenir une petite accélération en transformant crit en set.

EDIT2: Pour ceux qui sont intéressés:

approche Jouni: 1000 boucles, le meilleur de 3: 102 ms par boucle

numpy.in1d: 1000 boucles, plus de 3 : 1,33 ms par boucle

Edit3: Just testé à nouveau avec B = randint (10, taille = 100)

approche de Jouni: 1000 boucles, le meilleur de 3: 2,96 ms par boucle

numpy.in1d: 1000 boucles, le meilleur de 3: 1,34 ms par boucle

Conclusion: Utilisez numpy. in1d() sauf si B est très petit.

Répondre

6

Je pense que la fonction numpy in1d est ce que vous cherchez:

>>> A = numpy.array([1,2,3,4,5]) 
>>> B = [1,3,5] 
>>> numpy.in1d(A,crit) 
array([ True, False, True, False, True], dtype=bool) 

comme indiqué dans son docstring, "in1d(a, b) est à peu près équivalent à np.array([item in b for item in a])"

Certes, je n'ai pas fait de tests de vitesse, mais cela ressemble à ce que vous cherchez.

Une autre façon plus rapide

Voici une autre façon de le faire qui est plus rapide. Trier le tableau B premier (contenant les éléments que vous cherchez à trouver en A), en faire un tableau numpy, puis faire:

B[B.searchsorted(A)] == A 

mais si vous avez des éléments dans A qui sont plus grands que le plus grand B, vous devez faire:

inds = B.searchsorted(A) 
inds[inds == len(B)] = 0 
mask = B[inds] == A 

il ne peut pas être plus rapide pour les petits réseaux (en particulier pour B étant petit), mais avant longtemps, il sera certainement plus rapide. Pourquoi? Comme il s'agit d'un algorithme O (N log M), où N est le nombre d'éléments dans A et M le nombre d'éléments dans M, l'assemblage d'un groupe de masques individuels est O (N * M). Je l'ai testé avec N = 10000 et M = 14 et c'était déjà plus rapide. Quoi qu'il en soit, je pensais que vous aimeriez savoir, surtout si vous envisagez vraiment de l'utiliser sur de très grands tableaux.

+0

ressemble à une addition récente à numpy (n'était pas dans la version 1.3) – Paul

+0

Vous avez raison. J'ai seulement testé sur B ayant une longueur de 3. Si B est aussi grand, numpy.in1d ​​() va certainement beaucoup mieux. – aduric

+1

@aduric et ma seconde méthode est encore plus rapide que in1d. –

0

Créez un masque et utilisez la fonction de compression du tableau numpy. Cela devrait être beaucoup plus rapide. Si vous avez un critère complexe, n'oubliez pas de le construire en fonction des maths des tableaux.

a = numpy.array([3,1,2,4,5]) 
mask = a > 3 
b = a.compress(mask) 

ou

a = numpy.random.random_integers(1,5,100000) 
c=a.compress((a<=4)*(a>=2)) ## numbers between n<=4 and n>=2 
d=a.compress(~((a<=4)*(a>=2))) ## numbers either n>4 or n<2 

Ok, si vous voulez un masque qui a tout un dans [1,3,5] vous pouvez faire quelque chose comme

a = numpy.random.random_integers(1,5,100000) 
mask=(a==1)+(a==3)+(a==5) 

ou

a = numpy.random.random_integers(1,5,100000) 
mask = numpy.zeros(len(a), dtype=bool) 
for num in [1,3,5]: 
    mask += (a==num) 
+0

Je ne pense pas que ce soit ce que je cherche. Je ne veux pas récupérer le contenu réel du tableau, je veux juste obtenir un masque booléen qui a la même longueur que le tableau original. – aduric

+0

Ok, édité maintenant que je sais ce que vous voulez. Je suppose que la solution de Jouni qu'il a trouvée pendant que j'éditais la mienne était équivalente, comme Vrai = Vrai + Vrai, Vrai = Vrai + Faux, Faux = Faux + Faux, exactement la même chose que ou en utilisant |. –

3

Combiner plusieurs comparaisons avec "ou":

A = randint(10,size=10000) 
mask = (A == 1) | (A == 3) | (A == 5) 

Ou si vous avez une liste B et que vous voulez créer le masque dynamique:

B = [1, 3, 5] 
mask = zeros((10000,),dtype=bool) 
for t in B: mask = mask | (A == t) 
+0

Je me demandais juste pourquoi ou comment anticiper quand 'numpy' va naturellement faire cette opération logique par élément' ufunc' activée? Lorsque vous effectuez des opérations logiques, 'numpy' renvoie parfois une exception:' ValueError: La valeur de vérité d'un tableau contenant plus d'un élément est ambiguë. Utilisez a.any() ou a.all(). ' – dtlussier

+0

c'est certainement l'approche la plus rapide, mais pas la plus propre. – aduric