tutoriales.com

Manipulación Avanzada de Cadenas en Pandas: Potenciando tus Datos Textuales con `.str` 📝

Este tutorial te guiará a través de las potentes capacidades del atributo `.str` de Pandas para la manipulación de cadenas de texto. Aprenderás a limpiar, formatear, buscar patrones y extraer información valiosa de tus datos textuales, mejorando significativamente la calidad y usabilidad de tus datasets.

Intermedio20 min de lectura8 views
Reportar error

Introducción: El Desafío de los Datos Textuales 📚

En el mundo de la Ciencia de Datos, es raro encontrar datasets que contengan solo números perfectos. Más a menudo de lo que nos gustaría, nos enfrentamos a columnas repletas de texto: nombres, descripciones, comentarios, direcciones, URLs y mucho más. Estos datos textuales, aunque ricos en información, a menudo vienen en formatos inconsistentes, con errores tipográficos, mayúsculas y minúsculas variadas, espacios extra y caracteres no deseados.

Aquí es donde entra en juego el atributo .str de Pandas. Es una herramienta indispensable que nos permite aplicar métodos de cadena de Python directamente a Series y DataFrames, haciendo la limpieza y transformación de texto un proceso eficiente y manejable. ¡Prepárate para dominar tus datos textuales como un profesional!

💡 Consejo: La limpieza de datos textuales es un paso crítico en casi cualquier proyecto de Machine Learning o análisis de datos. Ignorarla puede llevar a modelos inexactos y conclusiones erróneas.

Requisitos Previos e Importación ✨

Para seguir este tutorial, solo necesitas tener Python y Pandas instalados en tu entorno. Si aún no los tienes, puedes instalarlos fácilmente:

pip install pandas numpy

Una vez que los tengas, el primer paso en cualquier script es importar la librería:

import pandas as pd
import numpy as np

Creando Datos de Ejemplo 🛠️

Para ilustrar las diferentes funcionalidades, crearemos un DataFrame de ejemplo con varias columnas de texto que simulan datos del mundo real con inconsistencias.

data = {
    'producto': [
        '  TELEVISOR SMART TV 55"',
        'Laptop Gamer RTX 3060',
        'Teléfono Móvil (rojo)',
        'refrigerador no Frost',
        'Smartwatch deportivo',
        np.nan,
        'Tablet 10.1" (azul)  '
    ],
    'descripcion': [
        'Televisor LED con resolución 4K y HDR. Perfecto para tu sala de estar.',
        'Portátil potente para juegos con procesador i7 y 16GB RAM.',
        'Smartphone de última generación con cámara de 108MP.',
        'Nevera moderna con dispensador de agua y tecnología No Frost.',
        'Reloj inteligente con monitor de frecuencia cardíaca y GPS.',
        'Auriculares inalámbricos con cancelación de ruido.',
        'Tablet Android con pantalla Full HD y batería de larga duración.'
    ],
    'codigo': [
        'TV-S-001',
        'LT-G-002',
        'TL-M-003',
        'RF-N-004',
        'SW-D-005',
        'AU-I-006',
        'TB-A-007'
    ],
    'etiquetas': [
        'electronica, hogar, tv',
        'informatica, gaming',
        'telefonia, movil',
        'electrodomesticos, cocina',
        'wearables, salud',
        'audio, electronica',
        'informatica, tablet'
    ]
}

df = pd.DataFrame(data)
print(df)

Salida del DataFrame inicial:

                 producto                                 descripcion     codigo            etiquetas
0    TELEVISOR SMART TV 55"   Televisor LED con resolución 4K y HDR. Perfecto para tu sala de estar.   TV-S-001    electronica, hogar, tv
1    Laptop Gamer RTX 3060    Portátil potente para juegos con procesador i7 y 16GB RAM.   LT-G-002       informatica, gaming
2    Teléfono Móvil (rojo)  Smartphone de última generación con cámara de 108MP.   TL-M-003          telefonia, movil
3    refrigerador no Frost   Nevera moderna con dispensador de agua y tecnología No Frost.   RF-N-004  electrodomesticos, cocina
4    Smartwatch deportivo   Reloj inteligente con monitor de frecuencia cardíaca y GPS.   SW-D-005           wearables, salud
5                      NaN  Auriculares inalámbricos con cancelación de ruido.   AU-I-006         audio, electronica
6   Tablet 10.1" (azul)     Tablet Android con pantalla Full HD y batería de larga duración.   TB-A-007      informatica, tablet

