227 votes

Fonction Numpy where à conditions multiples

J'ai un tableau de distances appelé dists . Je veux sélectionner dists qui se situent dans une fourchette.

 dists[(np.where(dists >= r)) and (np.where(dists <= r + dr))]

Cependant, cela ne sélectionne que pour la condition

 (np.where(dists <= r + dr))

Si je fais les commandes séquentiellement en utilisant une variable temporaire, cela fonctionne bien. Pourquoi le code ci-dessus ne fonctionne-t-il pas, et comment puis-je le faire fonctionner ?

307voto

askewchan Points 12215

Le meilleur moyen de votre cas particulier serait juste de changer vos deux critères en un seul critère :

dists[abs(dists - r - dr/2.) <= dr/2.]

Il ne crée qu'un seul tableau booléen et, à mon avis, il est plus facile à lire car il dit, est dist dans un dr o r ? (Bien que je redéfinisse r pour être le centre de votre région d'intérêt au lieu du début, donc r = r + dr/2. ) Mais cela ne répond pas à votre question.


La réponse à votre question :
Vous n'avez pas vraiment besoin where si vous essayez juste de filtrer les éléments de dists qui ne correspondent pas à vos critères :

dists[(dists >= r) & (dists <= r+dr)]

Parce que le & vous donnera un élément and (les parenthèses sont nécessaires).

Ou, si vous voulez utiliser where pour une raison quelconque, vous pouvez le faire :

 dists[(np.where((dists >= r) & (dists <= r + dr)))]

Pourquoi ?
La raison pour laquelle ça ne marche pas est que np.where renvoie une liste d'indices, et non un tableau de booléens. Vous essayez d'obtenir and entre deux listes de nombres, ce qui bien sûr n'a pas l'avantage True / False les valeurs que vous attendez. Si a y b sont tous deux True valeurs, alors a and b renvoie à b . Donc dire quelque chose comme [0,1,2] and [2,3,4] vous donnera juste [2,3,4] . Le voici en action :

In [230]: dists = np.arange(0,10,.5)
In [231]: r = 5
In [232]: dr = 1

In [233]: np.where(dists >= r)
Out[233]: (array([10, 11, 12, 13, 14, 15, 16, 17, 18, 19]),)

In [234]: np.where(dists <= r+dr)
Out[234]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

In [235]: np.where(dists >= r) and np.where(dists <= r+dr)
Out[235]: (array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12]),)

Ce que vous vous attendiez à comparer était simplement le tableau booléen, par exemple

In [236]: dists >= r
Out[236]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True], dtype=bool)

In [237]: dists <= r + dr
Out[237]: 
array([ True,  True,  True,  True,  True,  True,  True,  True,  True,
        True,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

In [238]: (dists >= r) & (dists <= r + dr)
Out[238]: 
array([False, False, False, False, False, False, False, False, False,
       False,  True,  True,  True, False, False, False, False, False,
       False, False], dtype=bool)

Vous pouvez maintenant appeler np.where sur le tableau booléen combiné :

In [239]: np.where((dists >= r) & (dists <= r + dr))
Out[239]: (array([10, 11, 12]),)

In [240]: dists[np.where((dists >= r) & (dists <= r + dr))]
Out[240]: array([ 5. ,  5.5,  6. ])

Ou simplement indexer le tableau original avec le tableau de booléens en utilisant indexation fantaisiste

In [241]: dists[(dists >= r) & (dists <= r + dr)]
Out[241]: array([ 5. ,  5.5,  6. ])

95voto

Kasramvd Points 32864

La réponse acceptée expliquait suffisamment bien le problème. Cependant, l'approche la plus Numpythonique pour appliquer des conditions multiples est d'utiliser fonctions logiques numpy . Dans ce cas, vous pouvez utiliser np.logical_and :

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))

48voto

Amit Amola Points 1415

Une chose intéressante à signaler ici : la façon habituelle d'utiliser les OU y ET aussi fonctionnera dans ce cas, mais avec une petite modification. Au lieu de "and" et au lieu de "or", utilisez plutôt Esperluette (&) y Opérateur de tuyauterie (|) et ça marchera.

