ValueError: истинное значение массива с более чем одним элементом неоднозначно. Используйте a.any() или a.all()
Пусть x
- массив NumPy . Следующее:
(x > 1) and (x < 3)
Выдает сообщение об ошибке:
ValueError: истинное значение массива с более чем одним элементом неоднозначно. Используйте a.any() или a.all()
Как мне это исправить?
Переведено автоматически
Ответ 1
Если a
и b
являются логическими массивами NumPy, то &
операция возвращает поэлементное значение-and из них:
a & b
Который возвращает логический массив. Чтобы свести это к одному логическому значению, используйте либо
(a & b).any()
или
(a & b).all()
Примечание: если a
и b
являются нелогическими массивами, вместо этого используйте (a - b).any()
or (a - b).all()
.
Обоснование
Разработчики NumPy посчитали, что не существует единого общепринятого способа вычисления массива в логическом контексте: это может означать, что True
если какой-либо элемент является True
, или это может означать, что True
если все элементы являются True
, или True
если массив имеет ненулевую длину, просто чтобы назвать три возможности.
Поскольку у разных пользователей могут быть разные потребности и разные предположения, разработчики NumPy отказались угадывать и вместо этого решили вызывать a ValueError
всякий раз, когда кто-то пытается оценить массив в логическом контексте. Применение and
к двум массивам numpy приводит к тому, что два массива вычисляются в логическом контексте (путем вызова __bool__
в Python3 или __nonzero__
в Python2).
Ответ 2
У меня была такая же проблема (т. Е. Индексирование с несколькими условиями, здесь это поиск данных в определенном диапазоне дат). (a-b).any()
Или (a-b).all()
, похоже, не работает, по крайней мере, для меня.
В качестве альтернативы я нашел другое решение, которое идеально подходит для моей желаемой функциональности (истинное значение массива с более чем одним элементом неоднозначно при попытке проиндексировать массив).
Вместо предложенного выше кода используйте:
numpy.logical_and(a, b)
Ответ 3
Причина исключения в том, что and
неявный вызов bool
. Сначала для левого операнда и (если левый операнд True
) затем для правого операнда. So x and y
эквивалентно bool(x) and bool(y)
.
Однако bool
на a numpy.ndarray
(если он содержит более одного элемента) будет генерироваться исключение, которое вы видели:
>>> 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()
bool()
Вызов неявен в and
, но также и в if
, while
, or
, поэтому любой из следующих примеров также завершится ошибкой:
>>> 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()
В Python есть больше функций и операторов, которые скрывают, например, bool
вызовы, 2 < x < 10
это просто другой способ записи 2 < x and x < 10
. И and
вызовет bool
: bool(2 < x) and bool(x < 10)
.
Поэлементным эквивалентом для and
была бы np.logical_and
функция, аналогично вы могли бы использовать np.logical_or
как эквивалент для or
.
Для логических массивов - а сравнения типа <
, <=
, ==
, !=
>=
и >
в массивах NumPy возвращают логические массивы NumPy - вы также можете использовать поэлементные побитовые функции (и операторы): 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)
и bitwise_or
(|
):
>>> 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)
Полный список логических и двоичных функций можно найти в документации NumPy:
Ответ 4
Причина
Эта ошибка возникает каждый раз, когда код пытается преобразовать массив Numpy в логическое значение (т. Е. Проверить его истинное значение, как описано в сообщении об ошибке). Для данного массива a
это может произойти:
Явно, используя
bool(a)
.Неявно с логическими операторами:
a and a
,a or a
,not a
.Неявно используя встроенные
any
иall
функции. (Они могут принимать один массив, независимо от того, сколько измерений он имеет; но не могут принимать список, кортеж, набор и т.д. Массивов.)Неявно в
if
инструкции, используяif a:
. Хотя обычно возможно использовать любой объект Python вif
инструкции, массивы Numpy намеренно нарушают эту функцию, чтобы избежать логических ошибок.
Массивы и сравнения Numpy (==
, !=
, <
, >
<=
, >=
, ,,,)
Сравнения имеют особое значение для массивов Numpy. Мы рассмотрим здесь оператор ==
; остальные ведут себя аналогично. Предположим, что у нас есть
import numpy as np
>>> a = np.arange(9)
>>> b = a % 3
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8])
>>> b
array([0, 1, 2, 0, 1, 2, 0, 1, 2])
Тогда, a == b
не означает "дайте ответ True
или False
: a
равно b
?", как это обычно означало бы. Вместо этого он будет сравнивать значения поэлементно и вычислять массив логических результатов для этих сравнений:
>>> a == b
array([ True, True, True, False, False, False, False, False, False])
Другими словами, он выполняет тот же вид трансляции, что и математические операторы (например, b = a % 3
).
Не имеет смысла использовать этот результат для if
инструкции, потому что неясно, что делать: должны ли мы вводить if
блок, потому что некоторые значения совпали? Или мы должны ввести else
блок, потому что некоторые значения не совпадают? Здесь Numpy применяет важный принцип из Zen of Python: "Перед лицом двусмысленности откажитесь от соблазна угадать".
Таким образом, Numpy только позволит преобразовать массив в bool
если он содержит ровно один элемент. (В некоторых более старых версиях оно также преобразуется в False
для пустого массива; но есть веские логические причины, почему это также следует рассматривать как неоднозначное.)
Аналогично, сравнение a == 4
не проверит, равен ли массив целому числу (конечно, ни один массив никогда не может быть равен какому-либо целому числу). Вместо этого он будет транслировать сравнение по всему массиву, выдавая аналогичный массив результатов:
>>> a == 4
array([False, False, False, False, True, False, False, False, False])
Исправление выражений
- Если код явно преобразуется в
bool
, выберите между применением.any
или.all
к результату, в зависимости от обстоятельств. Как следует из названий,.any
свернет массив до единственного логического значения, указывающего, было ли какое-либо значение истинным;.all
проверит, были ли истинны все значения.>>> (a == 4).all() # `a == 4` contains some `False` values
False
>>> (a == 4).any() # and also some `True` values
True
>>> a.all() # We can check `a` directly as well: `0` is not truthy,
False
>>> a.any() # but other values in `a` are.
True
Если цель состоит в том, чтобы преобразоватьa
в логическое значение по элементам, используйтеa.astype(bool)
, или (только для числовых вводов)a != 0
. - Если в коде используется логическая логика (
and
/or
/not
), используйте побитовые операторы (&
/|
/~
соответственно):>>> ((a % 2) != 0) & ((a % 3) != 0) # N.B. `&`, not `and`
array([False, True, False, False, False, True, False, True, False])
Обратите внимание, что побитовые операторы также предоставляют доступ к^
для исключающего-или логических входных данных; это не поддерживается логическими операторами (их нетxor
). - Для списка (или другой последовательности) массивов, которые необходимо объединить одинаковым образом (т. Е. То, что делают встроенные модули
all
иany
), вместо этого создайте соответствующий (N + 1)-мерный массив и используйтеnp.all
илиnp.any
вдоль оси 0:>>> a = np.arange(100) # a larger array for a more complex calculation
>>> sieves = [a % p for p in (2, 3, 5, 7)]
>>> all(sieves) # won't work
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: The truth value of an array with more than one element is ambiguous.
Use a.any() or a.all()
>>> np.all(np.array(sieves), axis=0) # instead:
array([False, True, False, False, False, False, False, False, False,
False, False, True, False, True, False, False, False, True,
False, True, False, False, False, True, False, False, False,
False, False, True, False, True, False, False, False, False,
False, True, False, False, False, True, False, True, False,
False, False, True, False, False, False, False, False, True,
False, False, False, False, False, True, False, True, False,
False, False, False, False, True, False, False, False, True,
False, True, False, False, False, False, False, True, False,
False, False, True, False, False, False, False, False, True,
False, False, False, False, False, False, False, True, False,
False])
Исправление if
Во-первых, имейте в виду, что если в коде есть if
оператор, который использует неработающее выражение (например, if (a % 3 == 0) or (a % 5 == 0):
), то выражение также нужно будет исправить.
Как правило, явное преобразование в bool (с использованием .all()
или .any()
, как указано выше) позволит избежать исключения:
>>> a = np.arange(20) # enough to illustrate this
>>> if ((a % 3 == 0) | (a % 5 == 0)).any():
... print('there are fizzbuzz values')
...
there are fizzbuzz values
но это может не делать того, что требуется:
>>> a = np.arange(20) # enough to illustrate this
>>> if ((a % 3 == 0) | (a % 5 == 0)).any():
... a = -1
...
>>> a
-1
Если цель состоит в том, чтобы оперировать каждым значением, где условие истинно, то естественный способ сделать это - использовать результирующий массив в качестве маски. Например, чтобы присвоить новое значение везде, где условие истинно, просто проиндексируйте в исходный массив с вычисленной маской и присвоите:
>>> a = np.arange(20)
>>> a[(a % 3 == 0) | (a % 5 == 0)] = -1
>>> a
array([-1, 1, 2, -1, 4, -1, -1, 7, 8, -1, -1, 11, -1, 13, 14, -1, 16,
17, -1, 19])
Этот метод индексирования также полезен для поиска значений, удовлетворяющих условию. Основываясь на предыдущем sieves
примере:
>>> a = np.arange(100)
>>> sieves = [a % p for p in (2, 3, 5, 7)]
>>> a[np.all(np.array(sieves), axis=0)]
array([ 1, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97])
(Упражнение: изучите код и поймите, почему этот результат не совсем является списком простых чисел меньше 100; затем исправьте это.)
Использование Pandas
Библиотека Pandas использует Numpy в качестве зависимости и реализует его DataFrame
тип поверх типа массива Numpy . Применимы все те же рассуждения, так что Pandas Series
(и DataFrame
) объекты не могут использоваться как логические: см. Значение истинности ряда неоднозначно. Используйте a.empty, a.bool(), a.item(), a.any() или a.all().
Интерфейс Pandas для решения проблемы немного сложнее - и его лучше всего понять, прочитав вопросы и ответы. Вопрос конкретно касается рядов, но логика обычно применима и к фреймам данных. Пожалуйста, также ознакомьтесь с Условием If с фреймом данных для получения более конкретных рекомендаций по применению условной логики к фрейму данных.