Истинная ценность Серии неоднозначна. Используйте 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')
Давайте поговорим о способах выхода из скобок в первой ситуации.
- Используйте математические функции Pandas
Pandas определил множество математических функций, включая сравнение, следующим образом:
pandas.Series.lt()
менее чем ;pandas.Series.gt()
для больше, чем ;pandas.Series.le()
за меньшее и равное ;pandas.Series.ge()
для большего и равного ;pandas.Series.ne()
для не равных ;pandas.Series.eq()
для равных ;
В результате вы можете использовать
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)]
- Использовать
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')]
, на который ссылались ранее, содержит главуДокумент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')
оценивает строку, описывающую операции над столбцами 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]
В моем случае у меня возникла ошибка значения типа, из-за которой возникла эта ошибка. Убедитесь, что оператору сравнения присвоен тот же элемент типа данных для сравнения.