Lorsque nous utilisons "et :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) and (ar<6), 'yo', ar)

Output:
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Lorsque nous utilisons Esperluette (&) :

ar = np.array([3,4,5,14,2,4,3,7])
np.where((ar>3) & (ar<6), 'yo', ar)

Output:
array(['3', 'yo', 'yo', '14', '2', 'yo', '3', '7'], dtype='<U11')

Et c'est la même chose lorsque nous essayons d'appliquer des filtres multiples dans le cas d'un Dataframe pandas. Le raisonnement derrière tout cela a à voir avec les opérateurs logiques et les opérateurs binaires. Pour mieux comprendre, je vous suggère de lire ceci réponse ou une Q/A similaire dans stackoverflow.

UPDATE

Un utilisateur a demandé pourquoi il est nécessaire de donner (ar>3) et (ar<6) à l'intérieur des parenthèses. Eh bien, voici ce qui se passe. Avant de commencer à parler de ce qui se passe ici, il faut connaître la précédence des opérateurs en Python.

À l'instar du BODMAS, python donne également la priorité à ce qui doit être exécuté en premier. Les éléments situés à l'intérieur des parenthèses sont exécutés en premier, puis l'opérateur bitwise entre en action. Je vais montrer ci-dessous ce qui se passe dans les deux cas lorsque vous utilisez et n'utilisez pas "(", ")".

Cas1 :

np.where( ar>3 & ar<6, 'yo', ar)
np.where( np.array([3,4,5,14,2,4,3,7])>3 & np.array([3,4,5,14,2,4,3,7])<6, 'yo', ar)

Comme il n'y a pas de parenthèses ici, l'opérateur binaire ( & ) s'embrouille ici : que lui demandez-vous de faire un ET logique, parce que dans le tableau de précédence des opérateurs, si vous voyez, & a la priorité sur < o > opérateurs. Voici le tableau, de la plus faible à la plus forte préséance.

enter image description here

Il ne fait même pas le < y > et on lui demande d'effectuer une opération logique ET. C'est pour ça que ça donne cette erreur.

On peut consulter le lien suivant pour en savoir plus : opérateur préséance

Passons maintenant au cas 2 :

Si vous utilisez le support, vous voyez clairement ce qui se passe.

np.where( (ar>3) & (ar<6), 'yo', ar)
np.where( (array([False,  True,  True,  True, False,  True, False,  True])) & (array([ True,  True,  True, False,  True,  True,  True, False])), 'yo', ar)

Deux tableaux de Vrai et Faux. Et vous pouvez facilement effectuer une opération logique ET sur eux. Ce qui vous donne :

np.where( array([False,  True,  True, False, False,  True, False, False]),  'yo', ar)

Et le reste vous le savez, np.where, pour des cas donnés, partout où c'est vrai, assigne la première valeur (i.e. ici 'yo') et si c'est faux, l'autre (i.e. ici, en gardant l'original).

C'est tout. J'espère avoir bien expliqué la requête.

10voto

neuronet Points 316

Pour obtenir np.where() pour travailler avec des conditions multiples, il suffit de faire ce qui suit :

np.where((condition 1) & (condition 2)) # for and
np.where((condition 1) | (condition 2)) # for or

Je sais que cela répète d'autres réponses, mais j'ai mis cette réponse simple ici pour les personnes qui se demandent encore "Pourquoi est-ce que je reçois ce message d'erreur ennuyeux sur The truth value of an array with more than one element is ambiguous "qui sont déroutés par les réponses très verbeuses et complexes qui répondent à la nature quelque peu spécialisée du message original.

Maintenant, en ce qui concerne pourquoi numpy se brise lorsque vous utilisez and au lieu de & Je n'essaierai pas de répondre à cette question ici. Il suffit de voir d'autres réponses ici pour l'explication. Il semble que c'est quelque chose qu'ils devraient simplement corriger au lieu de le forcer pour la cohérence IMHO. Ou au moins, ils devraient créer un message d'erreur spécial :)

5voto

Qhan Points 111

Cela devrait fonctionner :

dists[((dists >= r) & (dists <= r+dr))]

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X