pandas: несколько условий при индексировании фрейма данных - неожиданное поведение

135

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

По какой-то причине оператор ИЛИ ведет себя так, как я ожидал, что оператор И будет вести себя, и наоборот.

Мой тестовый код:

import pandas as pd

df = pd.DataFrame({'a': range(5), 'b': range(5) })

# let's insert some -1 values
df['a'][1] = -1
df['b'][1] = -1
df['a'][3] = -1
df['b'][4] = -1

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a != -1) | (df.b != -1)]

print pd.concat([df, df1, df2], axis=1,
                keys = [ 'original df', 'using AND (&)', 'using OR (|)',])

И результат:

      original df      using AND (&)      using OR (|)    
             a  b              a   b             a   b
0            0  0              0   0             0   0
1           -1 -1            NaN NaN           NaN NaN
2            2  2              2   2             2   2
3           -1  3            NaN NaN            -1   3
4            4 -1            NaN NaN             4  -1

[5 rows x 6 columns]

Как видите, ANDоператор отбрасывает каждую строку, в которой хотя бы одно значение равно -1. С другой стороны, ORоператор требует, чтобы оба значения были равны, -1чтобы отбросить их. Я ожидал прямо противоположного результата. Кто-нибудь может объяснить такое поведение, пожалуйста?

Я использую pandas 0.13.1.

Войцех Вальчак
источник
1
df.queryи pd.evalкажутся хорошими для этого варианта использования. Для получения информации о pd.eval()семействе функций, их возможностях и вариантах использования посетите страницу Dynamic Expression Evaluation в pandas, используя pd.eval () .
cs95

Ответы:

211

Как видите, оператор AND удаляет каждую строку, в которой хотя бы одно значение равно -1. С другой стороны, оператор OR требует, чтобы оба значения были равны -1, чтобы отбросить их.

Это правильно. Помните, что вы пишете условие в терминах того, что вы хотите сохранить , а не в терминах того, что вы хотите отказаться. Для df1:

df1 = df[(df.a != -1) & (df.b != -1)]

Вы говорите «сохранить строки, в которых df.aне -1 и df.bне -1», что равносильно удалению каждой строки, в которой хотя бы одно значение равно -1.

Для df2:

df2 = df[(df.a != -1) | (df.b != -1)]

Вы говорите «сохранить строки, в которых одно df.aили df.bне равно -1», что равносильно удалению строк, в которых оба значения равны -1.

PS: цепной доступ вроде df['a'][1] = -1может доставить вам неприятности. Лучше выработать привычку использовать.loc и .iloc.

DSM
источник
24
DataFrame.query()здесь тоже хорошо работает. df.query('a != -1 or b != -1'),
Филлип Клауд
6
Случись узнать, почему панды хотят &и |снова andи снова or?
печи
3
@stoves: в обычном коде Python andи orимеет базовую семантику Python, которую нельзя изменить. &и |, с другой стороны, имеют соответствующие специальные методы, которые контролируют их поведение. (В строках запроса, конечно, мы можем применять любой анализ, который нам нравится.)
DSM
интересно, похоже, что это df[True & False]не удается, но df[(True) & (False)]успешно (не проверено на этом примере)
3pitt
1
Можно ли разбить такой синтаксис на несколько строк? Какой был бы самый PEP8?
tommy.carstensen
42

Вы можете использовать query () , то есть:

df_filtered = df.query('a == 4 & b != 2')
CONvid19
источник
У меня есть ситуация, когда я думаю, что этот синтаксис имеет больше смысла, например: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10
9

Немного теории математической логики :

«НЕ а И НЕ b» означает «НЕ (а ИЛИ b)» , поэтому:

«a НЕ -1 И b НЕ -1» эквивалентно «НЕ (а равно -1 ИЛИ b равно -1)» , что противоположно (дополнению) «(а равно -1 ИЛИ b равно -1)» .

Поэтому, если вам нужен прямо противоположный результат, df1 и df2 должны быть такими, как показано ниже:

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
Джейк
источник