Saltar al contenido

lección 3

Día 2 (mañana) — Los datos nunca están limpios

Los datos llegan sucios: fechas en tres formatos, precios con euros pegados, descuentos escritos de tres maneras, categorías inconsistentes y cantidades imposibles. Hoy los dejas en un estado en el que sumar signifique algo.

55 min

### La mise en place del analista

En una cocina profesional nadie empieza a cocinar en cuanto entra. Primero está la mise en place: lavar, pelar, cortar y ordenar cada ingrediente antes de encender el fuego. Parece tiempo perdido, pero es lo que hace que el plato salga bien y a la vez. Con los datos es idéntico. Ayer abriste las cajas y viste qué había; hoy toca la mise en place, porque los ingredientes que te ha pasado Nuria vienen sin lavar. Si empezaras a sumar ahora mismo, sumarías texto con euros pegados y fechas que Pandas no entiende, y el número que le enseñaras a Carmen el viernes sería basura con decimales.

Álex ya te avisó: «la fecha viene en tres formatos, el precio con el euro y coma, y las categorías cada una como le da la gana». Tenía razón, y hoy lo vas a comprobar columna a columna. La buena noticia es que la suciedad de los datos, por caótica que parezca, casi siempre cae en un puñado de tipos conocidos. Vamos a verlos uno a uno y a arreglarlos.

Hay una frase que se repite en el oficio y que al principio suena a exageración: un analista pasa el 80% del tiempo limpiando datos y el 20% analizándolos. No es una queja, es la descripción del trabajo. Y tiene una explicación sencilla: los datos no se generan para que tú los analices, se generan para que la tienda funcione. A Shopify le da igual que el precio lleve el euro pegado, porque él solo tiene que cobrar; el problema es tuyo, que quieres sumar. Cada mancha que vas a limpiar hoy nació de una decisión razonable de otro sistema o de otra persona, que a ti te llega como un obstáculo. Aceptar eso te quita el enfado y te deja la curiosidad: ¿por qué está sucio esto, y qué me dice de dónde viene?

Y una idea que conviene tener clara antes de tocar nada: limpiar no es maquillar. No estás inventando datos ni «arreglando» la realidad para que salga más bonita. Estás traduciendo lo que ya hay a un formato en el que se pueda operar, sin cambiar lo que significa. «29,99 €» y `29.99` son el mismo precio; «20%» y `0.2` son el mismo descuento. Cuando conviertes uno en otro, no mientes: haces legible para la máquina lo que ya era verdad. La frontera se cruza el día que borras una fila que no te gusta o cambias un valor porque «seguro que es un error»; eso ya no es limpiar, es falsear, y se hace con muchísimo cuidado y dejando constancia.

Cada tipo de suciedad tiene su arreglo. El objetivo: que sumar signifique algo.

### Precios que son texto

La columna `precio_unitario` llega como `"29,99 €"`: texto, con el símbolo del euro pegado y coma decimal (a la española). Pandas no puede multiplicar texto. Para convertirlo en número quitamos el euro, cambiamos la coma por punto (que es el separador decimal que entiende Python), quitamos espacios sobrantes y convertimos a decimal con `.astype(float)`. El orden importa: primero limpias, luego conviertes.

1# "29,99 €" -> 29.99
2lineas['precio_unitario'] = (
3 lineas['precio_unitario']
4 .str.replace('€', '') # fuera el simbolo
5 .str.replace(',', '.') # coma decimal -> punto
6 .str.strip() # fuera espacios
7 .astype(float) # ahora si, a numero
8)

Encadenar operaciones de texto y convertir al final: el patrón para numéricos sucios.

¿Por qué llega así un precio? No es un capricho ni un error: es la localización. Un sistema configurado en español escribe los decimales con coma («29,99») y pega el símbolo del euro porque así lo lee un humano en una factura. Para una persona, «29,99 €» es más claro que «29.99». El problema aparece solo cuando quieres sumar, porque Python entiende el punto como separador decimal y la coma como otra cosa. No estás «arreglando» un fallo del sistema; estás traduciendo del formato humano al formato de cálculo. Esa distinción importa: el dato original no está mal, simplemente no está en el idioma de las operaciones matemáticas.

### Descuentos escritos de tres maneras

El `descuento_pct` es el caso más traicionero, porque el mismo descuento aparece de tres formas: `"20%"` (texto con símbolo), `0.2` (ya como fracción) y `20` (como número entero de porcentaje). Los tres significan lo mismo, pero si los sumas tal cual, `20` cuenta cien veces más que `0.2`. Hay que decidir un formato único —la fracción entre 0 y 1— y llevar todo a él con una pequeña función.

