
En una entrada anterior se explicó cómo filtrar registros en DataFrames de Pandas en base a los valores de los registros. Para lo que se utilizaron ejemplos únicamente numéricos. En los comentarios de la entrada varios lectores preguntasteis cómo hacer el filtrado de cadenas de texto en DataFrame, ya que esta es una tarea también habitual.
En la entrada de hoy nos vamos a centrar en explicar cómo realizar operaciones de filtrado en base al contenido de las cadenas de texto. Para lo que utilizaremos principalmente el método str.contains()
que existe en las series de Pandas.
Conjunto de datos de ejemplo
Al igual que en la entrada anterior vamos a utilizar el conjunto de datos de exoplanetas que se puede encontrar en la librería Seaborn. Un conjunto de datos que contiene en una de las series cadenas de texto. La importación del conjunto de datos se puede realizar con el siguiente código.
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
La columna method
contiene el nombre de los métodos mediante los que se descubrió cada uno de los exoplanetas. Podemos comprobar fácilmente que existen 10 métodos y ver estos con el siguiente código.
planets.method.unique()
['Radial Velocity' 'Imaging' 'Eclipse Timing Variations' 'Transit' 'Astrometry' 'Transit Timing Variations' 'Orbital Brightness Modulation' 'Microlensing' 'Pulsar Timing' 'Pulsation Timing Variations']
Buscar que contiene una cadena literal
En primer lugar, se pueden buscar los registros que contengan literalmente la cadena de texto que deseamos. Para lo que se puede utilizar el operador igualdad (==
). Por ejemplo, para buscar los planetas que han sido descubiertos mediante el método “Pulsar Timing” se puede usar.
planets[planets.method == 'Pulsar Timing']
method number orbital_period mass distance year 941 Pulsar Timing 3 25.262000 NaN NaN 1992 942 Pulsar Timing 3 66.541900 NaN NaN 1992 943 Pulsar Timing 3 98.211400 NaN NaN 1994 944 Pulsar Timing 1 36525.000000 NaN NaN 2003 945 Pulsar Timing 1 0.090706 NaN 1200.0 2011
En este caso es necesario indicar el nombre de forma exacta, si nos equivocamos el código anterior nos devolverá un DataFrame vacío.
Buscar cadenas de texto con una subcadena
Ampliando el ejemplo anterior, puede ser interesante localizar los planetas descubiertos mediante los métodos que contengan “Pulsa”. Conociendo los valores se puede hacer con el operador igualdad indicando que contenga una cadena u otra. Pero en un caso genérico esta no es una buena aproximación. La solución para esto es utilizar el método str.contains()
como se muestra en el siguiente ejemplo.
planets[planets.method.str.contains('Pulsa')]
method number orbital_period mass distance year 941 Pulsar Timing 3 25.262000 NaN NaN 1992 942 Pulsar Timing 3 66.541900 NaN NaN 1992 943 Pulsar Timing 3 98.211400 NaN NaN 1994 944 Pulsar Timing 1 36525.000000 NaN NaN 2003 945 Pulsar Timing 1 0.090706 NaN 1200.0 2011 958 Pulsation Timing Variations 1 1170.000000 NaN NaN 2007
En dónde se han obtenido los cinco planetas de que se han descubierto mediante “Pulsar Timing” y el que se ha descubierto mediante “Pulsation Timing Variations”. Si queremos buscar los que tiene la cadena “Timing” reemplazando una cadena por otra.
Ignorar diferencias entre mayúsculas y minúsculas
Por defecto el método str.contains()
diferencia entre mayúsculas y minúsculas, por lo que si escribimos “pulsa” en lugar de “Pulsa” se obtendremos una cadena vacía.
planets[planets.method.str.contains('pulsa')]
Para evitar esto se puede igualar la opción case
a falso para que ignore la diferencias entre mayúsculas y minúsculas.
planets[planets.method.str.contains('pulsa', case=False)]
method number orbital_period mass distance year 941 Pulsar Timing 3 25.262000 NaN NaN 1992 942 Pulsar Timing 3 66.541900 NaN NaN 1992 943 Pulsar Timing 3 98.211400 NaN NaN 1994 944 Pulsar Timing 1 36525.000000 NaN NaN 2003 945 Pulsar Timing 1 0.090706 NaN 1200.0 2011 958 Pulsation Timing Variations 1 1170.000000 NaN NaN 2007
Buscar más de una subcadena
Si queremos buscar más de una subcadena se puede utilizar |
para separar las cadenas a buscar. En este caso lo que se obtiene todos los registros que contenga una u otra subcadena. Por ejemplo, se pueden buscar los métodos que contenga “Pulsar” u “Orbital” en sus nombres.
planets[planets.method.str.contains('Pulsar|Orbital')]
method number orbital_period mass distance year 787 Orbital Brightness Modulation 2 0.240104 NaN 1180.0 2011 788 Orbital Brightness Modulation 2 0.342887 NaN 1180.0 2011 792 Orbital Brightness Modulation 1 1.544929 NaN NaN 2013 941 Pulsar Timing 3 25.262000 NaN NaN 1992 942 Pulsar Timing 3 66.541900 NaN NaN 1992 943 Pulsar Timing 3 98.211400 NaN NaN 1994 944 Pulsar Timing 1 36525.000000 NaN NaN 2003 945 Pulsar Timing 1 0.090706 NaN 1200.0 2011
Expresiones regulares
El método str.contains()
puede emplearse también expresiones regulares, lo que ofrece múltiples posibilidades.
planets[planets.method.str.contains('Pulsa?', regex=True)]
method number orbital_period mass distance year 941 Pulsar Timing 3 25.262000 NaN NaN 1992 942 Pulsar Timing 3 66.541900 NaN NaN 1992 943 Pulsar Timing 3 98.211400 NaN NaN 1994 944 Pulsar Timing 1 36525.000000 NaN NaN 2003 945 Pulsar Timing 1 0.090706 NaN 1200.0 2011 958 Pulsation Timing Variations 1 1170.000000 NaN NaN 2007
Conclusiones
En esta entrada se ha visto cómo se puede utilizar el método str.contains()
para realizar el filtrado de cadenas de texto en DataFrame con Pandas.
Imágenes: Pixabay (Nicole)
Como filtrar con pandas de tal forma que solo me consulte los registros que contengan palabras que inicien con una letra o un numero en especifico. Por ejemplo, filtrar todas claves geograficas pertenecientes al estado de yucatan, es decir las claves geograficas que inicien con 31… ya probe con .str.contains(‘31%’) y tambien con .filter(‘31%’)
Ese problema se puede resolver con la propiedad
.str.startswith('31')