Observa las inconsistencias: espacios extra, mayúsculas y minúsculas mezcladas, NaN (valores nulos). ¡Vamos a limpiarlos!


1. Limpieza Básica de Cadenas con .str 🧹

Los primeros pasos en la limpieza suelen ser estandarizar mayúsculas/minúsculas y eliminar espacios no deseados.

1.1. Conversión a Mayúsculas/Minúsculas (.lower(), .upper(), .capitalize(), .title(), .swapcase())

Es fundamental para asegurar que 'televisor' y 'Televisor' sean tratados como la misma entidad.

# Convertir la columna 'producto' a minúsculas
df['producto_lower'] = df['producto'].str.lower()

# Convertir la columna 'producto' a mayúsculas
df['producto_upper'] = df['producto'].str.upper()

# Capitalizar (primera letra de cada palabra en mayúscula)
df['descripcion_title'] = df['descripcion'].str.title()

print(df[['producto', 'producto_lower', 'producto_upper', 'descripcion_title']])
📌 Nota: Los métodos `.str` ignoran automáticamente los valores `NaN`, lo cual es muy conveniente y evita errores.

1.2. Eliminación de Espacios en Blanco (.strip(), .lstrip(), .rstrip())

Los espacios al inicio o al final de una cadena son muy comunes y pueden causar problemas en comparaciones o uniones de datos.

# Eliminar espacios en blanco al inicio y al final de 'producto'
df['producto_stripped'] = df['producto'].str.strip()

# Solo eliminar espacios a la izquierda
df['producto_lstripped'] = df['producto'].str.lstrip()

# Solo eliminar espacios a la derecha
df['producto_rstripped'] = df['producto'].str.rstrip()

print(df[['producto', 'producto_stripped', 'producto_lstripped', 'producto_rstripped']])

2. Búsqueda y Reemplazo de Patrones (.contains(), .replace(), .startswith(), .endswith()) 🔍

Estas funciones son esenciales para identificar y modificar partes específicas de tus cadenas.

2.1. Buscar Subcadenas (.contains())

Útil para filtrar filas o crear nuevas columnas booleanas basadas en la presencia de una subcadena o patrón regex.

# Buscar productos que contengan 'smart' (ignorando mayúsculas/minúsculas)
df['es_smart'] = df['producto'].str.contains('smart', case=False, na=False)

# Buscar descripciones que contengan '4K' o 'Full HD'
df['resolucion_alta'] = df['descripcion'].str.contains('4K|Full HD', case=False, na=False, regex=True)

print(df[['producto', 'es_smart', 'descripcion', 'resolucion_alta']])
💡 Consejo: Usa `case=False` para búsquedas insensibles a mayúsculas y minúsculas, y `regex=True` para expresiones regulares complejas. `na=False` reemplaza `NaN` con `False` para no afectar el tipo de dato de la serie booleana.

2.2. Reemplazar Subcadenas (.replace())

Ideal para corregir errores tipográficos, estandarizar términos o eliminar caracteres no deseados.

# Reemplazar 'SMART TV' por 'SmartTV' en la columna 'producto'
df['producto_limpio'] = df['producto'].str.replace('SMART TV', 'SmartTV', case=False)

# Reemplazar '"' por ' pulgadas' y eliminar '(rojo)' o '(azul)'
df['producto_limpio'] = df['producto_limpio'].str.replace('"', ' pulgadas')
df['producto_limpio'] = df['producto_limpio'].str.replace(r'\s*\(.*?\)', '', regex=True)

print(df[['producto', 'producto_limpio']])
⚠️ Advertencia: Al usar `replace` con `regex=True`, asegúrate de escapar caracteres especiales como `(` o `)` si quieres que sean interpretados literalmente.

2.3. Comprobar Inicio o Fin (.startswith(), .endswith())

Útil para identificar patrones al principio o al final de las cadenas.

# Identificar códigos que empiezan con 'TV'
df['es_tv'] = df['codigo'].str.startswith('TV', na=False)

# Identificar descripciones que terminan con un punto
df['termina_punto'] = df['descripcion'].str.endswith('.', na=False)

print(df[['codigo', 'es_tv', 'descripcion', 'termina_punto']])

3. Extracción de Información con Expresiones Regulares (.extract(), .findall()) 🎯

Las expresiones regulares (regex) son increíblemente poderosas para extraer patrones complejos de texto. Pandas integra estas capacidades a través de .str.extract() y .str.findall().

3.1. Extracción de Grupos Capturados (.extract())

extract() es perfecto cuando quieres extraer una o más partes específicas de una cadena, basándote en grupos de captura de una expresión regular. Retorna un DataFrame.

# Extraer el tamaño en pulgadas y el color (opcional) de la columna 'producto'
# Ejemplo: 'TELEVISOR SMART TV 55"' -> 55
# Ejemplo: 'Tablet 10.1" (azul)' -> 10.1, 'azul'

patron_producto = r'(\d+\.?\d*)\"\s*(?:\((.*?)\))?'
df[['tamano_pulgadas', 'color']] = df['producto'].str.extract(patron_producto, expand=True)

# Ajustar el tipo de dato para 'tamano_pulgadas' si es numérico
df['tamano_pulgadas'] = pd.to_numeric(df['tamano_pulgadas'], errors='coerce')

print(df[['producto', 'tamano_pulgadas', 'color']])
📌 Nota: `expand=True` hace que los grupos capturados se conviertan en columnas separadas en un nuevo DataFrame. Si un grupo no tiene coincidencia, se le asignará `NaN`.

3.2. Encontrar Todas las Coincidencias (.findall())

findall() retorna una lista de todas las subcadenas que coinciden con un patrón regex dentro de cada elemento de la Serie. Es útil cuando hay múltiples ocurrencias del patrón en una misma cadena.

# Encontrar todos los números de 2 o más dígitos en la descripción
df['numeros_descripcion'] = df['descripcion'].str.findall(r'\b\d{2,}\b')

print(df[['descripcion', 'numeros_descripcion']])

4. División y Unión de Cadenas (.split(), .join()) ✂️🔗

Estas funciones son fundamentales para trabajar con datos delimitados, como listas de etiquetas o CSV embebidos.

4.1. División de Cadenas (.split())

Divide cada cadena en una lista de subcadenas basándose en un delimitador. Muy útil para descomponer listas de elementos en una sola celda.

# Dividir la columna 'etiquetas' por la coma y espacio
df['lista_etiquetas'] = df['etiquetas'].str.split(', ')

print(df[['etiquetas', 'lista_etiquetas']])

# Expandir las etiquetas en filas separadas (si es necesario un formato 'largo')
df_etiquetas_expanded = df.explode('lista_etiquetas')
print("\nDataFrame con etiquetas expandidas:\n", df_etiquetas_expanded[['producto', 'lista_etiquetas']].head(10))
DataFrame Original id tags 1 "a, b, c" df['tags'].str.split(', ') Resultado del Split id tags (listas) 1 ['a', 'b', 'c'] df.explode('tags') Resultado Final (Explode) id tags (filas) 1 'a' 1 'b' 1 'c'

4.2. Unión de Cadenas (.join())

Opuesto a split(), join() une los elementos de una lista de cadenas en una sola cadena, utilizando un delimitador.

# Unir las listas de etiquetas de nuevo en una sola cadena (ejemplo con un separador diferente)
# Primero, asegurarse de que 'lista_etiquetas' no tenga NaN en las listas
df['lista_etiquetas_validas'] = df['lista_etiquetas'].apply(lambda x: [str(item) for item in x] if isinstance(x, list) else [])
df['etiquetas_reunidas'] = df['lista_etiquetas_validas'].apply(lambda x: ' | '.join(x))

print(df[['lista_etiquetas', 'etiquetas_reunidas']])

