Истинная ценность Серии неоднозначна. Используйте 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)]

14 ответов

Решение

or а также and операторы python требуют 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 операторов есть несколько функций Python, которые скрывают некоторые 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
    

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

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


Альтернативы, упомянутые в Исключении, больше подходят, если вы столкнулись с этим при выполнении if или же while, Я кратко объясню каждый из них:

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

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

    Python обычно интерпретирует len гт контейнеров (как 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
    
  • Если вы хотите проверить, что все или любой элемент не нулевой, не пустой или не ложный:

    >>> 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
    

Ну панды используют побитовое '&' '|' и каждое условие должно быть заключено в '()'

Например следующие работы

data_query = data[(data['year'] >= 2005) & (data['year'] <= 2010)]

Но тот же запрос без правильных скобок не

data_query = data[(data['year'] >= 2005 & data['year'] <= 2010)]

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

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 обрабатывает каждый столбец отдельно, поэтому сначала нужно уменьшить этот столбец до одного логического значения. Например, чтобы увидеть, является ли какое-либо значение или все значения в каждом из столбцов истинными.

# 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

Для получения дополнительной информации обратитесь к Булевому индексированию в документах.

Это довольно распространенный вопрос для новичков при создании нескольких условий в Pandas. Вообще говоря, есть два возможных условия, вызывающих эту ошибку:

Условие 1: приоритет оператора Python

Есть абзац булевой индексации | Индексирование и выбор данных — документация pandas объясняет это

Другой распространенной операцией является использование логических векторов для фильтрации данных. Операторы: for , for , for . Они должны быть сгруппированы с помощью круглых скобок .

По умолчанию Python будет оценивать такое выражение, как df['A'] > 2 & df['B'] < 3в качестве df['A'] > (2 & df['B']) < 3, в то время как желаемый порядок оценки (df['A'] > 2) & (df['B'] < 3).

      # Wrong
df['col'] < -0.25 | df['col'] > 0.25

# Right
(df['col'] < -0.25) | (df['col'] > 0.25)

Есть несколько возможных способов избавиться от круглых скобок, я расскажу об этом позже.


Условие 2: неправильный оператор/оператор

Как поясняется в предыдущей цитате, вам нужно использовать for , for и ~за not

      # Wrong
(df['col'] < -0.25) or (df['col'] > 0.25)

# Right
(df['col'] < -0.25) | (df['col'] > 0.25)

Другая возможная ситуация заключается в том, что вы используете логическую серию в выражении.

      # Wrong
if pd.Series([True, False]):
    pass

Понятно, что питон ifоператор принимает логическое выражение, а не серию Pandas. Вы должны использоватьpandas.Series.anyили методы, перечисленные в сообщении об ошибке, для преобразования Series в значение в соответствии с вашими потребностями.

Например:

      # Right
if df['col'].eq(0).all():
    # If you want all column values equal to zero
    print('do something')

# Right
if df['col'].eq(0).any():
    # If you want at least one column value equal to zero
    print('do something')

Давайте поговорим о способах выхода из скобок в первой ситуации.

  1. Используйте математические функции Pandas

Pandas определил множество математических функций, включая сравнение, следующим образом:

В результате вы можете использовать

      df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]

# is equal to

df = df[df['col'].lt(-0.25) | df['col'].gt(0.25)]
  1. Использоватьpandas.Series.between()

