Вопрос-Ответ

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

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 это может произойти:

Массивы и сравнения 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 SeriesDataFrame) объекты не могут использоваться как логические: см. Значение истинности ряда неоднозначно. Используйте a.empty, a.bool(), a.item(), a.any() или a.all().

Интерфейс Pandas для решения проблемы немного сложнее - и его лучше всего понять, прочитав вопросы и ответы. Вопрос конкретно касается рядов, но логика обычно применима и к фреймам данных. Пожалуйста, также ознакомьтесь с Условием If с фреймом данных для получения более конкретных рекомендаций по применению условной логики к фрейму данных.

2023-07-03 16:23 python numpy