Acabo de descubrir un error lógico en mi código que estaba causando todo tipo de problemas. Sin darme cuenta, estaba haciendo un And de bits en lugar de un And lógico.
He cambiado el código de:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
A:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) and (r["dt"] <= enddate))
selected = r[mask]
Para mi sorpresa, recibí un mensaje de error bastante críptico:
ValueError: El valor de verdad de un array con más de un elemento es ambiguo. Utilice a.any() o a.all()
¿Por qué no se emite un error similar cuando utilizo una operación bitwise - y cómo lo arreglo?
r
es un (rec)array de numpy. Así que r["dt"] >= startdate
es también un array (booleano)
(booleana). Para los arrays numpy la operación &
devuelve el elemento-y de los dos
arrays booleanos.
Los desarrolladores de NumPy consideraron que no había una forma comúnmente entendida de evaluar
un array en contexto booleano: puede significar Verdadero
si cualquier elemento es
verdadero", o podría significar "verdadero" si "todos" los elementos son "verdaderos", o "verdadero" si la matriz tiene una longitud distinta de cero, sólo para nombrar tres posibilidades.
Dado que diferentes usuarios pueden tener diferentes necesidades y diferentes suposiciones, los desarrolladores de
desarrolladores de NumPy se negaron a hacer conjeturas y decidieron lanzar un ValueError
siempre que se intente evaluar un array en un contexto booleano. La aplicación de and
a
a dos arrays de numpy hace que los dos arrays se evalúen en contexto booleano (llamando
llamando a __bool__
en Python3 o __nonzero__
en Python2).
Tu código original
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
parece correcto. Sin embargo, si quieres y
, entonces en lugar de a y b
utiliza (a-b).any()
o (a-b).all()
.
Yo tenía el mismo problema (es decir, la indexación con múltiples condiciones, aquí se trata de encontrar datos en un determinado rango de fechas). El (a-b).any()
o (a-b).all()
parecen no funcionar, al menos para mí.
Como alternativa he encontrado otra solución que funciona perfectamente para mi funcionalidad deseada (https://stackoverflow.com/questions/12647471/the-truth-value-of-an-array-with-more-than-one-element-is-ambigous-when-trying-t).
En lugar de usar el código sugerido arriba, simplemente usando un numpy.logical_and(a,b)
funcionaría. En este caso, es posible que desee reescribir el código como
selected = r[numpy.logical_and(r["dt"] >= startdate, r["dt"] <= enddate)]
La razón de la excepción es que and
llama implícitamente a bool
. Primero en el operando de la izquierda y (si el operando de la izquierda es True
) luego en el operando de la derecha. Así que x e y
es equivalente a bool(x) y bool(y)
.
Sin embargo el bool
en un numpy.ndarray
(si contiene más de un elemento) lanzará la excepción que has visto:
>>> import numpy as np
>>> arr = np.array([1, 2, 3])
>>> bool(arr)
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
La llamada a bool()
está implícita en and
, pero también en if
, while
, or
, por lo que cualquiera de los siguientes ejemplos también fallará:
>>> arr and arr
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> if arr: pass
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> while arr: pass
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
>>> arr or arr
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
Hay más funciones y sentencias en Python que ocultan las llamadas bool
, por ejemplo 2 < x < 10
es sólo otra forma de escribir 2 < x y x < 10
. Y el and
llamará a bool
: bool(2 < x) y bool(x < 10)
.
El equivalente elemental para and
sería la función np.logical_and
, del mismo modo se podría utilizar np.logical_or
como equivalente para or
.
Para matrices booleanas - y comparaciones como <
, <=
, ==
, !=
, >=
y >
en matrices NumPy devuelven matrices NumPy booleanas - también puedes usar las funciones (y operadores) element-wise bitwise: np.bitwise_and
(operador &
)
>>> np.logical_and(arr > 1, arr < 3)
array([False, True, False], dtype=bool)
>>> np.bitwise_and(arr > 1, arr < 3)
array([False, True, False], dtype=bool)
>>> (arr > 1) & (arr < 3)
array([False, True, False], dtype=bool)
y bitwise_or
(operador |
):
>>> np.logical_or(arr <= 1, arr >= 3)
array([ True, False, True], dtype=bool)
>>> np.bitwise_or(arr <= 1, arr >= 3)
array([ True, False, True], dtype=bool)
>>> (arr <= 1) | (arr >= 3)
array([ True, False, True], dtype=bool)
Una lista completa de funciones lógicas y binarias se puede encontrar en la documentación de NumPy: