pandas: múltiples condiciones al indexar el marco de datos - comportamiento inesperado

134

Estoy filtrando filas en un marco de datos por valores en dos columnas.

Por alguna razón, el operador OR se comporta como yo esperaría que el operador AND se comportara y viceversa.

Mi código de prueba:

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

Y el 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 puede ver, el ANDoperador descarta cada fila en la que al menos un valor es igual -1. Por otro lado, el ORoperador requiere que ambos valores sean iguales -1para descartarlos. Esperaría exactamente el resultado opuesto. ¿Alguien podría explicar este comportamiento, por favor?

Estoy usando pandas 0.13.1.

Wojciech Walczak
fuente
1
df.queryy pd.evalparece que encaja bien en este caso de uso. Para obtener información sobre la pd.eval()familia de funciones, sus características y casos de uso, visite Evaluación de expresión dinámica en pandas usando pd.eval () .
cs95

Respuestas:

211

Como puede ver, el operador AND descarta cada fila en la que al menos un valor es igual a -1. Por otro lado, el operador OR requiere que ambos valores sean iguales a -1 para descartarlos.

Así es. Recuerde que está escribiendo la condición en términos de lo que desea conservar , no en términos de lo que desea eliminar. Para df1:

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

Estás diciendo "mantener las filas en las que df.ano es -1 y df.bno es -1", que es lo mismo que soltar todas las filas en las que al menos un valor es -1.

Para df2:

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

Está diciendo "mantener las filas en las que df.ao df.bno es -1", que es lo mismo que soltar filas donde ambos valores son -1.

PD: el acceso encadenado como df['a'][1] = -1puede meterte en problemas. Es mejor acostumbrarse a usar .locy .iloc.

DSM
fuente
24
DataFrame.query()Funciona muy bien aquí también. df.query('a != -1 or b != -1').
Phillip Cloud
44
Pasar a saber por qué los pandas quieren &y |terminan andy or?
estufas el
2
@stoves: en código Python normal, andy ortiene una semántica básica de Python que no se puede modificar. &y |, por otro lado, tienen métodos especiales correspondientes que controlan su comportamiento. (En las cadenas de consulta, por supuesto, somos libres de aplicar cualquier análisis que deseemos).
DSM
Curiosamente, parece que df[True & False]falla pero df[(True) & (False)]tiene éxito (no probado en este ejemplo)
3pitt
¿Sería posible romper este tipo de sintaxis en varias líneas? ¿Cuál sería más PEP8?
tommy.carstensen
41

Puede usar query () , es decir:

df_filtered = df.query('a == 4 & b != 2')
CONvid19
fuente
Tengo una situación en la que creo que esta sintaxis tiene más sentido, por ejemplo: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10
9

Una pequeña teoría de la lógica matemática aquí:

"NO a Y NO b" es lo mismo que "NO (a OR b)" , entonces:

"a NO -1 Yb NO -1" es equivalente a "NO (a es -1 O b es -1)" , que es opuesto (Complemento) de "(a es -1 O b es -1)" .

Entonces, si desea un resultado exactamente opuesto, df1 y df2 deberían ser los siguientes:

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