Ik ontdekte net een logische fout in mijn code die allerlei problemen veroorzaakte. Ik deed per ongeluk een bitwise AND in plaats van een logical AND.
Ik veranderde de code van:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
NAAR:
r = mlab.csv2rec(datafile, delimiter=',', names=COL_HEADERS)
mask = ((r["dt"] >= startdate) and (r["dt"] <= enddate))
selected = r[mask]
Tot mijn verbazing kreeg ik de nogal cryptische foutmelding:
ValueError: De waarheidswaarde van een array met meer dan één element is ambigu. Gebruik a.any() of a.all()
Waarom werd een gelijkaardige fout niet gegenereerd wanneer ik een bitwise bewerking gebruik - en hoe los ik dit op?
r
is een numpy (rec)array. Dus r["dt"] >= startdate
is ook een (boolean)
array. Voor numpy arrays geeft de &
bewerking de element- en van de twee
boolean arrays.
De NumPy ontwikkelaars vonden dat er niet één algemeen begrepen manier was om
van een array in booleaanse context: het kan True
betekenen als elk element
True
is, of True
als alle elementen True
zijn, of True
als de array een lengte heeft die niet nul is, om maar eens drie mogelijkheden te noemen.
Omdat verschillende gebruikers verschillende behoeften en verschillende aannames kunnen hebben, hebben de
NumPy ontwikkelaars niet te raden en in plaats daarvan besloten ze een ValueError
wanneer men een array in booleaanse context probeert te evalueren. Het toepassen van en
op
twee numpy arrays zorgt ervoor dat de twee arrays worden geëvalueerd in booleaanse context (door
__bool__
in Python3 of __nonzero__
in Python2 aan te roepen).
Uw originele code
mask = ((r["dt"] >= startdate) & (r["dt"] <= enddate))
selected = r[mask]
ziet er correct uit. Echter, als je en
wilt, gebruik dan in plaats van a en b
(a-b).any()
of (a-b).all()
.
Ik had hetzelfde probleem (d.w.z. indexeren met meerdere voorwaarden, hier's het vinden van gegevens in een bepaald datumbereik). De (a-b).any()
of (a-b).all()
lijken niet te werken, althans voor mij.
Als alternatief heb ik een andere oplossing gevonden die perfect werkt voor mijn gewenste functionaliteit (https://stackoverflow.com/questions/12647471/the-truth-value-of-an-array-with-more-than-one-element-is-ambigous-when-trying-t).
In plaats van de hierboven voorgestelde code te gebruiken, zou een numpy.logical_and(a,b)
ook werken. Hier zou je de code kunnen herschrijven als
selected = r[numpy.logical_and(r["dt"] >= startdate, r["dt"] <= enddate)]
De reden voor de uitzondering is dat en
impliciet bool
aanroept. Eerst op de linker operand en (als de linker operand True
is) daarna op de rechter operand. Dus x en y
is equivalent met bool(x) en bool(y)
.
Maar de bool
op een numpy.ndarray
(als deze meer dan één element bevat) zal de uitzondering geven die je hebt gezien:
>>> 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()
De bool()
aanroep is impliciet in en
, maar ook in if
, while
, or
, dus elk van de volgende voorbeelden zal ook falen:
>>> 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()
Er zijn meer functies en verklaringen in Python die bool
aanroepen verbergen, bijvoorbeeld 2 < x < 10
is gewoon een andere manier om 2 < x en x < 10
te schrijven. En de en
zal bool
aanroepen: bool(2 < x) en bool(x < 10)
.
Het element-wise equivalent voor en
zou de np.logical_and
functie zijn, op dezelfde manier zou je np.logical_or
kunnen gebruiken als equivalent voor of
.
Voor booleaanse arrays - en vergelijkingen zoals <
, <=
, ==
, !=
, >=
en >
op NumPy arrays geven booleaanse NumPy arrays terug - kun je ook de element-wise bitwise functies (en operatoren) gebruiken: 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)
en bitwise_or
(|
operator):
>>> 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)
Een volledige lijst van logische en binaire functies is te vinden in de NumPy documentatie: