Cómo limpiar datos sucios en Python con Pandas

Publicado el: 11/07/2026
Tempo de leitura: 4 minutos
Limpeza de dados sujos em Python para data cleaning

Los datos reales rara vez llegan listos para analizar. Es común encontrar celdas vacías, nombres escritos de varias formas, números guardados como texto, fechas inválidas, espacios invisibles, filas duplicadas y valores extremos. Si estos problemas no se corrigen, cualquier gráfico, informe o modelo puede producir conclusiones equivocadas. Python y Pandas ofrecen un conjunto de herramientas muy completo para crear un proceso de limpieza repetible y verificable.

Antes de empezar, conviene conocer los fundamentos de Pandas en Python, el trabajo con archivos CSV, el uso de Pandas y NumPy para análisis y la creación de funciones reutilizables. Una limpieza profesional no consiste en modificar datos al azar, sino en aplicar reglas explícitas y conservar evidencias de cada transformación.

Cargar los datos sin perder información

import pandas as pd

df = pd.read_csv(
    "clientes.csv",
    dtype={"codigo_postal": "string"},
    encoding="utf-8"
)

Definir tipos durante la lectura evita que valores como 00123 pierdan ceros iniciales. Para archivos grandes, puedes leer por bloques con chunksize. Antes de cambiar nada, inspecciona el conjunto:

print(df.head())
print(df.info())
print(df.describe(include="all"))
print(df.isna().sum())

Estas operaciones revelan columnas vacías, tipos inesperados y distribuciones sospechosas.

Normalizar nombres de columnas

Los encabezados pueden contener mayúsculas, espacios o símbolos. Una convención consistente facilita el resto del trabajo:

df.columns = (
    df.columns
      .str.strip()
      .str.lower()
      .str.replace(" ", "_", regex=False)
      .str.replace(r"[^a-z0-9_]", "", regex=True)
)

Después de normalizar, verifica que no se hayan creado nombres duplicados. Por ejemplo, “Correo electrónico” y “Correo-electrónico” podrían terminar como la misma columna.

Eliminar espacios y unificar texto

columnas_texto = ["nombre", "ciudad", "email"]

for columna in columnas_texto:
    df[columna] = df[columna].astype("string").str.strip()

Para campos categóricos, unifica mayúsculas y variantes:

df["ciudad"] = df["ciudad"].str.casefold()

reemplazos = {
    "sao paulo": "São Paulo",
    "são paulo": "São Paulo",
    "sp": "São Paulo",
}

df["ciudad"] = df["ciudad"].replace(reemplazos)

No apliques una corrección automática sin revisar su significado. Una abreviatura puede representar valores distintos según el contexto.

Tratar valores ausentes

Pandas representa muchos valores faltantes con NaN o pd.NA. La documentación oficial sobre datos ausentes explica cómo se comportan según el tipo de columna.

df["telefono"] = df["telefono"].replace(
    ["", "N/A", "n/a", "sin datos", "-"],
    pd.NA
)

Después decide qué hacer. Algunas filas pueden eliminarse si falta un identificador imprescindible:

df = df.dropna(subset=["id_cliente"])

Otras columnas pueden rellenarse con un valor explícito:

df["ciudad"] = df["ciudad"].fillna("No informada")

No reemplaces automáticamente números faltantes por cero. Cero y ausencia son conceptos diferentes. Para modelos estadísticos, documenta el método de imputación.

Convertir números almacenados como texto

df["importe"] = (
    df["importe"]
      .astype("string")
      .str.replace("€", "", regex=False)
      .str.replace(".", "", regex=False)
      .str.replace(",", ".", regex=False)
)

df["importe"] = pd.to_numeric(df["importe"], errors="coerce")

errors="coerce" convierte entradas inválidas en valores ausentes. Esto es más seguro que ignorar silenciosamente el problema. Guarda las filas que fallaron para revisarlas:

invalidos = df[df["importe"].isna()].copy()

Limpiar fechas

df["fecha_registro"] = pd.to_datetime(
    df["fecha_registro"],
    errors="coerce",
    dayfirst=True
)

Si los datos mezclan formatos, puede ser necesario limpiarlos previamente o procesar grupos separados. Verifica rangos imposibles:

hoy = pd.Timestamp.today().normalize()
fechas_futuras = df[df["fecha_registro"] > hoy]

Una fecha futura puede ser válida en una reserva, pero no en una fecha de nacimiento. Las reglas dependen del dominio.

Detectar y eliminar duplicados

Primero distingue duplicados completos de registros que comparten una clave:

duplicados_completos = df[df.duplicated(keep=False)]

duplicados_email = df[
    df.duplicated(subset=["email"], keep=False)
]

Después define cuál conservar:

df = (
    df.sort_values("fecha_actualizacion")
      .drop_duplicates(subset=["email"], keep="last")
)

La referencia oficial de drop_duplicates detalla los parámetros. No borres duplicados sin entender si representan errores, compras repetidas o eventos legítimos.

Validar categorías

estados_validos = {"activo", "inactivo", "pendiente"}
mascara = ~df["estado"].isin(estados_validos)
print(df.loc[mascara, ["id_cliente", "estado"]])

