Saltar al contenido

Limpieza de datos con Python y Pandas: de datos sucios a listos para análisis en minutos

Código Python con Pandas mostrando la transformación de datos sucios a datos limpios: antes con duplicados, NaN y formatos inconsistentes; después con datos normalizados y listos para análisis

El 80% del tiempo en un proyecto de datos no lo pasas analizando — lo pasas limpiando. Emails en mayúsculas mezclados con minúsculas, fechas en tres formatos distintos, montos con signos de dólar dentro de una columna numérica, filas duplicadas que nadie sabe de dónde vinieron.

Ese caos tiene solución. Y con Python y Pandas, la solución es rápida, reproducible y se puede ejecutar cada vez que llegan datos nuevos sin tocar nada.

Esta guía cubre los cuatro problemas más comunes de datasets reales con código que puedes copiar directamente.

El dataset de ejemplo: clientes con datos sucios

Partimos de un CSV con información de clientes que tiene todos los vicios típicos:

import pandas as pd

df = pd.read_csv('clientes.csv')
df.head(10)

El resultado sería algo así:

Name Email Amount Date Country
john doe JOHN.DOE@Example.COM $1,200.00 2024-01-05 us
Jane Smith jane.smith@example.com 1,200 01/05/2024 USA
Mike Johnson mike.johnson@example.com $980 2024/01/06 us
Sarah Lee NaN 01-07-2024 Canada
John Doe john.doe@example.com $1,200.00 2024-01-05 US

Problemas visibles de un vistazo: nombres en minúsculas y con espacios extra, emails en mayúsculas, montos como texto con símbolo de dólar, fechas en cuatro formatos distintos, país con comillas y mezcla de «us»/»USA»/»US», y una fila con email vacío que es un cliente que no sirve para campañas.

Paso 1: inspección inicial

Antes de limpiar, siempre toca entender qué tan roto está el dataset:

# Dimensiones y tipos de datos
print(df.shape)
print(df.dtypes)
print()

# Conteo de nulos por columna
print(df.isnull().sum())
print()

# Duplicados exactos
print(f"Filas duplicadas: {df.duplicated().sum()}")

Esto te da el mapa del daño: cuántas filas, qué columnas tienen nulos, y si hay duplicados exactos. Es el paso que la mayoría salta y luego lamenta.

Paso 2: eliminar duplicados y filas sin email

Las filas sin email son inútiles para cualquier campaña o análisis de clientes. Los duplicados exactos distorsionan métricas de ventas. Fuera los dos:

df = (df
    .drop_duplicates()
    .dropna(subset=['email'])
    .reset_index(drop=True)
)

print(f"Filas después de limpiar: {len(df)}")

drop_duplicates() elimina filas idénticas en todas las columnas. dropna(subset=['email']) elimina solo las filas donde email es nulo, sin tocar el resto. El reset_index al final resetea el índice para que quede consecutivo.

Paso 3: estandarizar texto

Nombres con mezcla de mayúsculas/minúsculas, espacios al inicio o al final, países inconsistentes — todo esto rompe los agrupadores y los joins:

df = df.assign(
    name=lambda x: x['name'].str.strip().str.title(),
    email=lambda x: x['email'].str.strip().str.lower(),
    country=lambda x: x['country'].str.strip().str.replace(r"[`'\"]", "", regex=True).str.upper()
)
  • str.strip() elimina espacios y caracteres extraños al inicio y al final
  • str.title() convierte «john doe» en «John Doe»
  • str.lower() normaliza todos los emails a minúsculas
  • str.upper() estandariza países: «us», «USA», «usa» → todos quedan «US»

Paso 4: limpiar y convertir el monto

La columna amount tiene símbolo de dólar, comas, y viene como texto. Para hacer cálculos necesita ser número:

df['amount'] = (df['amount']
    .astype(str)
    .str.replace(r'[^0-9\.]', '', regex=True)
    .astype(float)
)

La expresión regular [^0-9\.] elimina todo lo que no sea dígito ni punto decimal. Lo que queda es un string limpio como "1200.00" que .astype(float) convierte sin problema.

Paso 5: estandarizar fechas

Cuatro formatos de fecha en la misma columna es el caos perfecto. Pandas tiene to_datetime con infer_datetime_format que detecta el formato automáticamente:

df['date'] = pd.to_datetime(df['date'], infer_datetime_format=True, errors='coerce')

# Verificar que quedó bien
print(df['date'].dtype)
print(df['date'].isnull().sum(), "fechas que no pudieron parsearse")

El parámetro errors='coerce' convierte las fechas que no puedan parsearse a NaT (Not a Time) en lugar de romper el proceso. Así puedes identificar qué filas tienen un problema real y tratarlas por separado.

Pipeline completo en un solo bloque

Todo el proceso junto, de un tirón:

import pandas as pd

df = (pd.read_csv('clientes.csv')
    # Estructura
    .drop_duplicates()
    .dropna(subset=['email'])
    .rename(columns=str.strip)          # columnas con espacios en el nombre
    .reset_index(drop=True)

    # Texto
    .assign(
        name    = lambda x: x['name'].str.strip().str.title(),
        email   = lambda x: x['email'].str.strip().str.lower(),
        country = lambda x: x['country'].str.strip()
                                .str.replace(r"[`'\"]", "", regex=True)
                                .str.upper(),

        # Monto: quitar símbolo de dólar y comas, convertir a float
        amount  = lambda x: x['amount']
                                .astype(str)
                                .str.replace(r'[^0-9\.]', '', regex=True)
                                .astype(float),

        # Fecha: detectar formato automáticamente
        date    = lambda x: pd.to_datetime(
                                x['date'],
                                infer_datetime_format=True,
                                errors='coerce'
                            ),
    )
)

print(df.dtypes)
df.head()

El resultado:

Name Email Amount Date Country
John Doe john.doe@example.com 1200.0 2024-01-05 US
Jane Smith jane.smith@example.com 1200.0 2024-01-05 US
Mike Johnson mike.johnson@example.com 980.0 2024-01-06 US

De 6 filas sucias a 3 filas limpias, todas con tipos de datos correctos y formato consistente.

Guardar el resultado

Una vez limpio, lo exportas y el siguiente paso del pipeline consume datos confiables:

# CSV limpio
df.to_csv('clientes_limpios.csv', index=False)

# O directo a Excel si lo necesitas en ese formato
df.to_excel('clientes_limpios.xlsx', index=False)

Lo que sigue

Con los datos limpios puedes conectar este CSV directamente a Power BI usando Obtener datos → Texto/CSV, y el modelo va a reconocer los tipos correctamente sin pasos adicionales en Power Query: el monto como número, la fecha como fecha, y los países normalizados para que los mapas y los filtros funcionen sin sorpresas.

Si el volumen es alto — decenas de miles de filas diarias — este mismo script lo puedes automatizar con Task Scheduler en Windows o un cron en Linux para que corra solo cada noche antes de que el equipo llegue a trabajar.


¿Tienes un dataset con otros tipos de problemas? Deja tu caso en los comentarios y lo trabajamos.

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *