Come filtrare il frame di dati Pandas usando 'in' e 'not in' come in SQL


434

Come posso ottenere gli equivalenti di SQL INe NOT IN?

Ho un elenco con i valori richiesti. Ecco lo scenario:

df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

# pseudo-code:
df[df['countries'] not in countries]

Il mio modo attuale di farlo è il seguente:

df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = pd.DataFrame({'countries':['UK','China'], 'matched':True})

# IN
df.merge(countries,how='inner',on='countries')

# NOT IN
not_in = df.merge(countries,how='left',on='countries')
not_in = not_in[pd.isnull(not_in['matched'])]

Ma questo sembra un orribile kludge. Qualcuno può migliorarlo?


1
Penso che la tua soluzione sia la migliore. Il tuo può coprire IN, NOT_IN di più colonne.
Bruce Jung

Vuoi provare su una o più colonne?
smci,

1
Correlati (performance / pandas internals): Pandas pd.Series.isin performance con set versus array
jpp

Risposte:


821

È possibile utilizzare pd.Series.isin.

Per "IN" utilizzare: something.isin(somewhere)

O per "NON IN": ~something.isin(somewhere)

Come esempio funzionante:

>>> df
  countries
0        US
1        UK
2   Germany
3     China
>>> countries
['UK', 'China']
>>> df.countries.isin(countries)
0    False
1     True
2    False
3     True
Name: countries, dtype: bool
>>> df[df.countries.isin(countries)]
  countries
1        UK
3     China
>>> df[~df.countries.isin(countries)]
  countries
0        US
2   Germany

1
Solo un FYI, @LondonRob aveva il suo come DataFrame e il tuo è una serie. DataFrame è isinstato aggiunto in .13.
TomAugspurger,

Qualche suggerimento su come farlo con i panda 0.12.0? È l'attuale versione rilasciata. (Forse dovrei solo aspettare 0.13 ?!)
LondonRob

Se in realtà hai a che fare con array monodimensionali (come nel tuo esempio), in prima linea usa una serie invece di un DataFrame, come utilizzato da @DSM:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger

2
@TomAugspurger: come al solito, probabilmente mi manca qualcosa. df, sia mio che suo, è a DataFrame. countriesè un elenco. df[~df.countries.isin(countries)]produce a DataFrame, non a Series, e sembra funzionare anche in 0.11.0.dev-14a04dd.
DSM,

7
Questa risposta è confusa perché continui a riutilizzare la countriesvariabile. Bene, l'OP lo fa, ed è ereditato, ma che qualcosa è stato fatto male prima non giustifica farlo male ora.
ifly6,

63

Soluzione alternativa che utilizza il metodo .query () :

In [5]: df.query("countries in @countries")
Out[5]:
  countries
1        UK
3     China

In [6]: df.query("countries not in @countries")
Out[6]:
  countries
0        US
2   Germany

10
@LondonRob querynon è più sperimentale.
Paul Rougieux,

38

Come implementare 'in' e 'non in' per un DataFrame Panda?

Pandas offre due metodi: Series.isine rispettivamente DataFrame.isinper Series e DataFrames.


Filtra DataFrame in base a UNA colonna (vale anche per le serie)

Lo scenario più comune è l'applicazione di una isincondizione su una colonna specifica per filtrare le righe in un DataFrame.

df = pd.DataFrame({'countries': ['US', 'UK', 'Germany', np.nan, 'China']})
df
  countries
0        US
1        UK
2   Germany
3     China

c1 = ['UK', 'China']             # list
c2 = {'Germany'}                 # set
c3 = pd.Series(['China', 'US'])  # Series
c4 = np.array(['US', 'UK'])      # array

Series.isinaccetta vari tipi come input. Di seguito sono riportati tutti i modi validi per ottenere ciò che desideri:

df['countries'].isin(c1)

0    False
1     True
2    False
3    False
4     True
Name: countries, dtype: bool

# `in` operation
df[df['countries'].isin(c1)]

  countries
1        UK
4     China

# `not in` operation
df[~df['countries'].isin(c1)]

  countries
0        US
2   Germany
3       NaN

# Filter with `set` (tuples work too)
df[df['countries'].isin(c2)]

  countries
2   Germany

# Filter with another Series
df[df['countries'].isin(c3)]

  countries
0        US
4     China

# Filter with array
df[df['countries'].isin(c4)]

  countries
0        US
1        UK

Filtro su MOLTE colonne

A volte, ti consigliamo di applicare un controllo di appartenenza "in" con alcuni termini di ricerca su più colonne,

