Я работаю с логическим индексом в Pandas. Вопрос в том, почему заявление:
a[(a['some_column']==some_number) & (a['some_other_column']==some_other_number)]
работает нормально, тогда как
a[(a['some_column']==some_number) and (a['some_other_column']==some_other_number)]
выходит с ошибкой?
Пример:
a=pd.DataFrame({'x':[1,1],'y':[10,20]})
In: a[(a['x']==1)&(a['y']==10)]
Out: x y
0 1 10
In: a[(a['x']==1) and (a['y']==10)]
Out: ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
and != &
.and
Оператор в Python не может быть переопределен, в то время как&
оператор (__and__
) может. Отсюда и выбор использования&
в numpy и pandas.Ответы:
Когда ты говоришь
(a['x']==1) and (a['y']==10)
Вы неявно просите Python преобразовать
(a['x']==1)
и(a['y']==10)
в логические значения.Массивы NumPy (длиной больше 1) и объекты Pandas, такие как Series, не имеют логического значения - другими словами, они вызывают
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
при использовании в качестве логического значения. Это потому, что неясно, когда это должно быть Истина или Ложь . Некоторые пользователи могут предположить, что они истинны, если у них ненулевая длина, например список Python. Другие могут пожелать, чтобы он был Истинным, только если все его элементы Истинны. Другие могут захотеть, чтобы он был True, если какой-либо из его элементов имеет значение True.
Поскольку существует так много противоречивых ожиданий, разработчики NumPy и Pandas отказываются гадать и вместо этого вызывают ValueError.
Вместо этого вы должны быть явными, вызывая метод
empty()
,all()
или,any()
чтобы указать, какое поведение вы хотите.В этом случае, однако, похоже, что вам не нужна логическая оценка, вам нужно поэлементное логическое и. Вот что выполняет
&
бинарный оператор:(a['x']==1) & (a['y']==10)
возвращает логический массив.
Кстати, как отмечает alexpmil , скобки обязательны, так как
&
имеет более высокий приоритет операторов, чем==
. Без круглых скобокa['x']==1 & a['y']==10
будет оцениваться,a['x'] == (1 & a['y']) == 10
что, в свою очередь, эквивалентно цепному сравнению(a['x'] == (1 & a['y'])) and ((1 & a['y']) == 10)
. Это выражение формыSeries and Series
. Использованиеand
с двумя сериями снова вызовет то же,ValueError
что и выше. Поэтому круглые скобки обязательны.источник
x and y
запускало оценкуbool(x)
иbool(y)
. Python «сначала выполняет оценкуx
; еслиx
ложно, возвращается его значение; в противном случаеy
оценивается и возвращается результирующее значение». Таким образом, синтаксисx and y
нельзя использовать для элементарного логического и, поскольку может быть возвращено толькоx
илиy
. Напротив,x & y
триггерыx.__and__(y)
и__and__
метод могут быть определены так, чтобы возвращать все, что нам нравится.==
предложения являются обязательными .a['x']==1 & a['y']==10
возвращает ту же ошибку, что и в вопросе.TL; DR; Логические операторы в пандах
&
,|
и~
, и круглые скобки(...)
важны!Python это
and
,or
иnot
логические операторы предназначены для работы с скаляров. Поэтому Pandas пришлось сделать что-то лучше и переопределить побитовые операторы для достижения векторизованной (поэлементной) версии этой функциональности.Итак, следующее в python (
exp1
иexp2
это выражения, которые оценивают логический результат) ...exp1 and exp2 # Logical AND exp1 or exp2 # Logical OR not exp1 # Logical NOT
... переведем на ...
exp1 & exp2 # Element-wise logical AND exp1 | exp2 # Element-wise logical OR ~exp1 # Element-wise logical NOT
для панд.
Если в процессе выполнения логической операции вы получите a
ValueError
, то для группировки нужно использовать круглые скобки:Например,
(df['col1'] == x) & (df['col2'] == y)
И так далее.
Логическое индексирование . Распространенной операцией является вычисление логических масок с помощью логических условий для фильтрации данных. Pandas предоставляет три оператора:
&
для логического И,|
для логического ИЛИ и~
для логического НЕ.Рассмотрим следующую схему:
np.random.seed(0) df = pd.DataFrame(np.random.choice(10, (5, 3)), columns=list('ABC')) df A B C 0 5 0 3 1 3 7 9 2 3 5 2 3 4 7 6 4 8 8 1
Логическое И
Как
df
указано выше, скажем, вы хотите вернуть все строки, где A <5 и B> 5. Это делается путем вычисления масок для каждого условия отдельно и их AND.Перегруженный побитовый
&
операторПрежде чем продолжить, обратите внимание на этот конкретный отрывок из документации, в котором говорится
Итак, имея это в виду, поэлементное логическое И может быть реализовано с помощью побитового оператора
&
:df['A'] < 5 0 False 1 True 2 True 3 True 4 False Name: A, dtype: bool df['B'] > 5 0 False 1 True 2 False 3 True 4 True Name: B, dtype: bool
(df['A'] < 5) & (df['B'] > 5) 0 False 1 True 2 False 3 True 4 False dtype: bool
И последующий шаг фильтрации просто,
df[(df['A'] < 5) & (df['B'] > 5)] A B C 1 3 7 9 3 4 7 6
Скобки используются для переопределения установленного по умолчанию порядка приоритета побитовых операторов, которые имеют более высокий приоритет над условными операторами
<
и>
. См. Раздел « Приоритет операторов» в документации по Python.Если вы не используете круглые скобки, выражение оценивается неправильно. Например, если вы случайно попытаетесь сделать что-то вроде
df['A'] < 5 & df['B'] > 5
Он разбирается как
df['A'] < (5 & df['B']) > 5
Что становится,
df['A'] < something_you_dont_want > 5
Что становится (см. Документацию python о сравнении связанных операторов ),
(df['A'] < something_you_dont_want) and (something_you_dont_want > 5)
Что становится,
# Both operands are Series... something_else_you_dont_want1 and something_else_you_dont_want2
Которая бросает
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Так что не делайте этой ошибки! 1
Как избежать группировки в круглых скобках Исправить
на самом деле довольно просто. У большинства операторов есть соответствующий метод привязки для DataFrames. Если отдельные маски созданы с использованием функций вместо условных операторов, вам больше не нужно будет группировать их по скобкам, чтобы указать порядок оценки:
df['A'].lt(5) 0 True 1 True 2 True 3 True 4 False Name: A, dtype: bool df['B'].gt(5) 0 False 1 True 2 False 3 True 4 True Name: B, dtype: bool
df['A'].lt(5) & df['B'].gt(5) 0 False 1 True 2 False 3 True 4 False dtype: bool
См. Раздел « Гибкие сравнения». . Подводя итог, у нас есть
╒════╤════════════╤════════════╕ │ │ Operator │ Function │ ╞════╪════════════╪════════════╡ │ 0 │ > │ gt │ ├────┼────────────┼────────────┤ │ 1 │ >= │ ge │ ├────┼────────────┼────────────┤ │ 2 │ < │ lt │ ├────┼────────────┼────────────┤ │ 3 │ <= │ le │ ├────┼────────────┼────────────┤ │ 4 │ == │ eq │ ├────┼────────────┼────────────┤ │ 5 │ != │ ne │ ╘════╧════════════╧════════════╛
Другой способ избежать скобок - использовать
DataFrame.query
(илиeval
):df.query('A < 5 and B > 5') A B C 1 3 7 9 3 4 7 6
Я подробно документированы
query
иeval
в динамической оценке экспрессии в панд с помощью pd.eval () .operator.and_
Позволяет выполнять эту операцию функционально. Внутренние вызовы,
Series.__and__
соответствующие побитовому оператору.import operator operator.and_(df['A'] < 5, df['B'] > 5) # Same as, # (df['A'] < 5).__and__(df['B'] > 5) 0 False 1 True 2 False 3 True 4 False dtype: bool df[operator.and_(df['A'] < 5, df['B'] > 5)] A B C 1 3 7 9 3 4 7 6
Обычно вам это не понадобится, но это полезно знать.
Обобщение:
np.logical_and
(иlogical_and.reduce
)Другой альтернативой является использование
np.logical_and
, которое также не требует группировки скобок:np.logical_and(df['A'] < 5, df['B'] > 5) 0 False 1 True 2 False 3 True 4 False Name: A, dtype: bool df[np.logical_and(df['A'] < 5, df['B'] > 5)] A B C 1 3 7 9 3 4 7 6
np.logical_and
- это ufunc (универсальные функции) , и у большинства ufunc естьreduce
метод. Это означает, что проще выполнить обобщение,logical_and
если у вас есть несколько масок для AND. Например, чтобы и маскиm1
иm2
иm3
с&
, вы должны сделатьОднако более простой вариант -
Это мощно, потому что позволяет строить поверх этого более сложную логику (например, динамически генерировать маски в понимании списка и добавлять их все):
import operator cols = ['A', 'B'] ops = [np.less, np.greater] values = [5, 5] m = np.logical_and.reduce([op(df[c], v) for op, c, v in zip(ops, cols, values)]) m # array([False, True, False, True, False]) df[m] A B C 1 3 7 9 3 4 7 6
1 - Я знаю, что настаиваю на этом, но, пожалуйста, потерпите меня. Это очень , очень распространенная ошибка новичков, и ее нужно объяснять очень тщательно.
Логическое ИЛИ
Для
df
выше, скажем , вы хотите , чтобы вернуть все строки , где A == 3 или B == 7.Перегружено побитовое
|
df['A'] == 3 0 False 1 True 2 True 3 False 4 False Name: A, dtype: bool df['B'] == 7 0 False 1 True 2 False 3 True 4 False Name: B, dtype: bool
(df['A'] == 3) | (df['B'] == 7) 0 False 1 True 2 True 3 True 4 False dtype: bool df[(df['A'] == 3) | (df['B'] == 7)] A B C 1 3 7 9 2 3 5 2 3 4 7 6
Если вы еще этого не сделали, прочтите также раздел « Логическое И» выше, здесь действуют все предостережения.
В качестве альтернативы эту операцию можно указать с помощью
df[df['A'].eq(3) | df['B'].eq(7)] A B C 1 3 7 9 2 3 5 2 3 4 7 6
operator.or_
Звонки
Series.__or__
под капотом.operator.or_(df['A'] == 3, df['B'] == 7) # Same as, # (df['A'] == 3).__or__(df['B'] == 7) 0 False 1 True 2 True 3 True 4 False dtype: bool df[operator.or_(df['A'] == 3, df['B'] == 7)] A B C 1 3 7 9 2 3 5 2 3 4 7 6
np.logical_or
Для двух условий используйте
logical_or
:np.logical_or(df['A'] == 3, df['B'] == 7) 0 False 1 True 2 True 3 True 4 False Name: A, dtype: bool df[np.logical_or(df['A'] == 3, df['B'] == 7)] A B C 1 3 7 9 2 3 5 2 3 4 7 6
Для нескольких масок используйте
logical_or.reduce
:np.logical_or.reduce([df['A'] == 3, df['B'] == 7]) # array([False, True, True, True, False]) df[np.logical_or.reduce([df['A'] == 3, df['B'] == 7])] A B C 1 3 7 9 2 3 5 2 3 4 7 6
Логическое НЕ
Учитывая маску, например
mask = pd.Series([True, True, False])
Если вам нужно инвертировать каждое логическое значение (чтобы получить конечный результат
[False, False, True]
), вы можете использовать любой из следующих методов.Побитовое
~
~mask 0 False 1 False 2 True dtype: bool
Опять же, выражения нужно заключать в круглые скобки.
~(df['A'] == 3) 0 True 1 False 2 False 3 True 4 True Name: A, dtype: bool
Это внутренне вызывает
mask.__invert__() 0 False 1 False 2 True dtype: bool
Но не используйте его напрямую.
operator.inv
Внутренне призывает
__invert__
к серии.operator.inv(mask) 0 False 1 False 2 True dtype: bool
np.logical_not
Это вариант numpy.
np.logical_not(mask) 0 False 1 False 2 True dtype: bool
Обратите внимание:
np.logical_and
можно заменить наnp.bitwise_and
,logical_or
сbitwise_or
иlogical_not
сinvert
.источник
|
, что эквивалентноnumpy.bitwise_or
, вместоnumpy.logical_or
. Могу я спросить, почему? Неnumpy.logical_or
предназначен специально для этой задачи? Зачем добавлять бремя побитовой обработки для каждой пары элементов?|
для поэлементных логических операций. Но для меня эта документация больше похожа на «учебник», и, напротив, я чувствую, что эти ссылки API ближе к источнику истины: numpy.bitwise_or и numpy.logical_or - поэтому я пытаюсь понять, что такое описано здесь.Важно понимать, что вы не можете использовать какие-либо логические операторы Python (
and
,or
илиnot
) дляpandas.Series
orpandas.DataFrame
s (аналогично вы не можете использовать их дляnumpy.array
s с более чем одним элементом). Причина, по которой вы не можете их использовать, заключается в том, что они неявно вызываютbool
свои операнды, которые вызывают исключение, потому что эти структуры данных решили, что логическое значение массива неоднозначно:>>> import numpy as np >>> import pandas as pd >>> arr = np.array([1,2,3]) >>> s = pd.Series([1,2,3]) >>> df = pd.DataFrame([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(s) ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all(). >>> bool(df) ValueError: The truth value of a DataFrame is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Я рассмотрел этот вопрос более подробно в своем ответе на вопрос «Истинное значение Серии неоднозначно. Используйте a.empty, a.bool (), a.item (), a.any () или a.all ()» Q + А .
Логические функции NumPys
Однако NumPy обеспечивает поэлементно операционные эквиваленты этих операторов как функций , которые могут быть использованы на
numpy.array
,pandas.Series
,pandas.DataFrame
, или любого другого ( в соответствии)numpy.array
подкласса:and
имеетnp.logical_and
or
имеетnp.logical_or
not
имеетnp.logical_not
numpy.logical_xor
который не имеет эквивалента в Python, но является логической операцией «исключающее ИЛИ»Так, по существу, следует использовать (при условии ,
df1
иdf2
являются панды DataFrames):Побитовые функции и побитовые операторы для логических значений
Однако, если у вас есть логический массив NumPy, серия pandas или pandas DataFrames, вы также можете использовать поэлементные побитовые функции (для логических значений они - или, по крайней мере, должны быть - неотличимы от логических функций):
np.bitwise_and
или&
операторnp.bitwise_or
или|
операторnp.invert
(или псевдонимnp.bitwise_not
) или~
операторnp.bitwise_xor
или^
операторОбычно используются операторы. Однако в сочетании с операторами сравнения нужно помнить о том, что сравнение следует заключать в круглые скобки, потому что побитовые операторы имеют более высокий приоритет, чем операторы сравнения :
(df1 < 10) | (df2 > 10) # instead of the wrong df1 < 10 | df2 > 10
Это может раздражать, потому что логические операторы Python имеют более низкий приоритет, чем операторы сравнения, поэтому вы обычно пишете
a < 10 and b > 10
(гдеa
иb
, например, простые целые числа) и не нуждаетесь в скобках.Различия между логическими и побитовыми операциями (для не-логических значений)
Очень важно подчеркнуть, что битовые и логические операции эквивалентны только для логических массивов NumPy (а также логических серий и DataFrames). Если они не содержат логических значений, тогда операции будут давать разные результаты. Я включу примеры с использованием массивов NumPy, но результаты будут аналогичными для структур данных pandas:
>>> import numpy as np >>> a1 = np.array([0, 0, 1, 1]) >>> a2 = np.array([0, 1, 0, 1]) >>> np.logical_and(a1, a2) array([False, False, False, True]) >>> np.bitwise_and(a1, a2) array([0, 0, 0, 1], dtype=int32)
И поскольку NumPy (и аналогично pandas) делает разные вещи для логических ( логические или «маскирующие» индексные массивы ) и целочисленных ( индексные массивы ) индексов, результаты индексирования также будут разными:
>>> a3 = np.array([1, 2, 3, 4]) >>> a3[np.logical_and(a1, a2)] array([4]) >>> a3[np.bitwise_and(a1, a2)] array([1, 1, 1, 2])
Таблица результатов
Где логический оператор не работает для массивов NumPy , серий pandas и DataFrames pandas. Остальные работают с этими структурами данных (и простыми объектами Python) и работают поэлементно. Однако будьте осторожны с побитовым инвертированием на простом Python,
bool
потому что bool будет интерпретироваться как целые числа в этом контексте (например,~False
return-1
и~True
return-2
).источник