Numpy où fonctionnent plusieurs conditions

132

J'ai un tableau de distances appelées dists. Je veux sélectionner des dists qui sont entre deux valeurs. J'ai écrit la ligne de code suivante pour ce faire:

 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 très bien. Pourquoi le code ci-dessus ne fonctionne-t-il pas et comment le faire fonctionner?

À votre santé

user1654183
la source

Réponses:

203

La meilleure façon dans votre cas particulier serait simplement 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 distdans un drou r? (Bien que je redéfinisse rpour ê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'en avez pas vraiment besoin wheresi vous essayez simplement de filtrer les éléments distsqui ne correspondent pas à vos critères:

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

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

Ou, si vous souhaitez utiliser wherepour une raison quelconque, vous pouvez faire:

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

Pourquoi:
La raison pour laquelle cela ne fonctionne pas est que np.whererenvoie une liste d'indices, pas un tableau booléen. Vous essayez de passer andentre deux listes de nombres, qui n'ont bien sûr pas les valeurs True/ Falseque vous attendez. Si aet bsont les deux Truevaleurs, alors a and brenvoie b. Donc, dire quelque chose comme ça [0,1,2] and [2,3,4]va vous donner [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.wherele 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 indexez simplement le tableau d'origine avec le tableau booléen en utilisant une indexation sophistiquée

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

La réponse acceptée expliquait assez bien le problème. Cependant, l'approche la plus numpythonique pour appliquer plusieurs conditions consiste à utiliser des fonctions logiques numpy . Dans cet ase, vous pouvez utiliser np.logical_and:

np.where(np.logical_and(np.greater_equal(dists,r),np.greater_equal(dists,r + dr)))
Kasramvd
la source
11

Une chose intéressante à signaler ici; la manière habituelle d'utiliser OR et AND fonctionnera également dans ce cas, mais avec un petit changement. Au lieu de "et" et au lieu de "ou", utilisez plutôt Esperluette (&) et Pipe Operator (|) et cela fonctionnera.

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 Ampersand (&) :

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 il en va de même dans le cas où nous essayons d'appliquer plusieurs filtres en cas de pandas Dataframe. Maintenant, le raisonnement derrière cela doit faire quelque chose avec les opérateurs logiques et les opérateurs au niveau du bit et pour plus de compréhension à ce sujet, je suggère de passer par cette réponse ou des questions / réponses similaires dans stackoverflow.

METTRE À JOUR

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

Semblable à ce que BODMAS parle, python donne également la priorité à ce qui doit être effectué en premier. Les éléments entre parenthèses sont exécutés en premier, puis l'opérateur au niveau du bit entre en action. Je vais montrer ci-dessous ce qui se passe dans les deux cas lorsque vous utilisez et que vous n'utilisez pas "(", ")".

Cas 1:

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)

Puisqu'il n'y a pas de crochets ici, l'opérateur binaire ( &) devient confus ici que de quoi lui demandez-vous même d'obtenir un ET logique, car dans la table de priorité des opérateurs, si vous voyez, la &priorité sur les opérateurs <ou >. Voici le tableau de la priorité la plus basse à la priorité la plus élevée.

entrez la description de l'image ici

Il est même pas effectuer le <et le >fonctionnement et être invité à effectuer une opération logique ET. C'est pourquoi il donne cette erreur.

On peut consulter le lien suivant pour en savoir plus: priorité des opérateurs

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 True et False. 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 vous le savez, np.where, pour des cas donnés, partout où True, attribue la première valeur (c'est-à-dire ici «yo») et si False, l'autre (c'est-à-dire ici, en gardant l'original).

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

Amit Amola
la source
1
Pourquoi devez-vous mettre ()autour (ar>3)et (ar>6)?
RTrain3k
C'est une très bonne question. C'est une si bonne question que j'ai dû penser moi-même que ce qui diable en est le besoin. C'est ce que j'ai fait, j'ai demandé à un collègue aussi et nous avons discuté et maintenant j'ai une solution pour vous. Le mettre dans la réponse en tant que MISE À JOUR. C'est vraiment simple mais difficile à comprendre vraiment.
Amit Amola le
Consultez la mise à jour RTrain3k, j'ai répondu à votre requête.
Amit Amola le
5

J'aime utiliser np.vectorizepour de telles tâches. Considérer ce qui suit:

>>> # function which returns True when constraints are satisfied.
>>> func = lambda d: d >= r and d<= (r+dr) 
>>>
>>> # Apply constraints element-wise to the dists array.
>>> result = np.vectorize(func)(dists) 
>>>
>>> result = np.where(result) # Get output.

Vous pouvez également utiliser à la np.argwhereplace denp.where pour une sortie claire. Mais c'est ton appel :)

J'espère que ça aide.


la source
2

Essayer:

np.intersect1d(np.where(dists >= r)[0],np.where(dists <= r + dr)[0])
Xin Wang
la source
2

Cela devrait fonctionner:

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

La manière la plus élégante ~~

Qhan
la source
2

Essayer:

import numpy as np
dist = np.array([1,2,3,4,5])
r = 2
dr = 3
np.where(np.logical_and(dist> r, dist<=r+dr))

Sortie: (array ([2, 3]),)

Vous pouvez voir les fonctions logiques pour plus de détails.

Xiong-Hui Chen
la source
0

J'ai élaboré cet exemple simple

import numpy as np

ar = np.array([3,4,5,14,2,4,3,7])

print [X for X in list(ar) if (X >= 3 and X <= 6)]

>>> 
[3, 4, 5, 4, 3]
Kiriloff
la source
6
Il n'est pas nécessaire d'itérer dans ce cas. NumPy a une indexation booléenne.
M456