Cómo filtrar el marco de datos de Pandas usando 'in' y 'not in' como en SQL

434

¿Cómo puedo lograr los equivalentes de SQL INy NOT IN?

Tengo una lista con los valores requeridos. Aquí está el escenario:

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

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

Mi forma actual de hacer esto es la siguiente:

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

Pero esto parece un horrible error. ¿Alguien puede mejorarlo?

LondonRob
fuente
1
Creo que tu solución es la mejor solución. La suya puede cubrir IN, NOT_IN de varias columnas.
Bruce Jung
¿Quieres probar en una sola columna o en varias columnas?
smci
1
Relacionado (rendimiento / aspectos internos de pandas): rendimiento de Pandas pd.Series.isin con conjunto versus matriz
jpp

Respuestas:

821

Puedes usar pd.Series.isin.

Para uso "IN": something.isin(somewhere)

O para "NO EN": ~something.isin(somewhere)

Como ejemplo trabajado:

>>> 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
DSM
fuente
1
Solo para su información, @LondonRob tenía el suyo como DataFrame y el suyo es una Serie. DataFrame isinse agregó en .13.
TomAugspurger
¿Alguna sugerencia sobre cómo hacer esto con pandas 0.12.0? Es la versión actual lanzada. (¡¿Tal vez debería esperar a 0.13 ?!)
LondonRob
Si realmente está tratando con matrices unidimensionales (como en su ejemplo), entonces en su primera línea use una Serie en lugar de un Marco de Datos, como @DSM:df = pd.Series({'countries':['US','UK','Germany','China']})
TomAugspurger
2
@TomAugspurger: como de costumbre, probablemente me falta algo. df, tanto el mío como el suyo, es a DataFrame. countrieses una lista df[~df.countries.isin(countries)]produce a DataFrame, no a Series, y parece funcionar incluso en 0.11.0.dev-14a04dd.
DSM
77
Esta respuesta es confusa porque sigue reutilizando la countriesvariable. Bueno, el OP lo hace, y eso se hereda, pero que algo se haga mal antes no justifica hacerlo mal ahora.
ifly6
63

Solución alternativa que utiliza el método .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
MaxU
fuente
10
@LondonRob queryya no es experimental.
Paul Rougieux
39

¿Cómo implementar 'en' y 'no en' para un DataFrame de pandas?

Pandas ofrece dos métodos: Series.isiny DataFrame.isinpara Series y DataFrames, respectivamente.


Filtrar marco de datos basado en UNA columna (también se aplica a la serie)

El escenario más común es aplicar una isincondición en una columna específica para filtrar filas en 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.isinacepta varios tipos como entradas. Las siguientes son formas válidas de obtener lo que desea:

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

Filtrar en MUCHAS columnas

A veces, querrá aplicar una verificación de membresía 'in' con algunos términos de búsqueda en varias columnas,

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']

Para aplicar la isincondición a ambas columnas "A" y "B", use DataFrame.isin:

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

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

A partir de esto, para retener filas donde está al menos una columnaTrue , podemos usar a lo anylargo del primer eje:

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

Tenga en cuenta que si desea buscar en cada columna, simplemente omita el paso de selección de columna y haga

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

Del mismo modo, para retener las filas donde están TODAS las columnasTrue , úselas allde la misma manera que antes.

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

   A  B  C
0  x  w  0

Notable Menciones: numpy.isin, query, listas por comprensión (datos de cadena)

Además de los métodos descritos anteriormente, también se puede utilizar el 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

¿Por qué vale la pena considerarlo? Las funciones NumPy suelen ser un poco más rápidas que sus equivalentes pandas debido a una menor sobrecarga. Dado que esta es una operación de elementos que no depende de la alineación del índice, hay muy pocas situaciones en las que este método no sea un reemplazo apropiado para los pandas ' isin.

Las rutinas de pandas suelen ser iterativas cuando se trabaja con cadenas, porque las operaciones de cadena son difíciles de vectorizar. Hay mucha evidencia que sugiere que las comprensiones de listas serán más rápidas aquí. . Recurrimos a un incheque ahora.

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

Sin embargo, es mucho más difícil de especificar, así que no lo use a menos que sepa lo que está haciendo.

Por último, también hay algo DataFrame.queryque se ha cubierto en esta respuesta . numexpr FTW!

cs95
fuente
Me gusta, pero ¿qué pasa si quiero comparar una columna en df3 que está en la columna df1? Como se veria eso?
Arthur D. Howland el
12

Por lo general, he estado haciendo filtros genéricos sobre filas como esta:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
Kos
fuente
10
FYI, esto es mucho más lento que la solución @DSM que está vectorizada
Jeff
@ Jeff esperaría eso, pero a eso recurro cuando necesito filtrar directamente sobre algo que no está disponible en pandas. (Estaba a punto de decir "como .startwith o regex matching, ¡pero acabo de enterarme de Series.str que tiene todo eso!)
Kos
7

Quería filtrar las filas dfbc que tenían un BUSINESS_ID que también estaba en el BUSINESS_ID de dfProfilesBusIds

dfbc = dfbc[~dfbc['BUSINESS_ID'].isin(dfProfilesBusIds['BUSINESS_ID'])]
Sam Henderson
fuente
55
Puede negar el isin (como se hace en la respuesta aceptada) en lugar de compararlo con False
OneCricketeer
6

Recopilando posibles soluciones a partir de las respuestas:

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

Para NO EN:

  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]))]

Abhishek Gaur
fuente
3
Esto repite principalmente la información de otras respuestas. Usar logical_notes un equivalente bocado del ~operador.
cs95
3
df = pd.DataFrame({'countries':['US','UK','Germany','China']})
countries = ['UK','China']

implementar en :

df[df.countries.isin(countries)]

implementar no como en el resto de países:

df[df.countries.isin([x for x in np.unique(df.countries) if x not in countries])]
Ioannis Nasios
fuente