Cómo filtrar el marco de datos de Pandas usando 'en' y 'no en' como en SQL
¿ Cómo puedo lograr los equivalentes de SQL IN
y 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?
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
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
¿Cómo implementar 'dentro' y 'no dentro' para un DataFrame de pandas?
Pandas ofrece dos métodos: Series.isin
y DataFrame.isin
para 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 isin
condició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.isin
Acepta 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 isin
condició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 any
a 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 all
de 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 in
cheque 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.query
ha cubierto en esta respuesta . númeroexpr FTW!
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)]
Recopilando posibles soluciones a partir de las respuestas:
Para EN:df[df['A'].isin([3, 6])]
Para NO EN:
df[-df["A"].isin([3, 6])]
df[~df["A"].isin([3, 6])]
df[df["A"].isin([3, 6]) == False]
df[np.logical_not(df["A"].isin([3, 6]))]