5. Otros Métodos Útiles de str 💡

Pandas ofrece muchos otros métodos de cadena que replican la funcionalidad de Python, pero aplicados a Series.

5.1. Longitud de las Cadenas (.len())

Calcula la longitud de cada cadena en la Serie.

df['longitud_producto'] = df['producto'].str.len()
print(df[['producto', 'longitud_producto']])

5.2. Alinear y Rellenar Cadenas (.pad(), .center(), .ljust(), .rjust())

Útil para formatear texto y asegurar anchos fijos, por ejemplo, en informes.

# Alinear a la derecha con un ancho total de 25 caracteres, rellenando con '-' a la izquierda
df['codigo_formateado'] = df['codigo'].str.rjust(25, fillchar='-')

print(df[['codigo', 'codigo_formateado']])

5.3. Comprobar el Tipo de Contenido (.isnumeric(), .isalpha(), .isalnum(), .isspace(), etc.)

Devuelven un valor booleano indicando si la cadena contiene solo números, solo letras, etc.

# Comprobar si la columna 'codigo' contiene solo caracteres alfanuméricos
df['codigo_es_alnum'] = df['codigo'].str.isalnum()

print(df[['codigo', 'codigo_es_alnum']])

5.4. Encoding/Decoding (.encode(), .decode())

Para trabajar con diferentes codificaciones de caracteres, aunque en la mayoría de los casos Pandas maneja esto internamente si los datos se cargan correctamente.

# Ejemplo de codificación a UTF-8 (útil si hay caracteres especiales)
df['producto_encoded'] = df['producto'].str.encode('utf-8')

print(df[['producto', 'producto_encoded']])

Caso de Estudio Avanzado: Limpieza de Direcciones (Ejemplo Hipotético) 🏡

Imaginemos que tenemos una lista de direcciones y queremos estandarizarlas, extrayendo el número y el tipo de vía.

direcciones = pd.Series([
    'Calle Falsa 123',
    'Av. Siempreviva, 742',
    'Plaza Mayor S/N',
    'C/ Luna #20',
    'Blvd. de los Sueños 500 Interior 10',
    np.nan,
    'Ruta Nacional 3 Km 102.5'
])

# Limpieza general: convertir a minúsculas y quitar espacios extra
direcciones_limpias = direcciones.str.lower().str.strip()

# Patrón Regex para extraer tipo de vía y número
# c/ (calle), av. (avenida), plaza, blvd. (bulevar), ruta nacional, km, s/n
patron_direccion = r'^(?:(calle|c/|av\.?|avenida|plaza|blvd\.?|bulevar|ruta nacional)\s+)?([^,\d#]+)?(?:\s*(?:#|km)?\s*(\d+\.?\d*))?'

extraccion = direcciones_limpias.str.extract(patron_direccion, expand=True)
extraccion.columns = ['tipo_via_crudo', 'nombre_via', 'numero_o_km']

# Estandarizar tipo de vía
def estandarizar_tipo_via(tipo):
    if pd.isna(tipo): return tipo
    if 'calle' in tipo or 'c/' in tipo: return 'Calle'
    if 'av' in tipo or 'avenida' in tipo: return 'Avenida'
    if 'plaza' in tipo: return 'Plaza'
    if 'blvd' in tipo or 'bulevar' in tipo: return 'Bulevar'
    if 'ruta' in tipo: return 'Ruta Nacional'
    return tipo

extraccion['tipo_via_estandar'] = extraccion['tipo_via_crudo'].apply(estandarizar_tipo_via)

# Limpiar nombre de la vía y número
extraccion['nombre_via'] = extraccion['nombre_via'].str.strip().str.title()
extraccion['numero_o_km'] = pd.to_numeric(extraccion['numero_o_km'], errors='coerce')

# Unir con las direcciones originales
df_direcciones = pd.DataFrame({
    'direccion_original': direcciones,
    'direccion_limpia': direcciones_limpias,
    'tipo_via': extraccion['tipo_via_estandar'],
    'nombre_via': extraccion['nombre_via'],
    'numero': extraccion['numero_o_km']
})