df2 = pd.DataFrame({
    'A': ['x', 'y', 'z', 'q'], 'B': ['w', 'a', np.nan, 'x'], 'C': np.arange(4)})
df2

   A    B  C
0  x    w  0
1  y    a  1
2  z  NaN  2
3  q    x  3

c1 = ['x', 'w', 'p']

Per applicare la isincondizione a entrambe le colonne "A" e "B", utilizzare DataFrame.isin:

df2[['A', 'B']].isin(c1)

      A      B
0   True   True
1  False  False
2  False  False
3  False   True

Da questo, per conservare le righe in cui si trova almeno una colonnaTrue , possiamo usare anylungo il primo asse:

df2[['A', 'B']].isin(c1).any(axis=1)

0     True
1    False
2    False
3     True
dtype: bool

df2[df2[['A', 'B']].isin(c1).any(axis=1)]

   A  B  C
0  x  w  0
3  q  x  3

Nota che se vuoi cercare in ogni colonna, dovresti semplicemente omettere il passaggio di selezione della colonna e farlo

df2.isin(c1).any(axis=1)

Allo stesso modo, per conservare le righe in cui si trovano TUTTE le colonneTrue , utilizzare allnello stesso modo di prima.

df2[df2[['A', 'B']].isin(c1).all(axis=1)]

   A  B  C
0  x  w  0

Notevole Menzioni: numpy.isin, query, list comprehension (string data)

Oltre ai metodi descritti in precedenza, è anche possibile utilizzare l'equivalente NumPy: numpy.isin.

# `in` operation
df[np.isin(df['countries'], c1)]

  countries
1        UK
4     China

# `not in` operation
df[np.isin(df['countries'], c1, invert=True)]

  countries
0        US
2   Germany
3       NaN

Perché vale la pena considerare? Le funzioni di NumPy sono in genere un po 'più veloci rispetto ai loro equivalenti panda a causa del sovraccarico inferiore. Dato che si tratta di un'operazione elementally che non dipende dall'allineamento dell'indice, ci sono pochissime situazioni in cui questo metodo non è un sostituto appropriato per i panda isin.

Le routine di Panda sono generalmente iterative quando si lavora con le stringhe, poiché le operazioni sulle stringhe sono difficili da vettorializzare. Ci sono molte prove che suggeriscono che la comprensione dell'elenco sarà più veloce qui. . Facciamo ricorso a un inassegno ora.

c1_set = set(c1) # Using `in` with `sets` is a constant time operation... 
                 # This doesn't matter for pandas because the implementation differs.
# `in` operation
df[[x in c1_set for x in df['countries']]]

  countries
1        UK
4     China

# `not in` operation
df[[x not in c1_set for x in df['countries']]]

  countries
0        US
2   Germany
3       NaN

È molto più ingombrante da specificare, tuttavia, quindi non usarlo se non sai cosa stai facendo.

Infine, c'è anche quello DataFrame.queryche è stato trattato in questa risposta . numexpr FTW!


Mi piace, ma cosa succede se voglio confrontare una colonna in df3 che è nella colonna df1? Come sarebbe?
Arthur D. Howland,

12

Di solito ho fatto il filtraggio generico su righe come questa:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]

10
Cordiali saluti, questo è molto più lento di @DSM soln che è vettorializzato
Jeff

@Jeff me lo sarei aspettato, ma è quello a cui torno quando ho bisogno di filtrare direttamente qualcosa che non è disponibile nei panda. (Stavo per dire "come .startwith o regex matching, ma ho appena scoperto di Series.str che ha tutto questo!)
Kos

7

Volevo filtrare le righe di dfbc che avevano un BUSINESS_ID che era anche nel BUSINESS_ID di dfProfilesBusIds

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]

5
Puoi negare l'isin (come fatto nella risposta accettata) piuttosto che confrontarlo con False
OneCricketeer

6

Raccogliere possibili soluzioni dalle risposte:

Per IN: df[df['A'].isin([3, 6])]

Per NON IN:

  1. df[-df["A"].isin([3, 6])]

  2. df[~df["A"].isin([3, 6])]

  3. df[df["A"].isin([3, 6]) == False]

  4. df[np.logical_not(df["A"].isin([3, 6]))]


3
Questo per lo più ripete le informazioni da altre risposte. L'uso logical_notè un equivalente orale ~dell'operatore.
cs95

3
df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

implementare in :

df[df.countries.isin(countries)]

implementare non come in dei paesi di riposo:

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
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.