Если вы хотите выбрать строки между двумя значениями, вы можете использовать pandas.Series.between

  • df['col].between(left, right)равно
    (left <= df['col']) & (df['col'] <= right);
  • df['col].between(left, right, inclusive='left)равно
    (left <= df['col']) & (df['col'] < right);
  • df['col].between(left, right, inclusive='right')равно
    (left < df['col']) & (df['col'] <= right);
  • df['col].between(left, right, inclusive='neither')равно
    (left < df['col']) & (df['col'] < right);
      df = df[(df['col'] > -0.25) & (df['col'] < 0.25)]

# is equal to

df = df[df['col'].between(-0.25, 0.25, inclusive='neither')]
  1. Использовать

, на который ссылались ранее, содержит главуДокументquery()Метод хорошо это объясняет.

может помочь вам выбрать DataFrame со строкой условия. В строке запроса вы можете использовать оба побитовых оператора ( &а также |) и их логические родственники( andа также or). Кроме того, вы можете опустить скобки, но я не рекомендую по понятной причине.

      df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]

# is equal to

df = df.query('col < -0.25 or col > 0.25')
  1. Использовать

оценивает строку, описывающую операции над столбцами DataFrame. Таким образом, мы можем использовать этот метод для построения нашего множественного условия. Синтаксис такой же, как и у .

      df = df[(df['col'] < -0.25) | (df['col'] > 0.25)]

# is equal to

df = df[df.eval('col < -0.25 or col > 0.25')]

pandas.DataFrame.query()а также pandas.DataFrame.eval()могут делать больше вещей, чем я здесь описываю, вам рекомендуется прочитать их документацию и повеселиться с ними.

Или, в качестве альтернативы, вы можете использовать модуль оператора. Более подробная информация здесь 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

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

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

Смотрите также http://pandas.pydata.org/pandas-docs/stable/indexing.html.

(Некоторые тесты с фреймом данных, с которым я сейчас работаю, предполагают, что этот метод немного медленнее, чем использование побитовых операторов в логических сериях: 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

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

Возможный обходной путь предлагается здесь.

Пытаться
result['var'].all() если у вас более 1 значения. Если это единственное значение, вы можете использовать result['var'].item()

Я получал ошибку в этой команде:

если df! = '': пройти

Но это сработало, когда я изменил его на это:

если df не '': пройти

Я столкнулся с той же проблемой, работая в кадре данных Panda.

Я использовал: :

Здесь я пытаюсь выбрать строку с идентификатором, совпадающим с41d7853и Degreee_type не сCertification.

Как показано ниже:

      display(df_degrees.loc[np.logical_and(df_degrees['person_id'] == '41d7853' , df_degrees['degree_type'] !='Certification')])

Если я попытаюсь написать код, как показано ниже:

      display(df_degrees.loc[df_degrees['person_id'] == '41d7853' and df_degrees['degree_type'] !='Certification'])

Мы получим ошибку:

      ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().

Я использовал numpy.logical_andnumpy.logical_, и это сработало для меня.

Вам нужно использовать побитовые операторы | вместо того or а также & вместо того andв пандах вы не можете просто использовать операторы bool из python.

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

mask = (df["col1"]>=df["col2"]) & (stock["col1"]<=df["col2"])
df_new = df[mask]

Я попытаюсь дать оценку трех наиболее распространенных способов (также упомянутых выше):

from timeit import repeat

setup = """
import numpy as np;
import random;
x = np.linspace(0,100);
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) * (x <= ub)]', 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'

for _ in range(3):
    for stmt in stmts:
        t = min(repeat(stmt, setup, number=100_000))
        print('%.4f' % t, stmt)
    print()

результат:

0.4808 x[(x > lb) * (x <= ub)]
0.4726 x[(x > lb) & (x <= ub)]
0.4904 x[np.logical_and(x > lb, x <= ub)]

0.4725 x[(x > lb) * (x <= ub)]
0.4806 x[(x > lb) & (x <= ub)]
0.5002 x[np.logical_and(x > lb, x <= ub)]

0.4781 x[(x > lb) * (x <= ub)]
0.4336 x[(x > lb) & (x <= ub)]
0.4974 x[np.logical_and(x > lb, x <= ub)]

Но, * не поддерживается в Panda Series, а массив NumPy быстрее, чем кадр данных pandas (примерно в 1000 раз медленнее, см. число):

from timeit import repeat

setup = """
import numpy as np;
import random;
import pandas as pd;
x = pd.DataFrame(np.linspace(0,100));
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
"""
stmts = 'x[(x > lb) & (x <= ub)]', 'x[np.logical_and(x > lb, x <= ub)]'

for _ in range(3):
    for stmt in stmts:
        t = min(repeat(stmt, setup, number=100))
        print('%.4f' % t, stmt)
    print()

результат:

0.1964 x[(x > lb) & (x <= ub)]
0.1992 x[np.logical_and(x > lb, x <= ub)]

0.2018 x[(x > lb) & (x <= ub)]
0.1838 x[np.logical_and(x > lb, x <= ub)]

0.1871 x[(x > lb) & (x <= ub)]
0.1883 x[np.logical_and(x > lb, x <= ub)]

Примечание: добавление одной строки кода x = x.to_numpy() потребуется около 20 мкс.

Для тех, кто предпочитает %timeit:

import numpy as np
import random
lb, ub = np.sort([random.random() * 100, random.random() * 100]).tolist()
lb, ub
x = pd.DataFrame(np.linspace(0,100))

def asterik(x):
    x = x.to_numpy()
    return x[(x > lb) * (x <= ub)]

def and_symbol(x):
    x = x.to_numpy()
    return x[(x > lb) & (x <= ub)]

def numpy_logical(x):
    x = x.to_numpy()
    return x[np.logical_and(x > lb, x <= ub)]

for i in range(3):
    %timeit asterik(x)
    %timeit and_symbol(x)
    %timeit numpy_logical(x)
    print('\n')

результат:

23 µs ± 3.62 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
35.6 µs ± 9.53 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
31.3 µs ± 8.9 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)


21.4 µs ± 3.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
21.9 µs ± 1.02 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
21.7 µs ± 500 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)


25.1 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
36.8 µs ± 18.3 µs per loop (mean ± std. dev. of 7 runs, 100000 loops each)
28.2 µs ± 5.97 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Я столкнулся с той же ошибкой и на несколько дней застрял с фреймворком pyspark, я смог успешно разрешить ее, заполнив значения na 0, поскольку я сравнивал целые значения из 2 полей.

Одна мелочь, которая зря потратила мое время.

Поместите условия (при сравнении с использованием " = ", "!= ") В скобки, в противном случае также возникает это исключение. Это будет работать

df[(some condition) conditional operator (some conditions)]

Это не будет

df[some condition conditional-operator some condition]

В моем случае у меня возникла ошибка значения типа, из-за которой возникла эта ошибка. Убедитесь, что оператору сравнения присвоен тот же элемент типа данных для сравнения.

Другие вопросы по тегам