print(df_direcciones)

Salida del caso de estudio de direcciones:

          direccion_original           direccion_limpia tipo_via         nombre_via  numero
0            Calle Falsa 123            calle falsa 123    Calle              Falsa   123.0
1      Av. Siempreviva, 742        av. siempreviva, 742  Avenida        Siempreviva   742.0
2          Plaza Mayor S/N            plaza mayor s/n    Plaza              Mayor     NaN
3              C/ Luna #20                c/ luna #20    Calle               Luna    20.0
4  Blvd. de los Sueños 500  blvd. de los sueños 500  Bulevar   De Los Sueños    500.0
5                      NaN                      NaN      NaN                NaN     NaN
6    Ruta Nacional 3 Km 102.5  ruta nacional 3 km 102.5  Ruta Nacional          NaN   102.5

Este ejemplo demuestra cómo combinar varios métodos .str con expresiones regulares y funciones Python personalizadas para lograr una limpieza y estandarización de datos compleja.


Consideraciones de Rendimiento y Grandes Datasets 🚀

Aunque los métodos .str son vectorizados y generalmente eficientes, con datasets extremadamente grandes (millones o miles de millones de filas), el rendimiento puede ser una preocupación. Aquí algunas estrategias:

  • Usa category para columnas con pocas cadenas únicas: Si una columna de texto tiene un número limitado de valores únicos, convertirlas a tipo category puede ahorrar mucha memoria y, en algunos casos, mejorar el rendimiento.
# Ejemplo: Convertir 'producto_lower' a categoría
df['producto_lower_cat'] = df['producto_lower'].astype('category')
print(df['producto_lower_cat'].dtype)
  • Procesamiento por Chunks: Para archivos muy grandes que no caben en memoria, puedes leerlos y procesarlos en trozos (chunks).

  • Bibliotecas especializadas: Para tareas de NLP muy intensivas, considera bibliotecas como spaCy o NLTK, que están optimizadas para el procesamiento de texto. Sin embargo, para la mayoría de las tareas de limpieza estructurada, Pandas es más que suficiente.

  • Optimización de Regex: Las expresiones regulares pueden ser costosas. Intenta que tus patrones sean lo más específicos posible para evitar retrocesos excesivos (backtracking).


Conclusión ✨

El atributo .str de Pandas es un pilar fundamental en la manipulación y limpieza de datos textuales. Desde la estandarización básica de mayúsculas y minúsculas hasta la extracción compleja de patrones con expresiones regulares, su conjunto de herramientas te permite transformar datos crudos y desordenados en información estructurada y utilizable.

Al dominar estas técnicas, no solo mejorarás la calidad de tus análisis y modelos, sino que también ahorrarás una cantidad significativa de tiempo que de otro modo dedicarías a tareas de limpieza manual y propensas a errores. ¡Sigue practicando y explorando todas las posibilidades que .str tiene para ofrecer!

🔥 Importante: La consistencia es clave en la limpieza de datos. Define un conjunto de reglas y aplícalas sistemáticamente a todas las columnas textuales relevantes.

Preguntas Frecuentes (FAQ) 🤔

¿Qué pasa con los valores `NaN` cuando uso `.str`? Los métodos de `.str` están diseñados para ignorar automáticamente los valores `NaN`. Si intentas aplicar un método de cadena a un `NaN`, el resultado seguirá siendo `NaN` sin lanzar un error. Esto es una característica muy útil para la limpieza de datos.
¿Puedo encadenar múltiples métodos `.str`? ¡Absolutamente! Es una práctica común y altamente recomendada. Por ejemplo: `df['columna'].str.lower().str.strip().str.replace('old', 'new')`. Esto hace que tu código sea más conciso y legible.
¿Cuándo debo usar expresiones regulares y cuándo métodos simples? Usa métodos simples (`.lower()`, `.strip()`, `.replace()` sin regex) para tareas directas y bien definidas. Usa expresiones regulares (`.contains()` con `regex=True`, `.extract()`, `.findall()`) cuando necesites buscar o extraer patrones complejos que no pueden ser descritos con una subcadena fija.

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!