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?


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

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

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!


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

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

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


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])]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.