1def norm_desc(x):
2 s = str(x).strip()
3 if s.endswith('%'): # "20%" -> 0.20
4 return float(s[:-1]) / 100
5 v = float(s)
6 return v / 100 if v > 1 else v # 20 -> 0.20 ; 0.2 -> 0.2
7
8lineas['descuento_pct'] = lineas['descuento_pct'].apply(norm_desc)

Una función que reconoce cada formato y lo lleva todos a fracción 0-1.

Este descuento de tres cabezas es el error de datos que más dinero mueve de toda la semana, y es invisible: no falla, no da error, simplemente calcula mal. Si no lo normalizas, los ingresos de la campaña saldrán disparatados y nadie sabrá por qué. Los errores que no rompen nada, solo mienten, son los que hay que cazar con más cuidado.

Consejo de senior: cada vez que limpies una columna, comprueba el resultado antes de seguir. Después de normalizar el descuento, imprime sus valores únicos: si esperabas ver [0.0, 0.1, 0.2, 0.3, 0.4, 0.5] y aparece un 20 suelto, tu función no cazó un formato. Esa comprobación de diez segundos —mirar los valores distintos de una columna recién limpiada— es la red de seguridad más barata que existe. La alternativa es descubrir el fallo el viernes, delante de Carmen, cuando ya es tarde.

### Fechas en tres formatos, categorías a su aire, y cantidades imposibles

La columna de fechas mezcla `"2024-07-05"`, `"05/07/2024"` y variantes. Pandas puede con todas si le dices que el formato es mixto y que el día va delante (formato español): `pd.to_datetime(col, format="mixed", dayfirst=True)`. Las categorías de producto llegan como `"Vestidos"`, `"vestido"` y `"VESTIDOS"` —el mismo grupo escrito de tres formas—, así que las pasamos todas a minúsculas, quitamos espacios y las mapeamos a un nombre canónico único. Y en `cantidad` hay ceros y hasta números negativos, que son imposibles en una venta: los detectamos y los excluimos del cálculo, porque una unidad negativa contaminaría cualquier suma.

Detente un segundo en las categorías, porque el problema es más sutil de lo que parece. Para un humano, «Vestidos», «vestido» y «VESTIDOS» son obviamente lo mismo. Para Pandas son tres textos distintos, tan distintos como «Vestidos» y «Calzado». Si agrupas por categoría sin normalizar, tendrás tres montones de vestidos separados y los ingresos de la categoría estrella saldrán repartidos en tres cifras que nadie sabe que hay que sumar. Es un error silencioso perfecto: no rompe nada, solo divide tu categoría más importante en pedazos y te hace sacar conclusiones sobre trozos. Por eso normalizamos a un valor canónico: una versión oficial a la que apuntan todas las variantes.

Y las cantidades imposibles tienen su propia lección. ¿De dónde sale una venta de cantidad cero o negativa? A veces es una corrección contable (una devolución mal registrada como línea negativa), a veces un fallo de exportación. Da igual el origen: no puedes vender −2 vestidos, así que esas filas no representan una venta y las sacas del cálculo de ingresos. Pero —y esto es lo importante— las cuentas antes de tirarlas. Si mañana Finanzas te pregunta «¿por qué tu total no cuadra con el mío por 154 líneas?», la respuesta «excluí 15 cantidades imposibles y 139 duplicados exactos, aquí están» es la diferencia entre un analista fiable y uno que «perdió filas por el camino».

Mientras arreglas todo esto, Álex se pasa por el canal, que ya ha vivido esta película:

1Alex Pena 11:20
2te lo dije jaja. el orden que a mi me funciona: primero los tipos
3(precio y fecha a numero y fecha de verdad), luego los valores raros
4(cantidades imposibles, duplicados), y al final lo de texto
5(categorias, ciudades). si limpias en ese orden no te pisas.
6y guarda SIEMPRE cuantas filas tiras y por que. te lo van a preguntar.

El orden de limpieza que evita pisarte: tipos, luego valores, luego texto.

Consejo de senior: limpiar no es «borrar lo que molesta». Un nulo puede ser información (un pedido sin campaña es un pedido legítimo de fuera de campaña, no un error). Una cantidad negativa sí es un error y se excluye, pero dejando constancia de cuántas eran. La regla es: entiende por qué está sucio antes de decidir qué haces con ello. Borrar sin pensar es cómo se pierden datos buenos.

### El hueco que hoy no molesta y el viernes sí

Al explorar el catálogo habrás visto que a un puñado de productos les falta el coste (la columna `coste_unitario` viene vacía en un pequeño porcentaje). Hoy, mientras calculas ingresos y unidades, ese hueco no te molesta lo más mínimo: para saber cuánto se vendió no necesitas saber cuánto costó fabricarlo. Es tentador ignorarlo y seguir. Pero un buen analista tiene la vista puesta más allá del cálculo de hoy, y sabe una cosa: para calcular el margen —lo que de verdad se gana— el coste es imprescindible. Y el margen es justo lo que va a decidir la semana.

Aquí hay una lección de anticipación que solo se aprende sufriéndola una vez. Un dato que falta no se evalúa por lo que necesitas hoy, sino por lo que vas a necesitar. Si dejas los costes vacíos sin mirarlos, el jueves —cuando cruces todo para sacar el margen del Alma— te encontrarás con un cálculo que da resultados raros o vacíos y no sabrás por qué, con el viernes encima. La buena noticia para este caso concreto: el Vestido Alma, el producto que va a importar, tiene su coste bien puesto (30,10 €). Pero eso lo sabes porque lo comprobaste, no porque tuvieras suerte. Comprobar que el dato que vas a necesitar está donde tiene que estar, antes de necesitarlo, es lo que separa una semana tranquila de un viernes de pánico.

Consejo de senior: cuando explores, no mires solo si los datos sirven para el cálculo de hoy; pregúntate qué vas a calcular esta semana y comprueba que las columnas para ello están sanas. Si el jueves necesitas costes, revisa los costes el martes. Descubrir que falta un dato clave con margen de días es un contratiempo; descubrirlo la víspera de la demo es una crisis. La anticipación no es un lujo de senior: es lo que te compra tiempo para reaccionar cuando algo falta.

### El patrón que se repite: normalizar texto

Habrás notado que la limpieza de texto —categorías, tallas, ciudades, motivos de devolución, género— siempre acaba haciendo lo mismo, con pequeñas variantes. No es casualidad: casi toda la suciedad de texto viene de que los humanos escriben igual de tres maneras distintas, y hay un puñado de operaciones que reconcilian esas variantes. Aprenderlas como un patrón, y no como trucos sueltos, es lo que hace que la próxima columna sucia no te asuste. Estas son las piezas, y casi siempre se combinan en este orden:

  • .str.strip() — quita los espacios sobrantes al principio y al final. " Madrid " y "Madrid" son el mismo sitio, pero para el ordenador no hasta que quitas los espacios. Es el primer paso casi siempre, porque los espacios invisibles son la causa número uno de valores que «deberían ser iguales» y no lo son.
  • .str.lower() (o .str.upper()) — unifica mayúsculas y minúsculas. "VESTIDOS", "Vestidos" y "vestidos" se vuelven el mismo texto. Se elige minúsculas o mayúsculas por convención; da igual cuál, mientras sea siempre la misma.
  • .map(diccionario) — traduce cada variante ya normalizada a un valor oficial (el canónico). Es el paso que convierte "vestido" y "vestidos" en un único "Vestidos". Necesita que antes hayas unificado espacios y mayúsculas, o el diccionario tendría que listar todas las combinaciones.
  • .str.replace(viejo, nuevo) — quita o cambia trozos concretos: el "€" de un precio, el "Talla " de "Talla M", el "%" de un descuento. Es el bisturí para la suciedad que va pegada al valor.

La lógica de fondo es siempre la misma y conviene decirla en voz alta: primero reduces las variantes accidentales (espacios, mayúsculas), y solo entonces traduces al valor oficial. Si lo haces al revés —intentar mapear antes de normalizar— acabas manteniendo un diccionario gigante con todas las grafías posibles, que es justo el trabajo que la normalización te ahorra. Reducir primero, traducir después. Ese orden es el que convierte cinco líneas de limpieza en algo que funciona para cualquier columna de texto sucio que te echen.

La normalización de texto tiene un límite que ya viste con las ciudades: .str.lower() y .str.strip() arreglan mayúsculas y espacios, pero no acentos. "Málaga" y "Malaga" siguen siendo distintas después de bajarlas a minúsculas. Cuando el dato lleva acentos inconsistentes, hace falta un paso más (quitar los acentos a propósito, o un diccionario que los contemple). No des por limpia una columna de texto solo porque le pasaste strip y lower: comprueba el número de valores únicos y contrástalo con lo que sabes del negocio.

### Qué hacer con un hueco: la decisión que no es automática

Los valores que faltan —los nulos— son el caso donde más se nota si tienes criterio o si aplicas recetas. La tentación del junior es tratarlos siempre igual: o borrar todas las filas con huecos, o rellenarlos con la media y seguir. Las dos cosas hechas a ciegas hacen daño. Un nulo no es un problema uniforme: es una pregunta que hay que responder mirando qué significa ese hueco en concreto. Y hay tres respuestas posibles, según lo que el hueco represente.

Piénsalo con los tres huecos reales que tienes delante. El primero: en `pedidos`, la columna `campana_id` está vacía en muchas filas. ¿Es un error? No: un pedido de fuera de campaña legítimamente no tiene campaña. Ese hueco es información —significa «este pedido no vino de una promoción»— y borrarlo sería tirar pedidos buenos. Se deja como está. El segundo: en `clientes`, 150 fechas de registro están vacías. No es información, es un dato que se perdió, pero tampoco puedes inventarlo. Como no afecta a lo que vas a calcular esta semana (ingresos, devoluciones), lo dejas marcado como desconocido y sigues, sin rellenar. El tercero: en `productos`, algún coste está vacío. Ese sí te haría falta para calcular margen, así que ahí tienes que decidir con Diego si excluir esos productos del cálculo de margen o buscar el coste en otra fuente. Tres huecos, tres decisiones distintas.

Ante un hueco, tres caminos según lo que signifique. Nunca una receta única.

Rellenar los nulos con la media «para que no molesten» es uno de los reflejos más peligrosos del principiante. Si rellenas 150 fechas de registro vacías con la fecha media, te inventas 150 clientes que se registraron un día que no existió, y cualquier análisis de antigüedad quedará contaminado con datos falsos que parecen reales. Rellenar es una decisión legítima en algunos casos, pero nunca por defecto y nunca sin dejar dicho que lo hiciste. Un dato inventado con buena intención sigue siendo un dato inventado.

### El orden importa: normaliza antes de agrupar, no después

Hay un error de secuencia que arruina más análisis que cualquier fórmula mal escrita, y es tan silencioso que casi nadie lo ve venir. Es este: agrupar por una columna sucia. Imagina que tienes prisa y decides ir «al grano»: sumas las ventas por categoría sin haber unificado antes «Vestidos», «vestido» y «VESTIDOS». Pandas hará exactamente lo que le pides, sin quejarse, y te devolverá tres filas de vestidos, cada una con un trozo de las ventas. Ninguna es la cifra real de la categoría, y la tabla parece perfectamente sana. No hay error, no hay aviso rojo: solo un número partido en tres que suma bien pero significa mal.

La regla que se deriva de esto es sencilla y salva análisis: primero se limpia, luego se calcula, nunca al revés. Concretamente, cualquier columna por la que vayas a agrupar (categoría, temporada, ciudad, canal, talla, motivo) tiene que estar normalizada antes de que la toque un groupby. Si el orden se invierte, el fallo no es que el programa reviente —ojalá, porque entonces lo verías—; el fallo es que da un resultado creíble y equivocado. Y un resultado creíble y equivocado es lo más caro que existe en este oficio, porque nadie lo cuestiona hasta que ya se ha tomado una decisión con él.

Agrupar por una columna sucia no da error: da un número creíble y falso

Consejo de senior: cuando heredes el análisis de otra persona y una categoría te parezca sospechosamente baja, lo primero que miro yo es si esa columna estaba limpia antes del groupby. Muchísimas veces «la categoría X vende poco» resulta ser «la categoría X está escrita de cuatro formas y solo has contado una». Antes de buscar explicaciones de negocio a una cifra rara, descarta que sea un problema de limpieza. La navaja de Occam del analista: antes que una teoría elaborada, comprueba si simplemente el dato estaba sucio.

1Alex Pena 13:20
2te veo limpiando categorias, bien
3un truco que a mi me costo aprender: no limpies "todo" porque si
4limpia lo que vas a USAR. si vas a agrupar por categoria, limpia
5categoria. si vas a sumar precios, limpia precio. gastar la manana
6puliendo una columna que no toca el analisis es la trampa clasica
7del que quiere que todo este perfecto y no entrega nada

La limpieza se dirige por lo que vas a calcular, no por el afán de perfección.

El apunte de Álex esconde una tensión real del oficio. Por un lado, no puedes calcular sobre datos sucios. Por otro, podrías pasarte una semana entera puliendo columnas que nadie va a usar y no entregar nada. El equilibrio es limpiar con propósito: mira qué preguntas vas a responder, identifica qué columnas entran en esas respuestas, y limpia esas. Las demás, déjalas como están y sigue. Un análisis perfecto que llega tarde no le sirve a Carmen; un análisis honesto que llega el viernes con las columnas que importan bien limpias, sí. La limpieza no es un fin, es un medio, y confundir las dos cosas es cómo un junior meticuloso se convierte en un junior que no termina nada.

Al terminar la limpieza tienes los datos listos para que sumar, agrupar y comparar signifiquen de verdad lo que crees. Mañana empiezas a calcular los primeros KPIs de la campaña. Y ahí aparecerá el primer sobresalto del oficio: dos formas razonables de calcular «los ingresos» que dan dos cifras distintas, y la pregunta de Diego que lo resuelve.

Regístrate para guardar tu progreso.