Numpy donde funcionan múltiples condiciones

132

Tengo una serie de distancias llamadas dists. Quiero seleccionar discos que están entre dos valores. Escribí la siguiente línea de código para hacer eso:

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

Sin embargo, esto selecciona solo para la condición

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

Si hago los comandos secuencialmente usando una variable temporal, funciona bien. ¿Por qué el código anterior no funciona y cómo lo hago funcionar?

Salud

usuario1654183
fuente

Respuestas:

203

La mejor manera en su caso particular sería cambiar sus dos criterios a uno:

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

Solo crea una matriz booleana, y en mi opinión es más fácil de leer porque dice, ¿ está distdentro de un dro r? (Aunque me redefiniría rpara ser el centro de su región de interés en lugar del principio, entonces r = r + dr/2.) Pero eso no responde a su pregunta.


La respuesta a su pregunta: en
realidad no es necesario wheresi solo está tratando de filtrar los elementos distsque no se ajustan a sus criterios:

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

Porque el &te dará un elemento and(los paréntesis son necesarios).

O, si quiere usarlo wherepor alguna razón, puede hacer:

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

Por qué:
la razón por la que no funciona es porque np.wheredevuelve una lista de índices, no una matriz booleana. Estás tratando de obtener andentre dos listas de números, que por supuesto no tienen los valores True/ Falseque esperas. Si ay bson ambos Truevalores, entonces a and bregresa b. Así que decir algo así [0,1,2] and [2,3,4]solo te dará [2,3,4]. Aquí está en acción:

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]),)

Lo que esperaba comparar era simplemente la matriz booleana, por ejemplo

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)

Ahora puede llamar np.wherea la matriz booleana combinada:

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. ])

O simplemente indexe la matriz original con la matriz booleana usando una indexación elegante

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

La respuesta aceptada explicó el problema lo suficientemente bien. Sin embargo, el enfoque más Numpythonic para aplicar múltiples condiciones es utilizar funciones lógicas numpy . En este ase puedes usar np.logical_and:

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

Una cosa interesante para señalar aquí; la forma habitual de usar OR y AND también funcionará en este caso, pero con un pequeño cambio. En lugar de "y" y en lugar de "o", use Ampersand (&) y Pipe Operator (|) y funcionará.

Cuando usamos 'y' :

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

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

Y esto es lo mismo en el caso cuando intentamos aplicar múltiples filtros en el caso de pandas Dataframe. Ahora, el razonamiento detrás de esto tiene que ver con los Operadores lógicos y los Operadores bit a bit y para una mejor comprensión de los mismos, sugeriría pasar por esta respuesta o Q / A similar en stackoverflow.

ACTUALIZAR

Un usuario preguntó, ¿por qué es necesario dar (ar> 3) y (ar <6) dentro del paréntesis. Bueno, aquí está la cosa. Antes de comenzar a hablar sobre lo que está sucediendo aquí, uno debe saber acerca de la precedencia del operador en Python.

Similar a lo que trata BODMAS, python también da prioridad a lo que debe realizarse primero. Los elementos dentro del paréntesis se realizan primero y luego el operador bit a bit se pone a trabajar. A continuación, mostraré lo que sucede en los dos casos cuando usa y no usa "(", ")".

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

Como no hay corchetes aquí, el operador bit a bit ( &) se confunde aquí de lo que incluso le está pidiendo que obtenga un AND lógico, porque en la tabla de precedencia de operador, si lo ve, &se le da prioridad sobre <u >operadores. Aquí está la tabla desde la precedencia más baja hasta la más alta.

ingrese la descripción de la imagen aquí

Ni siquiera está realizando la operación <y >, y se le pide que realice una operación AND lógica. Entonces es por eso que da ese error.

Puede consultar el siguiente enlace para obtener más información sobre: precedencia del operador

Ahora al caso 2:

Si usa el soporte, verá claramente lo que sucede.

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)

Dos matrices de verdadero y falso. Y puede realizar fácilmente operaciones lógicas Y en ellos. Lo que te da:

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

Y descansa, ya sabes, en cualquier lugar, para casos dados, donde sea verdadero, asigna el primer valor (es decir, aquí 'yo') y si es falso, el otro (es decir, aquí, manteniendo el original).

Eso es todo. Espero haber explicado bien la consulta.

Amit Amola
fuente
1
¿Por qué se tiene que poner ()alrededor (ar>3)y (ar>6)?
RTrain3k
Esa es una muy buena pregunta. Es una pregunta tan buena que tuve que pensar que lo que en el mundo necesita. Entonces lo hice, le pregunté a un colega también y discutimos y ahora tengo una solución para usted. Poniéndolo en la respuesta como una ACTUALIZACIÓN. Es realmente simple pero difícil de entender.
Amit Amola
Verifique la actualización RTrain3k, he respondido su consulta.
Amit Amola
5

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

>>> # 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.

También puede usar en np.argwherelugar de np.wherepara una salida clara. Pero esa es tu decisión :)

Espero eso ayude.


fuente
2

Tratar:

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

Esto debería funcionar:

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

La forma más elegante ~~

Qhan
fuente
2

Tratar:

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

Salida: (matriz ([2, 3]),)

Puede ver las funciones lógicas para más detalles.

Xiong-Hui Chen
fuente
0

He resuelto este simple ejemplo.

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
fuente
66
No hay necesidad de iterar en este caso. NumPy tiene indexación booleana.
M456