Acabei de descobrir um bug lógico no meu código que estava a causar todo o tipo de problemas. Eu estava inadvertidamente fazendo um bitwise AND em vez de um logical AND.
Eu mudei o código de:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
TO:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) and (r["dt"] <= enddate))
selected = r[mask]
Para minha surpresa, recebi a mensagem de erro um pouco críptica:
ValueError: O valor verdadeiro de um array com mais de um elemento é ambíguo. Use a.any() ou a.all()
Por que um erro similar não foi emitido quando eu uso uma operação um pouco - e como eu corrijo isso?
O r
é um (rec)array entorpecido. Então r["dt"] >= data de início
também é um (booleano)
...matriz. Para arrays numéricos a operação &
retorna o elemento- e dos dois
matrizes booleanas.
Os desenvolvedores da NumPy sentiram que não havia uma forma comumente entendida de avaliar um array em contexto booleano: pode significar "Verdade" se algum elemento for "Verdade", ou pode significar "Verdade" se todos os elementos forem "Verdade", ou "Verdade" se o array não tiver comprimento zero, só para citar três possibilidades.
Como diferentes usuários podem ter necessidades diferentes e pressupostos diferentes, a
Os desenvolvedores da NumPy se recusaram a adivinhar e, em vez disso, decidiram aumentar um ValueError
sempre que se tenta avaliar uma matriz em contexto booleano. Aplicando e
a
duas matrizes numéricas fazem com que as duas matrizes sejam avaliadas em contexto booleano (por
chamando __bool__
em Python3 ou __nonzero__
em Python2).
O seu código original
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
parece correcto. Entretanto, se você quiser and
, então ao invés de a e b
utilize (a-b).any()
ou (a-b).all()
.
Eu tive o mesmo problema (i.e. indexação com múltiplas condições, aqui ele's encontrando dados em um determinado intervalo de datas). O (a-b).any()
ou (a-b).all()
parece não funcionar, pelo menos para mim.
Alternativamente, encontrei outra solução que funciona perfeitamente para a minha funcionalidade desejada (https://stackoverflow.com/questions/12647471/the-truth-value-of-an-array-with-more-than-one-element-is-ambigous-when-trying-t).
Ao invés de utilizar o código sugerido acima, simplesmente utilizar um numpy.logical_and(a,b)
iria funcionar. Aqui você pode querer reescrever o código como
selected = r[numpy.logical_and(r["dt"] >= startdate, r["dt"] <= enddate)]
A razão para a exceção é que "e" implicitamente chama "bool". Primeiro no operando da esquerda e (se o operando da esquerda for 'Verdade') então no operando da direita. Então x e y
é equivalente a bool(x) e bool(y)
.
No entanto o bool' em um
numpy.ndarray' (se ele contém mais de um elemento) lançará a exceção que você viu:
>>> 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()
A chamada bool()
está implícita em and
, mas também em if
, while
, or
, portanto qualquer um dos seguintes exemplos também falhará:
>>> 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()
Há mais funções e afirmações em Python que ocultam chamadas 'bool', por exemplo '2 < x < 10' é apenas outra forma de escrever '2 < x e x < 10'. E o e' chamará
bool': `bool(2 < x) e bool(x < 10''.
O **element-wise*** equivalente para and
seria a função np.logical_and
, da mesma forma você poderia utilizar np.logical_or
como equivalente para or
.
Para arrays booleanos - e comparações como <
, <=
, ==
, !=
, >=
e >
em arrays NumPy retornam arrays NumPy booleanos - você também pode utilizar as funções **element-wise bitwise*** (e operadores): np.bitwise_and
(&
operator)
>>> 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)
e 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)
Uma lista completa de funções lógicas e binárias pode ser encontrada na documentação NumPy: