Comment sélectionner des éléments d'un tableau dans une condition donnée?

156

Supposons que j'ai un tableau numpy x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Je souhaite sélectionner les éléments ycorrespondant aux éléments xsupérieurs à 1 et inférieurs à 5.

j'ai essayé

x = array([5, 2, 3, 1, 4, 5])
y = array(['f','o','o','b','a','r'])
output = y[x > 1 & x < 5] # desired output is ['o','o','a']

mais cela ne fonctionne pas. Comment ferais-je cela?

Bob
la source

Réponses:

220

Votre expression fonctionne si vous ajoutez des parenthèses:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
jfs
la source
1
C'est bien .. vecMask = 1 <x génère un masque vectoriel comme vecMask = (False, True, ...), qui peut être simplement combiné avec d'autres masques vectoriels. Chaque élément est la condition pour prendre les éléments d'un vecteur source (True) ou non (False). Cela peut également être utilisé avec la version complète numpy.extract (vecMask, vecSrc) ou numpy.where (vecMask, vecSrc, vecSrc2).
MasterControlProgram
6
@JennyYueJin: Cela arrive à cause de la préséance. (Bitwise) &a une priorité plus élevée que <et >, qui à son tour ont une priorité plus élevée que (logique) and. x > 1 and x < 5évite d'abord les inégalités, puis la conjonction logique; x > 1 & x < 5évalue la conjonction au niveau du bit de 1et (les valeurs dans) x, puis les inégalités. (x > 1) & (x < 5)force les inégalités à évaluer en premier, de sorte que toutes les opérations se produisent dans l'ordre prévu et les résultats sont tous bien définis. Consultez la documentation ici.
calavicci
@ ru111 Cela fonctionne aussi sur Python 3.6 (il n'y a aucune raison pour qu'il cesse de fonctionner).
jfs
J'obtiens "ValueError: La valeur de vérité d'un tableau avec plus d'un élément est ambiguë. Utilisez a.any () ou a.all ()"
ru111
@ ru111, vous devez écrire (0 < x) & (x < 10)(comme indiqué dans la réponse) au lieu de 0 < x < 10ce qui ne fonctionne pas pour les tableaux numpy sur aucune version de Python.
jfs
34

IMO OP ne veut pas réellement np.bitwise_and()(aka &) mais veut réellement np.logical_and()parce qu'ils comparent des valeurs logiques telles que Trueet False- voir ce message SO sur logique vs bit à bit pour voir la différence.

>>> x = array([5, 2, 3, 1, 4, 5])
>>> y = array(['f','o','o','b','a','r'])
>>> output = y[np.logical_and(x > 1, x < 5)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

Et une façon équivalente de le faire est np.all()de définir l' axisargument de manière appropriée.

>>> output = y[np.all([x > 1, x < 5], axis=0)] # desired output is ['o','o','a']
>>> output
array(['o', 'o', 'a'],
      dtype='|S1')

par les chiffres:

>>> %timeit (a < b) & (b < c)
The slowest run took 32.97 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 1.15 µs per loop

>>> %timeit np.logical_and(a < b, b < c)
The slowest run took 32.59 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 3: 1.17 µs per loop

>>> %timeit np.all([a < b, b < c], 0)
The slowest run took 67.47 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 3: 5.06 µs per loop

donc l'utilisation np.all()est plus lente, mais &et logical_andsont à peu près les mêmes.

Mark Mikofski
la source
7
Vous devez faire un peu attention à la façon dont vous parlez de ce qui est évalué. Par exemple, in output = y[np.logical_and(x > 1, x < 5)], x < 5 est évalué (créant éventuellement un énorme tableau), même si c'est le deuxième argument, car cette évaluation se produit en dehors de la fonction. IOW, logical_andobtient passé deux arguments déjà évalués. Ceci est différent du cas habituel de a and b, dans lequel bn'est pas évalué si aest vrai.
DSM
15
il n'y a pas de différence entre bitwise_and () et logical_and () pour les tableaux booléens
jfs
22

Ajoutez un détail aux réponses de @JF Sebastian et @Mark Mikofski:
Si l'on veut obtenir les indices correspondants (plutôt que les valeurs réelles du tableau), le code suivant fera l'affaire:

Pour satisfaire plusieurs (toutes) conditions:

select_indices = np.where( np.logical_and( x > 1, x < 5) )[0] #   1 < x <5

Pour satisfaire plusieurs (ou) conditions:

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Bonne volonté
la source
2
Notez que numpy.where ne retournera pas seulement un tableau d'indices, mais retournera à la place un tuple (la sortie de condition.nonzero ()) contenant des tableaux - dans ce cas, (the array of indices you want,)vous devrez select_indices = np.where(...)[0]donc obtenir le résultat souhaité et attendez.
calavicci
5

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

>>> # Arrays
>>> x = np.array([5, 2, 3, 1, 4, 5])
>>> y = np.array(['f','o','o','b','a','r'])

>>> # Function containing the constraints
>>> func = np.vectorize(lambda t: t>1 and t<5)

>>> # Call function on x
>>> y[func(x)]
>>> array(['o', 'o', 'a'], dtype='<U1')

L'avantage est que vous pouvez ajouter beaucoup plus de types de contraintes dans la fonction vectorisée.

J'espère que ça aide.


la source
1
Ce n'est pas un bon moyen de faire une indexation dans NumPy (ce sera très lent).
Alex Riley
1

En fait, je le ferais de cette façon:

L1 est la liste d'index des éléments satisfaisant la condition 1; (peut-être que vous pouvez utiliser somelist.index(condition1)ou np.where(condition1)pour obtenir L1.)

De même, vous obtenez L2, une liste d'éléments satisfaisant la condition 2;

Ensuite, vous trouvez l'intersection en utilisant intersect(L1,L2).

Vous pouvez également trouver l'intersection de plusieurs listes si vous obtenez plusieurs conditions à satisfaire.

Ensuite, vous pouvez appliquer l'index dans n'importe quel autre tableau, par exemple, x.

Shuo Yang
la source
0

Pour les tableaux 2D, vous pouvez le faire. Créez un masque 2D en utilisant la condition. Convertissez le masque de condition en int ou float, selon le tableau, et multipliez-le par le tableau d'origine.

In [8]: arr
Out[8]: 
array([[ 1.,  2.,  3.,  4.,  5.],
       [ 6.,  7.,  8.,  9., 10.]])

In [9]: arr*(arr % 2 == 0).astype(np.int) 
Out[9]: 
array([[ 0.,  2.,  0.,  4.,  0.],
       [ 6.,  0.,  8.,  0., 10.]])
Gautam Sreekumar
la source