Puedes convertir valores desconocidos en una categoría de revisión, pero no inventes el dato correcto sin evidencia.

Identificar valores extremos

Un valor extremo no siempre es un error. Una venta muy alta puede ser real. Utiliza reglas de negocio y estadísticas:

q1 = df["importe"].quantile(0.25)
q3 = df["importe"].quantile(0.75)
iqr = q3 - q1

limite_inferior = q1 - 1.5 * iqr
limite_superior = q3 + 1.5 * iqr

outliers = df[
    (df["importe"] < limite_inferior) |
    (df["importe"] > limite_superior)
]

Analiza los casos antes de excluirlos. En muchos proyectos conviene marcar una columna requiere_revision en lugar de eliminar filas.

Validar correos y teléfonos

Una expresión regular sencilla puede detectar errores evidentes, pero no demuestra que la dirección exista:

patron_email = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
df["email_valido"] = df["email"].str.fullmatch(
    patron_email,
    na=False
)

Para teléfonos, conserva primero el valor original y crea una columna normalizada. No elimines prefijos internacionales ni ceros sin conocer las reglas del país.

Crear una función de limpieza

def limpiar_clientes(df):
    resultado = df.copy()
    resultado.columns = resultado.columns.str.strip().str.lower()
    resultado["email"] = resultado["email"].astype("string").str.strip().str.lower()
    resultado["importe"] = pd.to_numeric(resultado["importe"], errors="coerce")
    resultado["fecha_registro"] = pd.to_datetime(
        resultado["fecha_registro"], errors="coerce"
    )
    resultado = resultado.dropna(subset=["id_cliente"])
    resultado = resultado.drop_duplicates(subset=["id_cliente"], keep="last")
    return resultado

Trabajar sobre una copia evita modificar el DataFrame original de forma inesperada. Divide procesos complejos en funciones pequeñas y prueba cada una.

Crear un informe de calidad

informe = {
    "filas": len(df),
    "duplicados": int(df.duplicated().sum()),
    "emails_invalidos": int((~df["email_valido"]).sum()),
    "importes_ausentes": int(df["importe"].isna().sum()),
}

print(informe)

Comparar el informe antes y después ayuda a verificar que las reglas mejoraron la calidad sin borrar demasiada información.

Guardar resultados de forma segura

df_limpio.to_csv("clientes_limpios.csv", index=False, encoding="utf-8")
invalidos.to_csv("clientes_para_revision.csv", index=False, encoding="utf-8")

No sobrescribas el archivo original durante las primeras ejecuciones. Conserva versiones y registra la fecha, las reglas aplicadas y el número de filas afectadas.

Errores frecuentes

Los fallos más comunes son eliminar todas las filas incompletas, rellenar cualquier ausencia con cero, convertir columnas sin revisar errores, borrar duplicados legítimos, aplicar formatos de fecha equivocados y modificar el archivo original sin copia. También es peligroso limpiar únicamente una muestra visual y asumir que el resto tiene los mismos problemas.

Conclusión

La limpieza de datos es un proceso de diagnóstico, transformación y validación. Pandas facilita normalizar texto, convertir tipos, tratar ausencias, detectar duplicados y crear informes de calidad. La parte más importante sigue siendo definir reglas coherentes con el significado de los datos. Conserva los valores originales, separa registros dudosos y automatiza las verificaciones para que el proceso pueda repetirse cada vez que llegue un archivo nuevo.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    logos do Python, Pandas e NumPy numa mesa de madeira
    Ciencia de Datos
    Foto de perfil de Leandro Hirt da Academify

    Análisis de datos con Python, Pandas y NumPy

    Aprende análisis de datos con Python, NumPy y Pandas: arrays, DataFrames, limpieza, filtros, agrupaciones, merges y exportación.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    representação de IA com código binário na tela
    Ciencia de Datos
    Foto de perfil de Leandro Hirt da Academify

    Scikit-Learn en Python: guía para principiantes

    Aprende Scikit-Learn desde cero: prepara datos, entrena clasificación y regresión, usa pipelines, métricas, validación y evita fugas de datos.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Logo do matplotlib em um fundo branco
    Ciencia de Datos
    Foto de perfil de Leandro Hirt da Academify

    Matplotlib en Python: guía de gráficos para principiantes

    Aprende Matplotlib en Python para crear gráficos de líneas, barras, dispersión, histogramas, subplots y archivos de alta resolución.

    Ler mais

    Tempo de leitura: 4 minutos
    11/07/2026
    Visualização de dados com Plotly em Python
    Ciencia de Datos
    Foto de perfil de Leandro Hirt da Academify

    Plotly en Python: gráficos interactivos paso a paso

    Aprende Plotly en Python para crear gráficos interactivos de líneas, barras y dispersión, personalizar figuras, usar Pandas y exportar resultados.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026
    Mesa de trabalho com laptop exibindo gráficos e a logo do Pandas em primeiro plano
    Ciencia de Datos
    Foto de perfil de Leandro Hirt da Academify

    Pandas en Python: guía completa para principiantes

    Aprende Pandas en Python: Series, DataFrames, CSV, Excel, selección, filtros, valores ausentes, agrupaciones, merges y exportación.

    Ler mais

    Tempo de leitura: 3 minutos
    11/07/2026