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

Resuelto LondonRob asked hace 11 años • 12 respuestas

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

Tengo una lista con los valores requeridos. Este es el escenario:

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

# pseudo-code:
df[df['country'] not in countries_to_keep]

Mi forma actual de hacer esto es la siguiente:

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

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

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

Pero esto parece una torpeza horrible. ¿Alguien puede mejorarlo?

LondonRob avatar Nov 14 '13 00:11 LondonRob
Aceptado

Puedes usar pd.Series.isin.

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

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

Como ejemplo trabajado:

>>> df
    country
0        US
1        UK
2   Germany
3     China
>>> countries_to_keep
['UK', 'China']
>>> df.country.isin(countries_to_keep)
0    False
1     True
2    False
3     True
Name: country, dtype: bool
>>> df[df.country.isin(countries_to_keep)]
    country
1        UK
3     China
>>> df[~df.country.isin(countries_to_keep)]
    country
0        US
2   Germany
DSM avatar Nov 13 '2013 17:11 DSM

Solución alternativa que utiliza el método .query() :

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

In [6]: df.query("country not in @countries_to_keep")
Out[6]:
  countries
0        US
2   Germany
MaxU - stand with Ukraine avatar Jul 19 '2017 12:07 MaxU - stand with Ukraine

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

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


Filtrar marco de datos según 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 todas formas válidas de conseguir 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ás 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", utilice 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 anya lo largo 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 todas las columnas, simplemente omitirá el paso de selección de columnas y hará

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

De manera similar, para conservar las filas donde están TODAS las columnasTrue , utilícelas allde la misma manera que antes.

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

   A  B  C
0  x  w  0

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

Además de los métodos descritos anteriormente, también puedes 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 se trata de una operación por 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 pandas isin.

Las rutinas de Pandas suelen ser iterativas cuando se trabaja con cadenas, porque las operaciones con cadenas son difíciles de vectorizar. Hay mucha evidencia que sugiere que la comprensión de listas será más rápida 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 manejar, así que no lo uses a menos que sepas lo que estás haciendo.

Por último, también se DataFrame.queryha cubierto en esta respuesta . númeroexpr FTW!

cs95 avatar Apr 07 '2019 00:04 cs95

Normalmente he estado haciendo filtros genéricos en filas como esta:

criterion = lambda row: row['countries'] not in countries
not_in = df[df.apply(criterion, axis=1)]
Kos avatar Nov 13 '2013 17:11 Kos

Recopilando posibles soluciones a partir de las respuestas:

Para EN: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 avatar Jun 01 '2019 15:06 Abhishek Gaur