Ho appena scoperto un bug logico nel mio codice che stava causando ogni sorta di problemi. Stavo inavvertitamente facendo un bitwise AND invece di un logical AND.
Ho cambiato il codice da:
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]
Con mia sorpresa, ho ricevuto un messaggio di errore piuttosto criptico:
ValueError: Il valore di verità di un array con più di un elemento è ambiguo. Usa a.any() o a.all()
Perché un errore simile non è stato emesso quando uso un'operazione bitwise - e come lo risolvo?
r
è un (rec)array numpy. Quindi r["dt"] >= startdate
è anche un (booleano)
array. Per gli array numpy l'operazione &
restituisce l'elementwise-and dei due
array booleani.
Gli sviluppatori di NumPy hanno ritenuto che non ci fosse un modo comunemente compreso per valutare
un array in un contesto booleano: potrebbe significare True
se ogni elemento è
True
, o potrebbe significare True
se tutti gli elementi sono True
, o True
se l'array ha lunghezza non nulla, solo per citare tre possibilità.
Poiché utenti diversi potrebbero avere esigenze diverse e presupposti diversi, gli sviluppatori di
sviluppatori di NumPy hanno rifiutato di indovinare e hanno invece deciso di sollevare un ValueError
ogni volta che si cerca di valutare un array in un contesto booleano. Applicando e
a
due array di NumPy fa sì che i due array siano valutati in un contesto booleano (chiamando
chiamando __bool__
in Python3 o __nonzero__
in Python2).
Il tuo codice originale
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
sembra corretto. Tuttavia, se vuoi "e", invece di "a e b" usa "a-b).any()o "a-b).all()
.
Ho avuto lo stesso problema (cioè l'indicizzazione con più condizioni, qui si tratta di trovare dati in un certo intervallo di date). I metodi (a-b).any()
o (a-b).all()
sembrano non funzionare, almeno per me.
In alternativa ho trovato un'altra soluzione che funziona perfettamente per la mia funzionalità desiderata (https://stackoverflow.com/questions/12647471/the-truth-value-of-an-array-with-more-than-one-element-is-ambigous-when-trying-t).
Invece di usare il codice suggerito sopra, semplicemente usando un numpy.logical_and(a,b)
funzionerebbe. Qui potresti voler riscrivere il codice come
selected = r[numpy.logical_and(r["dt"] >= startdate, r["dt"] <= enddate)]
La ragione dell'eccezione è che e
chiama implicitamente bool
. Prima sull'operando sinistro e (se l'operando sinistro è True
) poi sull'operando destro. Quindi x e y
è equivalente a bool(x) e bool(y)
.
Tuttavia il bool
su un numpy.ndarray
(se contiene più di un elemento) lancerà l'eccezione che hai 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 chiamata bool()
è implicita in and
, ma anche in if
, while
, or
, quindi anche uno qualsiasi dei seguenti esempi fallirà:
>>> 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()
Ci sono più funzioni e dichiarazioni in Python che nascondono chiamate bool
, per esempio 2 < x < 10
è solo un altro modo di scrivere 2 < x e x < 10
. E la e
chiamerà bool
: bool(2 < x) e bool(x < 10)
.
L'equivalente element-wise per and
sarebbe la funzione np.logical_and
, allo stesso modo si potrebbe usare np.logical_or
come equivalente per or
.
Per gli array booleani - e i confronti come <
, <=
, ==
, !=
, >=
e >
sugli array NumPy restituiscono array NumPy booleani - potete anche usare le funzioni (e operatori) element-wise bitwise: np.bitwise_and
(operatore &
)
>>> 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
(operatore |
):
>>> 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 di funzioni logiche e binarie può essere trovata nella documentazione di NumPy: