¿Cómo selecciono elementos de una condición dada de una matriz?

156

Supongamos que tengo una matriz numpy x = [5, 2, 3, 1, 4, 5], y = ['f', 'o', 'o', 'b', 'a', 'r']. Quiero seleccionar los elementos ycorrespondientes a los elementos xque son mayores que 1 y menores que 5.

Lo intenté

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']

Pero esto no funciona. ¿Cómo haría esto?

Beto
fuente

Respuestas:

220

Su expresión funciona si agrega paréntesis:

>>> y[(1 < x) & (x < 5)]
array(['o', 'o', 'a'], 
      dtype='|S1')
jfs
fuente
1
Eso es bueno ... vecMask = 1 <x genera una máscara vectorial como vecMask = (False, True, ...), que se puede combinar con otras máscaras vectoriales. Cada elemento es la condición para tomar los elementos de un vector fuente (Verdadero) o no (Falso). Esto se puede usar también con la versión completa numpy.extract (vecMask, vecSrc) o numpy.where (vecMask, vecSrc, vecSrc2).
MasterControlProgram
66
@ JennyYueJin: Sucede debido a la precedencia. (Bitwise) &tiene mayor prioridad que <y >, que a su vez tienen mayor prioridad que (lógico) and. x > 1 and x < 5Evaluar las desigualdades primero y luego la conjunción lógica; x > 1 & x < 5evalúa la conjunción bit a bit de 1y (los valores en) x, luego las desigualdades. (x > 1) & (x < 5)obliga a las desigualdades a evaluar primero, por lo que todas las operaciones ocurren en el orden previsto y los resultados están bien definidos. Ver documentos aquí.
calavicci
@ ru111 También funciona en Python 3.6 (no hay razón para que deje de funcionar).
jfs
Obtengo "ValueError: el valor de verdad de una matriz con más de un elemento es ambiguo. Use a.any () o a.all ()"
ru111
@ ru111 debe escribir (0 < x) & (x < 10)(como se muestra en la respuesta) en lugar de lo 0 < x < 10cual no funciona para matrices numpy en ninguna versión de Python.
jfs
34

IMO OP en realidad no quiere np.bitwise_and()(aka &) pero realmente quiere np.logical_and()porque están comparando valores lógicos como Truey False- vea esta publicación SO en lógico vs. bit a bit para ver la diferencia.

>>> 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')

Y una forma equivalente de hacer esto es con np.all() estableciendo el axisargumento adecuadamente.

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

Por los números:

>>> %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

así que usar np.all()es más lento, pero &y logical_andson casi lo mismo.

Mark Mikofski
fuente
77
Debe tener un poco de cuidado sobre cómo habla sobre lo que se evalúa. Por ejemplo, en output = y[np.logical_and(x > 1, x < 5)], x < 5 se evalúa (posiblemente creando una matriz enorme), aunque es el segundo argumento, porque esa evaluación ocurre fuera de la función. IOW, logical_andpasa dos argumentos ya evaluados. Esto es diferente del caso habitual de a and b, en el que bno se evalúa si aes truelike.
DSM
15
no hay diferencia entre bitwise_and () y logical_and () para matrices booleanas
jfs
21

Agregue un detalle a las respuestas de @JF Sebastian y @Mark Mikofski:
Si uno quiere obtener los índices correspondientes (en lugar de los valores reales de la matriz), el siguiente código servirá:

Para satisfacer múltiples (todas) condiciones:

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

Para satisfacer múltiples (o) condiciones:

select_indices = np.where( np.logical_or( x < 1, x > 5 ) )[0] # x <1 or x >5
Buena voluntad
fuente
2
Tenga en cuenta que numpy.where no solo devolverá una matriz de índices, sino que también devolverá una tupla (la salida de condition.nonzero ()) que contiene matrices, en este caso (the array of indices you want,), por lo que select_indices = np.where(...)[0]deberá obtener el resultado que desea y esperar
calavicci
5

Me gusta usar np.vectorizepara tales tareas. Considera lo siguiente:

>>> # 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')

La ventaja es que puede agregar muchos más tipos de restricciones en la función vectorizada.

Espero eso ayude.


fuente
1
Esta no es una buena forma de indexar en NumPy (será muy lenta).
Alex Riley
1

En realidad lo haría de esta manera:

L1 es la lista índice de elementos que satisfacen la condición 1; (tal vez pueda usar somelist.index(condition1)o np.where(condition1)para obtener L1).

Del mismo modo, obtienes L2, una lista de elementos que satisfacen la condición 2;

Luego encuentras la intersección usando intersect(L1,L2).

También puede encontrar la intersección de varias listas si obtiene múltiples condiciones para satisfacer.

Luego puede aplicar el índice en cualquier otra matriz, por ejemplo, x.

Shuo Yang
fuente
0

Para matrices 2D, puede hacer esto. Crea una máscara 2D usando la condición. Escriba la máscara de condición en int o float, dependiendo de la matriz, y multiplíquela con la matriz original.

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
fuente