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 | 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 finalstr.title()convierte «john doe» en «John Doe»str.lower()normaliza todos los emails a minúsculasstr.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 | 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.

Deja una respuesta