Parte II · Preparación

4 · Limpieza y estandarización

Cada transformación está escrita en un script y queda anotada en un registro con el número de filas que afecta. Los cálculos los hace el script y no la IA, porque un script da siempre la misma salida con la misma entrada y cualquiera puede repetirlo para comprobar el resultado.

20.000
ventas distintas
de 20.827 filas: 827 eran copia exacta de otra
8 y 52
productos y provincias
aparecían con 10 y 55 nombres, porque algunos estaban escritos de dos formas (con y sin tilde, Gerona y Girona)
9.024
correos corregidos
de las 20.000 ventas sin copias: 8.952 con tilde y 884 con espacios, 812 de ellos con los dos defectos
19.984
ventas válidas
las 20.000 menos 16 marcadas como anomalía, que se conservan en el fichero

Qué se hizo, paso a paso

  1. Duplicados exactos: 827 filas idénticas eliminadas (se conserva la primera).
  2. Fechas: cada formato se lee con su regla y pasa a ISO AAAA-MM-DD, más año, mes y trimestre.
  3. Texto: productos y provincias a su forma canónica.
  4. Correos: sin espacios, en minúsculas y sin tildes, como pide el encargo. Quedan 12.957 direcciones distintas.
  5. Numéricos: tipos correctos e importe = cant × precio.
  6. Satisfacción: escala de 1 a 10. Los valores ausentes se dejan vacíos para no inventarlos.
  7. Anomalías: 16 ventas con cantidad fuera de 1-5, 12 de ellas también con precio fuera de catálogo. Se marcan con su motivo en dos columnas nuevas, anomalia y motivo_anomalia. Se estudian aquí.
Ver el código de estandarización (Python)
# Estandarización de fechas: por FORMATO, no genérica
FORMATOS = [
    (r"^\d{4}-\d{2}-\d{2}$", "%Y-%m-%d"),
    (r"^\d{4}/\d{2}/\d{2}$", "%Y/%m/%d"),
    (r"^\d{2}-\d{2}-\d{4}$", "%d-%m-%Y"),
    (r"^\d{2}/\d{2}/\d{2}$", "%m/%d/%y"),   # formato US, año de 2 dígitos
]

# Email: sin espacios, en minúsculas y sin tildes
def sin_tildes(s):
    return "".join(c for c in unicodedata.normalize("NFKD", s) if not unicodedata.combining(c))
df["email"] = df["email"].str.replace(" ", "").str.lower().map(sin_tildes)

# Paso 11: marcar (no borrar) lo que rompe las reglas del negocio
precio_catalogo = df.groupby("prod")["precio"].agg(lambda s: s.mode().iloc[0])
fuera_cant = (df["cant"] < 1) | (df["cant"] > 5)
fuera_precio = df["precio"] != df["prod"].map(precio_catalogo)
df["anomalia"] = (fuera_cant | fuera_precio).astype(int)
El script se puede ejecutar las veces que haga falta y el resultado no cambia

Muchas correcciones estropean el dato si se aplican dos veces. Un paso que divide el precio entre 100 para pasar de céntimos a euros deja bien el fichero la primera vez. Si se vuelve a lanzar sobre ese mismo fichero ya corregido, lo divide otra vez y todos los precios quedan cien veces más bajos. Es lo que ocurre cuando alguien limpia a mano un fichero que ya había limpiado otra persona.

Para evitarlo, el script nunca lee el fichero limpio. Parte siempre del fichero en bruto, que no se modifica, y escribe el limpio desde cero. Ejecutarlo una vez o diez da exactamente el mismo resultado. Esta propiedad se llama idempotencia.

Dos decisiones: no imputar y no corregir
Falta uno de cada cinco valores de satisfacción. Rellenarlos con la media supondría inventar datos, así que se dejan ausentes y el análisis usa los casos disponibles. Con las anomalías se sigue el mismo criterio. El valor verdadero de esas ventas no se conoce, de modo que se marcan y se apartan en lugar de corregirlas.

Con el dato limpio, ya se puede describir cada campo. Siguiente: el diccionario de datos →