pandas: várias condições durante a indexação do quadro de dados - comportamento inesperado

134

Estou filtrando linhas em um dataframe por valores em duas colunas.

Por alguma razão, o operador OR se comporta como eu esperaria que o operador AND se comportasse e vice-versa.

Meu código de teste:

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 (|)',])

E o resultado:

      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]

Como você pode ver, o ANDoperador descarta todas as linhas nas quais pelo menos um valor é igual -1. Por outro lado, o ORoperador exige que ambos os valores sejam iguais -1para descartá-los. Eu esperaria exatamente o resultado oposto. Alguém poderia explicar esse comportamento, por favor?

Estou usando pandas 0.13.1.

Wojciech Walczak
fonte
1
df.querye pd.evalparece ser um bom ajuste para este caso de uso. Para obter informações sobre a pd.eval()família de funções, seus recursos e casos de uso, visite Avaliação de Expressão Dinâmica em pandas usando pd.eval () .
cs95

Respostas:

210

Como você pode ver, o operador AND descarta todas as linhas nas quais pelo menos um valor é igual a -1. Por outro lado, o operador OR exige que ambos os valores sejam iguais a -1 para eliminá-los.

Está certo. Lembre-se de que você está escrevendo a condição em termos do que deseja manter , não em termos do que deseja eliminar. Para df1:

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

Você está dizendo "mantenha as linhas em que df.anão seja -1 e df.bnão seja -1", o mesmo que eliminar todas as linhas em que pelo menos um valor seja -1.

Para df2:

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

Você está dizendo "mantenha as linhas em que é df.aou df.bnão -1", que é o mesmo que soltar linhas em que os dois valores são -1.

PS: acesso encadeado como df['a'][1] = -1pode causar problemas. É melhor adquirir o hábito de usar .loce .iloc.

DSM
fonte
24
DataFrame.query()funciona bem aqui também. df.query('a != -1 or b != -1').
Phillip Cloud
4
Por acaso sabe por que pandas quer &e |sobre ande or?
fogões
2
@ fogões: no código Python normal, ande orsemântica básica do Python que não pode ser modificada. &e |, por outro lado, possuem métodos especiais correspondentes que controlam seu comportamento. (Em cadeias de consulta, é claro, somos livres para aplicar qualquer análise que desejar.) #
313 DSM do DSM
curiosamente, parece que df[True & False]falha, mas df[(True) & (False)]é bem-sucedido (não testado neste exemplo) #
3pitt 15/02/18
Seria possível quebrar esse tipo de sintaxe em várias linhas? O que seria mais PEP8?
tommy.carstensen
41

Você pode usar query () , ou seja:

df_filtered = df.query('a == 4 & b != 2')
CONvid19
fonte
Eu tenho uma situação em que acho que essa sintaxe faz mais sentido, por exemplo: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10 28/02
9

Um pouco de teoria da lógica matemática aqui:

"NOT a AND NOT b" é igual a "NOT (a OR b)" , portanto:

"a NOT -1 AND b NOT -1" é equivalente a "NOT (a é -1 OR b é -1)" , que é o oposto (Complemento) de "(a é -1 OR b é -1)" .

Portanto, se você deseja um resultado exatamente oposto, df1 e df2 devem ser os seguintes:

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