Panda: condizioni multiple durante l'indicizzazione del frame di dati - comportamento imprevisto


135

Sto filtrando le righe in un dataframe per valori in due colonne.

Per qualche motivo l'operatore OR si comporta come mi aspetterei che l'operatore AND si comportasse e viceversa.

Il mio codice di prova:

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 il risultato:

      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]

Come puoi vedere, l' ANDoperatore rilascia ogni riga in cui almeno un valore è uguale -1. D'altra parte, l' ORoperatore richiede che entrambi i valori siano uguali a -1per eliminarli. Mi aspetto esattamente il risultato opposto. Qualcuno potrebbe spiegare questo comportamento, per favore?

Sto usando i panda 0.13.1.


1
df.querye pd.evalsembrano buoni per questo caso d'uso. Per informazioni sulla pd.eval()famiglia di funzioni, le loro caratteristiche e i casi d'uso, visita la pagina Valutazione delle espressioni dinamiche nei panda utilizzando pd.eval () .
cs95

Risposte:


211

Come puoi vedere, l'operatore AND rilascia ogni riga in cui almeno un valore è uguale a -1. D'altra parte, l'operatore OR richiede che entrambi i valori siano uguali a -1 per eliminarli.

Giusto. Ricorda che stai scrivendo la condizione in termini di ciò che vuoi conservare , non in termini di ciò che vuoi eliminare. Per df1:

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

Stai dicendo "mantieni le righe in cui df.a non è -1 e df.bnon è -1", che equivale a rilasciare ogni riga in cui almeno un valore è -1.

Per df2:

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

Stai dicendo "mantieni le righe in cui df.aodf.b meno è -1", che equivale a rilasciare righe in cui entrambi i valori sono -1.

PS: l'accesso concatenato come df['a'][1] = -1può metterti nei guai. È meglio prendere l'abitudine di usare .loce .iloc.


24
DataFrame.query()funziona bene anche qui. df.query('a != -1 or b != -1').
Phillip Cloud

5
Capita di sapere perché i panda vogliono &e |oltre ande or?
stufe il

3
@stoves: nel normale codice Python ande orhanno una semantica Python di base che non può essere modificata. &e |, d'altra parte, hanno metodi speciali corrispondenti che controllano il loro comportamento. (Nelle stringhe di query, ovviamente, siamo liberi di applicare qualsiasi analisi che preferiamo.)
DSM

interessante, sembra che df[True & False]fallisca ma ha df[(True) & (False)]successo (non testato in questo esempio)
3pitt

Sarebbe possibile rompere questo tipo di sintassi su più righe? Quale sarebbe più PEP8?
tommy.carstensen

41

Puoi usare query () , cioè:

df_filtered = df.query('a == 4 & b != 2')

Ho una situazione in cui penso che questa sintassi abbia più senso, ad esempio: df.query ('' (a == 4 & b! = 2) | c == 3 ")
Aus_10

9

Un po 'di teoria della logica matematica qui:

"NOT a AND NOT b" equivale a "NOT (a OR b)" , quindi:

"a NOT -1 AND b NOT -1" è equivalente a "NOT (a è -1 OR b è -1)" , che è opposto a (Complemento) di "(a è -1 OR b è -1)" .

Quindi, se vuoi il risultato esatto opposto, df1 e df2 dovrebbero essere come di seguito:

df1 = df[(df.a != -1) & (df.b != -1)]
df2 = df[(df.a == -1) | (df.b == -1)]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.