Значение истины серии неоднозначно. Используйте команды a.empty, a.bool(), a.item(), a.any() или a.all()

Возникла проблема фильтрации моего результирующего фрейма с условием or. Я хочу, чтобы мой результат df извлекал все значения столбца _var_, которые выше 0,25 и ниже -0.25. Эта логика ниже дает мне двусмысленное значение истины, однако оно работает, когда я разделяю эту фильтрацию в двух отдельных операциях. Что здесь происходит? не знаете, где использовать предлагаемые функции a.empty(), a.bool(), a.item(),a.any() or a.all().

 result = result[(result['var']>0.25) or (result['var']<-0.25)]

Ответ 1

Операторы python or и and требуют truth -значений. Для pandas они считаются неоднозначными, поэтому вы должны использовать "поразрядные" операции | (или) или & (и):

result = result[(result['var']>0.25) | (result['var']<-0.25)]

Они перегружены для этих типов данных, чтобы получить элемент-мудрый or (или and).


Чтобы добавить еще одно объяснение к этому утверждению:

Исключение генерируется, если вы хотите получить bool pandas.Series:

>>> import pandas as pd
>>> x = pd.Series([1])
>>> bool(x)
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

То, что вы нажали, было местом, где оператор неявно преобразовал операнды в bool (вы использовали or, но это также происходит для and, if и while)

>>> x or x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> x and x
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> if x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
>>> while x:
...     print('fun')
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Помимо этих 4-х операторов существует несколько функций-питонов, которые скрывают некоторые вызовы bool (например, any, all, filter,...), как правило, это не проблема с pandas.Series, а для полноты я хотел упомянуть их.


В вашем случае исключение не очень полезно, поскольку оно не упоминает альтернативные права. Для and и or вы можете использовать (если вы хотите сравнить по элементам):

  • numpy.logical_or:

    >>> import numpy as np
    >>> np.logical_or(x, y)
    

    или просто оператор |:

    >>> x | y
    
  • numpy.logical_and:

    >>> np.logical_and(x, y)
    

    или просто оператор &:

    >>> x & y
    

Если вы используете операторы, убедитесь, что вы правильно установили свою скобку из-за приоритета оператора.

Есть несколько логических функций numpy, которые должны работать на pandas.Series.


Альтернативы, упомянутые в Exception, более подходят, если вы столкнулись с этим при выполнении if или while. Вкратце объясню каждый из них:

  • Если вы хотите проверить, является ли ваша серия пустой:

    >>> x = pd.Series([])
    >>> x.empty
    True
    >>> x = pd.Series([1])
    >>> x.empty
    False
    

    Python обычно интерпретирует len gth контейнеров (например, list, tuple,...) как значение истинности, если он не имеет явной булевой интерпретации. Поэтому, если вы хотите выполнить проверку типа python, вы можете сделать: if x.size или if not x.empty вместо if x.

  • Если ваш Series содержит одно и только одно логическое значение:

    >>> x = pd.Series([100])
    >>> (x > 50).bool()
    True
    >>> (x < 50).bool()
    False
    
  • Если вы хотите проверить первый и единственный элемент вашей серии (например, .bool(), но работает даже для не логического содержимого):

    >>> x = pd.Series([100])
    >>> x.item()
    100
    
  • Если вы хотите проверить, нет ли all или любого элемента, не равного нулю, непустого или не-False:

    >>> x = pd.Series([0, 1, 2])
    >>> x.all()   # because one element is zero
    False
    >>> x.any()   # because one (or more) elements are non-zero
    True
    

Ответ 2

Для логической логики используйте & и |.

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,3), columns=list('ABC'))

>>> df
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
2  0.950088 -0.151357 -0.103219
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

>>> df.loc[(df.C > 0.25) | (df.C < -0.25)]
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

Чтобы узнать, что происходит, вы получите столбец логических значений для каждого сравнения, например.

df.C > 0.25
0     True
1    False
2    False
3     True
4     True
Name: C, dtype: bool

Если у вас есть несколько критериев, вы получите несколько столбцов. Вот почему логика объединения неоднозначна. Использование and или or обрабатывает каждый столбец отдельно, поэтому вам сначала нужно свести этот столбец к одному логическому значению. Например, чтобы убедиться, что любое значение или все значения в каждом столбце True.

# Any value in either column is True?
(df.C > 0.25).any() or (df.C < -0.25).any()
True

# All values in either column is True?
(df.C > 0.25).all() or (df.C < -0.25).all()
False

Один сложный способ добиться того же самого - это объединить все эти столбцы и выполнить соответствующую логику.

>>> df[[any([a, b]) for a, b in zip(df.C > 0.25, df.C < -0.25)]]
          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.443863

Подробнее см. Булево индексирование в документах.

Ответ 3

Или, альтернативно, вы можете использовать модуль Operator. Более подробная информация здесь. Документы Python

import operator
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,3), columns=list('ABC'))
df.loc[operator.or_(df.C > 0.25, df.C < -0.25)]

          A         B         C
0  1.764052  0.400157  0.978738
1  2.240893  1.867558 -0.977278
3  0.410599  0.144044  1.454274
4  0.761038  0.121675  0.4438

Ответ 4

Этот отличный ответ очень хорошо объясняет, что происходит и дает решение. Я хотел бы добавить еще одно решение, которое может быть подходящим в подобных случаях: с помощью метода query:

result = result.query("(var > 0.25) or (var < -0.25)")

См. также http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-query.

(Некоторые тесты с фреймворком данных, с которым я работаю в настоящее время, предполагают, что этот метод немного медленнее, чем использование побитовых операторов в серии логических: 2 мс против 870 мкс)

Предупреждение. По крайней мере, одна ситуация, когда это непросто, - это когда имена столбцов являются выражениями python. Я имел столбцы с именем WT_38hph_IP_2, WT_38hph_input_2 и log2(WT_38hph_IP_2/WT_38hph_input_2) и хотел выполнить следующий запрос: "(log2(WT_38hph_IP_2/WT_38hph_input_2) > 1) and (WT_38hph_IP_2 > 20)"

Я получил следующий каскад исключения:

  • KeyError: 'log2'
  • UndefinedVariableError: name 'log2' is not defined
  • ValueError: "log2" is not a supported function

Я предполагаю, что это произошло потому, что анализатор запросов пытался сделать что-то из первых двух столбцов вместо того, чтобы идентифицировать выражение с именем третьего столбца.

Предлагается возможное обходное решение здесь.