Los objetos DataFrame de Pandas son una herramienta fantástica para trabajar con datos. Permitiendo realizar múltiples tareas de una forma rápida y sencilla. Una de las más habituales es filtrar, poder seleccionar un subconjunto de los datos en base a los valores de uno o varias columnas. En esta entrada se explicarán diferentes formas de realizar el filtrado de DataFrame con Pandas en base a los valores de las columnas.
A modo de ejemplo en esta entrada se utilizará el conjunto de datos de exoplanetas que se puede encontrar en la librería Seaborn. Para importarlo se ha de importar la librería y utilizas la función load_dataset()
como se muestra en el siguiente ejemplo
import seaborn as sb planets = sb.load_dataset('planets') planets.head()
method number orbital_period mass distance year 0 Radial Velocity 1 269.300 7.10 77.40 2006 1 Radial Velocity 1 874.774 2.21 56.95 2008 2 Radial Velocity 1 763.000 2.60 19.84 2011 3 Radial Velocity 1 326.030 19.40 110.62 2007 4 Radial Velocity 1 516.220 10.50 119.47 2009
Filtrado en base al valor de una columna
La operación básica de filtrado es seleccionar aquellos valores que cumplen una condición. Para ello se puede comparar la columna con el valor deseado y obtener una lista de valores verdaderos o falsos. Por ejemplo, en el caso de los exoplanetas se pueden seleccionar aquellos que han sido descubiertos en 2008 con el siguiente código
in_2008 = planets['year'] == 2008 in_2008.head()
0 False 1 True 2 False 3 False 4 False Name: year, dtype: bool
Para conseguir un nuevo DataFrame solamente con estos planetas simplemente se ha de usar in_2018
como filtro. Así se puede conseguir el nuevo conjunto de datos.
planets_2008 = planets[in_2008] planets_2008.head()
method number orbital_period mass distance year 1 Radial Velocity 1 874.774 2.21 56.95 2008 5 Radial Velocity 1 185.840 4.80 76.39 2008 8 Radial Velocity 1 993.300 10.30 73.10 2008 12 Radial Velocity 1 479.100 3.88 97.28 2008 27 Radial Velocity 1 952.700 5.30 97.18 2008
No es necesario guardar el filtro en una variable, simplemente se puede utilizar la operación directamente como filtro.
planets_2008 = planets[planets['year'] == 2008]
Filtrado de las columnas que no tienen un valor
De forma análoga a como se ha conseguido seleccionar los registros que cumplen una condición se puede seleccionar aquellos que no la cumple. Ahora simplemente el filtro se puede conseguir mediante el operador no igual. Así los planetas que no han sido descubiertos en 2008 se pueden obtener con el siguiente código.
planets_not_2008 = planets[planets['year'] != 2008] planets_not_2008.head()
method number orbital_period mass distance year 0 Radial Velocity 1 269.30 7.10 77.40 2006 2 Radial Velocity 1 763.00 2.60 19.84 2011 3 Radial Velocity 1 326.03 19.40 110.62 2007 4 Radial Velocity 1 516.22 10.50 119.47 2009 6 Radial Velocity 1 1773.40 4.64 18.15 2002
Filtrado en base a valores nulos
En muchos conjuntos de datos es habitual que existan registros con valores nulos. Para identificar los registros nulos objetos de Pandas cuentan con los métodos isnull()
y notnull()
. Tal como indica el nombre, el primero de ellos devuelve verdadero en los registros nulos y el segundo en los que no son nulos. Aplicando estos métodos a una columna se puede saber los registros que cumplen una condición u otra. En el ejemplo de los exoplanetas se puede filtrar aquellos en los que no se conoce la masa con el siguiente ejemplo.
planets_null = planets[planets.mass.isnull()] planets_null.head()
method number orbital_period mass distance year 7 Radial Velocity 1 798.50000 NaN 21.41 1996 20 Radial Velocity 5 0.73654 NaN 12.53 2011 25 Radial Velocity 1 116.68840 NaN 18.11 1996 26 Radial Velocity 1 691.90000 NaN 81.50 2012 29 Imaging 1 NaN NaN 45.52 2005
Por otro lado, para obtener aquellos en los que se conoce la masa se puede utilizar.
planets_not_null = planets[planets.mass.notnull()] planets_not_null.head()
method number orbital_period mass distance year 0 Radial Velocity 1 269.300 7.10 77.40 2006 1 Radial Velocity 1 874.774 2.21 56.95 2008 2 Radial Velocity 1 763.000 2.60 19.84 2011 3 Radial Velocity 1 326.030 19.40 110.62 2007 4 Radial Velocity 1 516.220 10.50 119.47 2009
Filtrados por elementos en una lista
Los filtros anteriores bastante útiles, pero en muchas ocasiones se desea seleccionar datos cuyos valores se encuentra en una lista. Para ello se puede aplicar el método isin()
a las columnas para obtener un filtro. Por ejemplo, los planetas que han sido descubiertos en 2008 y 2008 son:
planets_in_years = planets[planets.year.isin([2008, 2009])] planets_in_years.head()
method number orbital_period mass distance year 1 Radial Velocity 1 874.774 2.21 56.95 2008 4 Radial Velocity 1 516.220 10.50 119.47 2009 5 Radial Velocity 1 185.840 4.80 76.39 2008 8 Radial Velocity 1 993.300 10.30 73.10 2008 11 Radial Velocity 1 335.100 9.88 39.43 2009
Por otro lado, si se desea conocer los registros que no está en una lista mediante el operador negación ~
se puede invertir el filtro. Así los planetas que no han sido descubiertos en 2008 y 2009 son:
planets_not_in_years = planets[~planets.year.isin([2008, 2009])] planets_not_in_years.head()
method number orbital_period mass distance year 0 Radial Velocity 1 269.30 7.10 77.40 2006 2 Radial Velocity 1 763.00 2.60 19.84 2011 3 Radial Velocity 1 326.03 19.40 110.62 2007 6 Radial Velocity 1 1773.40 4.64 18.15 2002 7 Radial Velocity 1 798.50 NaN 21.41 1996
Unión de varias condiciones
Finalmente se puede combinar varios filtros mediante el uso de los operadores & (“and”) y | (“or”). Así los planetas que han sido descubiertos en 2008 o 2009 y al mismo tiempo no se conoce la masa se pueden obtener mediante el siguiente ejemplo.
planets[planets.year.isin([2008, 2009]) & planets.mass.isnull()]
method number orbital_period mass distance year 33 Imaging 1 NaN NaN NaN 2008 37 Eclipse Timing Variations 2 5767.0 NaN 130.72 2008 38 Eclipse Timing Variations 2 3321.0 NaN 130.72 2008 47 Imaging 1 6000.0 NaN 19.28 2008 68 Imaging 1 318280.0 NaN 7.69 2008
Conclusiones
En esta entrada se han visto diferentes operaciones de filtrado de DataFrame con Pandas en base a los valores de una columna. Conocer todas estas operaciones es clave para operar de forma eficiente con los objetos DataFrame de Pandas. Operaciones que complementan a otras vistas anteriormente como ordenar el contenido, comparar los registros o combinar diferentes DataFrames.
Imágenes: Pixabay (Ahmad Ardity)
Marc dice
Hola!
(Publico de nuevo el comentario, con algún ajuste. )
Gracias por la explicación. Estos filtrados son muy habituales, así que… ¡gracias!
Tengo una duda, a ver si me la podéis resolver:
Pongamos este ejemplo:
planets[planets.year.isin([1990])]
Con esto filtramos y nos quedamos con los descubiertos en el año 1990. ¿Pero y si quiero filtrar por todos los que se descubrieron durante la década de 1990? Se podrían usar booleanos (>1989 y <2000). Sin embargo, me gustaría poder aplicarlo no solo a fechas, sino también a cadenas de texto. Por ejemplo, filtrar según aparezcan ciertas palabras. Por ejemplo: gases de la atmósfera. Pongo algunos casos:
PLANETA – GASES ATMÓSFERA
Planeta 1: hidrógeno, helio, dióxido de carbono
Planeta 2: hidrógeno, helio
Planeta 3: helio
Planeta 4: dióxido de carbono
Planeta 5: helio, dióxido de carbono
Planeta 5: hidrógeno
¿Cómo podría filtrar para quedarme con aquellos planetas que contienen en su atmósfera hidrógeno (al margen del resto de gases, si los hay)? Me gustaría saber esto dado que necesito filtrar columnas en base a ciertas palabras (bastantes, debo admitir) que aparezcan en oraciones.
Muchas gracias y siento el sermón.
Un saludo!!
Daniel Rodríguez dice
Gracias por los comentarios.
Para lo que pides existe en pandas el método .str.contains() con el que se pueden obtener todos los registros de una serie que contenga una cadena de texto. Por ejemplo, en el conjunto de datos se pueden obtener todos los planetas cuyo método contiene la palabra “Velocity” con:
planets[planets['method'].str.contains('Velocity')]
Marc dice
MUCHAS GRACIAS!
Por la respuesta y la rapidez. Voy a probarlo ahora mismo.
Un saludo!!
Daniel Rodríguez dice
Gracias a ti. No he hablado nunca de estos métodos para trabajar con cadenas de texto y creo que son unos grandes desconocidos que pueden ser muy útiles. Posiblemente escriba una entrada pronto.
Marc dice
Perfecto Daniel, es un placer siempre leer tus posts.
He estado liado con lo que has enviado, hasta que he visto que str.contains no puede trabajar si hay valores nulos.
A modo de ayuda, por si alguien se encuentra con este problema, lo he resuelto así:
planets[planets[“method”].str.contains(“Radial”, na=False)]
He intentado hacerlo usando un bucle:
import pandas as pd
formula=planets[planets[“method”].str.contains(“Radial”, na=False)]
def metodos(c):
if c[“method”]==formula:
return “radial”
else:
return “neutral”
planets[“metodos”]=planets.apply(metodos,axis=1)
planets.head()
No me ha funcionado. Me salta un error que dice que The truth value of a DataFrame is ambiguous. Ya indagaré más, a ver qué hago mal.
La idea era aprender esto con algo simple, como es el caso del ejemplo, para luego usarlo de una forma más compleja agregando “elif” para cada palabra que quisiera usar para categorizar.
Gracias por todo,
¡Nos vemos por aquí!
Daniel Rodríguez dice
Por el planteamiento entiendo que lo que buscas es reemplazar los valores por otros en base a una condición. Puedes probar con algo como lo siguiente:
valid = planets["method"].str.contains("Radial", na=False)
planets["method"][valid] = 'radial'
planets["method"][~valid] = 'neutral'
Marc dice
El planteamiento no estará bien hecho… lo que quería hacer era crear una nueva columna (“metodo”, por ejemplo, que no “method”, ya existente) y añadir allí los valores “Radial” o Neutral en función de si en la columna method aparece o no la palabra “Radial” (el method que contiene “Radial” es “Radial Velocity”.
¡Aún así, muchas gracias Daniel!
alan dice
estaba buscando esta solucion hace dias, muchas gracias!
Axel Caballero dice
Hola muy útiles los ejemplos que presentas, si quisiera filtrar la base por medio de texto por ejemplo filtrar los method =”Imaging” como lo puedo hacer
Daniel Rodríguez dice
Si lo que quieres hacer es comparar cadenas de caracteres literales puedes usar el operador igualdad (==), por ejemplo para seleccionar los campos que contienen solamente el el método ‘Radial Velocity’.
planets['method'] == 'Radial Velocity'
JeanWolf dice
Hola.
El post fue muy util, en verdad gracias.
Tengo un problema. Cuando genero un filtro y lo exporto a un csv solo exporta 5 registros.
El codigo
# Filtrar por dato del campo
# filtrar exoplanetas descubiertoes entre 2008 y 2009
planets_in_years = planets[planets.year.isin([2008, 2009])]
planets_in_years.head().to_csv(“planets_in_years.csv”)
ni idea de porque pasa eso
Daniel Rodríguez dice
No sé si ya te has dado cuenta, pera antes de convertir a CSV aplicas el método head que por defecto devuelve los 5 primeros elementos.
Javier Pachón dice
Hola buenas días, gracias por las respuestas me han servido de mucho estoy filtrando por str Daniel lo explica en un comentario pero además de eso me gustaría saber como obtengo la posición de ese registro que me está retornando muchas gracias.
Daniel Rodríguez dice
No sé si lo que se desea obtener son los índices que se pueden conseguir con la propiedad .index del DataFrame resultante.
L.C.G dice
Buenas ,para filtrar varias columnas con valor positivo como se puede hacer sin tener que anotar cada una ?,probé con iloc[:,5:16] > 0 pero no funcionó
Daniel Rodríguez dice
Siguiendo los ejemplos de la entrada para obtener los planetas con masa superior a 20 se puede hacer
planets[planets.mass > 20]
lo mismo seria para seleccionar los positivos.
Gabriel dice
Hola Daniel:
Gracias por el post, me clarificó bastante. Quisiera hacer una consulta, si tengo una columna con valores y quisiera filtrar esa columna por rangos para despúes graficarla, como podría hacerlo? He probado con BETWEEN y no hay caso. En el fondo, necesito identificar cuantos valores deesa columna cumplen determinado rango para llevarlo a un agrafica.
Saludos!
Daniel Rodríguez dice
Es posible que el problema lo puedas resolver mediante la creación de tablas dinámicas: https://www.analyticslane.com/2018/11/23/tablas-dinamicas-en-python-con-pandas/
Alejandro dice
Como podría obtener el promedio de los valores de distancia si solo necesito los que están en el años 2008?
Daniel Rodríguez dice
El resultado del filtrado es un nuevo DataFrame, por lo que solo hay que obtener la media de la todas las columnas como de forma habitual
planets[planets['year'] == 2008].mean()
o solo para una de las series
planets[planets['year'] == 2008]['distance'].mean()
Maria dice
Hola, como puedo filtrar solo columnas enteras de un DF?
Daniel Rodríguez dice
Para seleccionar el contenido una columna se puede hacer simplemente poniendo el nombre, por ejemplo, para seleccionar el año
planets['year']
. Para más de una columan habría que pasar un vector, por ejemplo, para el año y la masaplanets[['year','mass']]
.Julianmc dice
¿Cómo puedo filtrar un Dataframe de acuerdo a los valores posicionales de una columna?
Por ejemplo, tengo un Dataframe con información sobre animales. Tiene diversas columnas y variables sobre estos, pero quiero filtrar la información de acuerdo a la columna “NOMBRE” , filtrando todos los valores tengan en la posición 2 del str sea la letra “r”, obteniendo información, por ejemplo, de perro, loro, tortuga, gorila, jirafa, etc
Me podrían ayudar?
Daniel Rodríguez dice
Para eso lo más fácil es usar el conjunto de funciones srt de Pandas, algo que explico en https://www.analyticslane.com/2020/01/13/filtrado-de-cadenas-de-texto-en-dataframe-con-pandas/
Felipe dice
Hola buenas tardes
tengo un csv que tiene varias columnas con diferentes datos de personas, entre ellos la edad. Entonces necesito hacer un filtro entre las edades [18:100]. He intentado varias formas pero no me da resultado. Necesito poder mantener todos los datos de las columnas tal como están, solo necesito poder dejar el rango de edad de 18-100. Espero me puedas ayudar, muchas gracias!!
Daniel Rodríguez dice
La condición sería algo así como df[df[‘edad’] >= 18 & df[‘edad’] <= 100]. Aunque también se puede probar con Pandas Query que es algo más intuitivo
Felipe dice
muchas gracias!!