Saltar al contenido

lección 3

Día 2 (mañana) — Antes de calcular, limpiar (y decidir tú qué es el margen)

Con el inventario de ayer delante, hoy se limpia con criterio: fechas en tres formatos (una con el mes en inglés), importes con «€», comisión con «%», categorías en varias grafías, duplicados y outliers, y los dos nulos con significado. Y tomas la decisión de peso de la semana: qué costes entran en el margen de contribución por pedido. Cierras confirmando el titular de Gonzalo con la primera consulta SQL.

55 min

### Un «margen» sin decir qué costes incluye no es un número

Imagina que le preguntas a tres personas de RutaSabor cuánto deja un pedido de La Brasa y te dan tres cifras distintas, todas correctas. Finanzas dice «doce euros, que es lo que factura». Compras dice «unos siete y medio, después de los ingredientes». Y alguien de dirección, que ha oído hablar de la comisión, dice «un euro y pico, si le quitas lo de Glovo». No mienten: es que la palabra «margen» está vacía hasta que dices qué costes le restas. Y ahí está la primera gran decisión de tu semana, la que Irene te deja entera a ti: definir qué es el margen de contribución por pedido antes de calcular nada. No hay una fórmula única que sea la correcta; hay una que es defendible, y tendrás que defenderla ante Clara el viernes.

Antes de llegar a esa decisión hay que limpiar, porque no se puede calcular un margen sobre importes que son texto, fechas que no parsean y categorías escritas de diez maneras. Ayer hiciste el inventario del desastre; hoy lo arreglas de una vez, con la lista delante. La limpieza de este caso no tiene nada que no hayas visto antes por separado —parsear fechas, quitar el símbolo del euro, normalizar categorías, deduplicar—, pero sí las tiene todas juntas y sobre datos que vienen de cuatro exportaciones distintas. Vamos por orden, de lo mecánico a lo que exige criterio.

Antes de empezar, fija una prioridad: no toda la suciedad cuesta lo mismo si la tratas mal. Equivocarte al normalizar el tipo de incidencia solo afecta a un desglose secundario; equivocarte al parsear la comisión hunde todo el margen. Así que ordena la limpieza por impacto: primero lo que entra en el número que decide (importes, comisión, coste de ingredientes, estado para filtrar entregados, y los dos nulos con significado), y después lo que es cosmético o de contexto (grafías de categorías secundarias, tiempos de entrega). Limpiar con esta jerarquía en la cabeza te evita dedicar media mañana a normalizar una columna que no entra en el margen mientras dejas sin revisar la comisión, que lo decide todo.

### Las fechas: cuidado con el mes en inglés

Las fechas de RutaSabor vienen en tres formatos mezclados dentro de la misma columna, porque cada canal exporta como le da la gana. Verás fechas como «2024-03-12 20:15» (formato ISO, el ordenado), «12/03/2024 20:15» (el formato de toda la vida en España, día primero) y «12 Mar 2024 20:15». Esta última tiene una trampa preciosa: el mes está en inglés abreviado, «Mar» de March, no de marzo. Es lo normal en exportaciones de plataformas internacionales, aunque la empresa sea española. Y es una trampa porque las herramientas de parseo de fechas entienden los meses en inglés, no en español: si probaras a parsear «12 Mar 2024» funcionaría, pero «12 Marzo 2024» daría un error o un hueco. Como aquí vienen en inglés, la buena noticia es que parsean; la mala sería confiarse y usar un solo formato.

La forma robusta de parsear una columna con formatos mezclados es decirle a la herramienta que los acepte todos y que ponga día primero, con pd.to_datetime(..., format="mixed", dayfirst=True). El argumento format="mixed" le dice que cada fila puede tener un formato distinto; dayfirst=True le dice que cuando vea «12/03/2024» interprete 12 como día, no como mes (si no, «12/03» lo leería a la americana como diciembre... perdón, como 3 de diciembre, y te cambiaría medio calendario). Y errors="coerce" convierte en hueco (NaT, el nulo de las fechas) lo que no consiga parsear, en vez de reventar. Vas a comprobar, en un ejercicio, que con format="mixed" no queda ninguna fecha sin parsear, y que si probaras solo el formato ISO se te quedarían decenas de miles fuera: la prueba visible de por qué el parseo robusto importa.

Consejo de senior: cuando una columna de fechas viene sucia, nunca asumas un solo formato aunque las primeras filas lo tengan. Mira el final del fichero, no solo el principio: las exportaciones concatenan fuentes y el formato raro suele estar al fondo. Y siempre parsea con errors="coerce" y luego cuenta los NaT: si salen cero, tu parseo cubre todos los formatos; si salen unos pocos, ahí tienes las filas rebeldes para investigar; si sale un montón, tu formato está mal y lo estás perdiendo casi todo sin enterarte.

Merece la pena entender por qué una empresa española tiene fechas con el mes en inglés, porque no es un error y te va a pasar en muchos proyectos. Las plataformas de reparto y la mayoría del software del mundo nacieron en inglés y guardan las fechas en formatos anglosajones por defecto. Cuando RutaSabor recibe la exportación de un marketplace, hereda ese formato. Y aquí hay una asimetría afortunada: las herramientas de análisis (Pandas, SQL) también hablan inglés, así que entienden «Mar» como marzo, «Jun» como junio. Si las fechas vinieran con el mes en español —«Mar» de marzo pero escrito como «mar» o «marzo»—, no las entenderían y tendrías que traducirlas a mano. Que vengan en inglés, paradójicamente, te facilita el trabajo. Es una de esas cosas que parecen un fastidio (¿por qué en inglés?) y resultan ser una suerte disfrazada.

### Los importes y las comisiones: quitar el símbolo, decidir la coma

Los importes vienen como texto con el símbolo del euro y la coma decimal española: «12,50 €», «3,70 €». Para convertirlos en números hay que quitar el «€», quitar los espacios, y cambiar la coma por punto (que es lo que entiende el ordenador como separador decimal). Cuidado con los separadores de miles: si algún importe llegara como «1.250,50 €», habría que quitar primero el punto de los miles y luego cambiar la coma. Y aquí aparece una sorpresa que ya has visto en otros encargos: no todo parsea. Unos pocos importes vienen con basura literal («N/D» y similares) que no es un número, y al convertir hay que dejarlos como hueco en vez de reventar. Por eso se usa pd.to_numeric(..., errors="coerce"): lo que no es número se vuelve NaN, y esos pedidos se excluyen del margen.

La comisión de cada canal es el caso más importante de esta familia, porque de ella depende todo el margen neto. Viene en la tabla de canales como texto con el símbolo de porcentaje: «30 %», «28 %», «25 %», «6 %». Y para picar más, alguna llega ya como decimal con coma, «0,25», para obligarte a decidir. Lo que necesitas es una fracción entre 0 y 1: Glovo 0,30, Uber Eats 0,28, Just Eat 0,25, app propia 0,06. La regla que resuelve las dos formas de una vez: quita el «%», cambia la coma por punto, conviértelo a número, y si el resultado es mayor que 1 (o sea, venía como «30» y no como «0,30»), divídelo entre 100. Así «30 %» y «0,30» acaban los dos en 0,30. Este detalle no es cosmético: si te equivocas y dejas la comisión como 30 en vez de 0,30, el margen neto de La Brasa saldría negativo por kilómetros y pensarías que la empresa pierde dinero en cada pedido.

La regla que normaliza «30 %» y «0,30» a la vez: si tras limpiar el número es mayor que 1, se divide entre 100. Equivocarse aquí hunde todo el margen neto
Normalizar el estado colapsa diez grafías en tres valores. Solo «entregado» cuenta para el margen, y sin normalizar antes, filtrar por él perdería casi la mitad de los pedidos entregados

### Las categorías, los duplicados y los outliers

Las categorías inconsistentes se arreglan como ya sabes: pasar a minúsculas, quitar espacios sobrantes, y mapear cada grafía a un valor canónico. El estado del pedido se reduce a tres valores («entregado», «cancelado», «en_reparto»), y solo «entregado» cuenta para el margen. La ciudad de la cocina a cuatro («Madrid», «Barcelona», «Valencia», «Zaragoza»). El tipo de incidencia a sus categorías reales. La marca y el tipo de cocina, a sus valores canónicos. Cada mapeo es un diccionario que traduce las grafías sucias al valor bueno, y el objetivo es que al agrupar no se te parta una categoría real en cinco trozos.

Los duplicados se quitan con drop_duplicates sobre la clave que debería ser única. En pedidos, deduplicas por pedido_id, porque el webhook del marketplace reenvía el mismo pedido; en líneas, por linea_id; en incidencias, por incidencia_id, porque a veces el mismo caso se registra dos veces en atención al cliente. Deduplicar antes de contar o sumar es innegociable: si no, cuentas pedidos que no existen y sumas GMV de más. Y los outliers —valores imposibles— se detectan y se marcan para excluirlos del cálculo que afecten: tiempos de entrega de cero, negativos o de 999 minutos; y en productos, platos cuyo coste de ingredientes es mayor que su precio de venta (imposible: venderías a pérdida seguro) o cuyo precio o coste es cero. Esos productos rotos se apartan porque estropearían el coste de ingredientes.

Los dos nulos con significado que avisó Nacho vuelven hoy, y son la decisión que abre el jueves. El cliente_id vacío NO se tira: se marca con una bandera es_invitado y se conserva, porque son pedidos reales (más de un tercio del total). Y la ausencia de fila en incidencias NO es un hueco a rellenar con NaN: es coste de incidencia cero, y obliga a cruzar con un LEFT JOIN rellenando la ausencia con 0. Si hoy decides mal cualquiera de las dos, el jueves el margen te saldrá delirante y no sabrás por qué. Anótalo en tu decisión de limpieza, con su justificación, para poder defenderlo.

Los dos nulos con significado del caso: el invitado se conserva con una bandera, y la ausencia de incidencia es coste cero (LEFT JOIN). Tratarlos como errores es el fallo que hunde el análisis

### La decisión del día: qué es el margen de contribución

Con los datos limpios, toca la decisión de peso. ¿Qué costes le restas al importe cobrado para obtener el margen de un pedido? Hay varias opciones sobre la mesa, y cada una cuenta una historia distinta. Podrías restar solo los ingredientes: eso es el margen bruto, el que mira Compras, y es el que enamora a Gonzalo porque deja a La Brasa con buena pinta. Podrías restar además la comisión del canal: eso es el margen neto de comisión, y ya es un coste real y variable que Compras no ve. Y podrías restar también las incidencias: eso es el margen de contribución completo. La razonable, la que vas a defender, es la completa: margen de contribución = importe cobrado − coste de ingredientes − comisión del canal − compensación de incidencias. Por qué esa y no otra: porque la comisión y las incidencias son costes reales y variables que dependen del pedido, y dejar cualquiera de las dos fuera es justo el error que ha cometido RutaSabor hasta hoy, que solo mira el bruto o el GMV.

La misma palabra, tres números. El error de la empresa es quedarse en el bruto; la decisión defendible es el margen de contribución, que resta los tres costes reales del pedido

Guarda esta decisión por escrito, con su justificación, porque es lo primero que te va a preguntar Clara: «¿qué has metido en ese margen?». Un margen sin definición es un número que cualquiera puede discutir; un margen definido y justificado es un número que aguanta. Y una advertencia para hoy: no calcules todavía la contribución completa. Hoy limpias, defines el margen y confirmas el titular de Gonzalo con lo más simple —pedidos y GMV por marca—. El margen neto de comisión llega mañana; el de contribución, el jueves. Se descubre por capas, y cada capa te enseña algo. Si te adelantas y calculas todo hoy, te pierdes el momento en el que ves cómo se desinfla lo que parecía sólido.

Consejo de senior: cuando definas una métrica de negocio, escríbela como una fórmula explícita con cada término nombrado, y guarda al lado por qué incluyes o excluyes cada coste. No es burocracia: es lo que separa un análisis que convence de uno que se discute en la reunión. El día que alguien te diga «pero eso no es el margen de verdad», tu respuesta no es defenderte, es enseñar la definición que acordasteis y preguntar qué término cambiaría. La discusión pasa de tu credibilidad a la fórmula, que es donde debe estar.

### De dónde viene la idea de «margen de contribución»

El margen de contribución no es un invento de RutaSabor ni de las cocinas fantasma: es una de las ideas más viejas y más potentes de la contabilidad de gestión, y nació hace casi un siglo para resolver exactamente el problema que tienes hoy. A principios del siglo XX, cuando las fábricas empezaron a producir muchos productos distintos en las mismas líneas, los directivos se dieron cuenta de que el beneficio total no les decía qué producto convenía fabricar más. Un producto podía vender muchísimo y, sin embargo, dejar menos que otro que vendía poco, porque sus costes variables —los que crecen con cada unidad— eran mayores. Así nació la distinción entre coste fijo (el alquiler de la fábrica, que pagas produzcas lo que produzcas) y coste variable (la materia prima de cada unidad), y con ella el margen de contribución: lo que cada unidad aporta para cubrir los costes fijos una vez pagados sus costes variables.

Traducido a RutaSabor: la cocina, el alquiler y el personal son costes fijos que se pagan haya diez pedidos o mil. Los ingredientes, la comisión del canal y las incidencias son costes variables que crecen con cada pedido. El margen de contribución por pedido es lo que cada pedido aporta para pagar las cocinas y el personal después de cubrir sus propios costes variables. Y la lección de hace cien años sigue valiendo: la marca que más factura no es necesariamente la que más contribuye, porque sus costes variables pueden comerse casi todo. Estás a punto de reproducir, con datos de reparto y en SQL, exactamente el descubrimiento que hicieron los contables de las fábricas de los años veinte. Los negocios cambian; la aritmética del margen, no.

Esto explica también por qué tu definición del margen tiene que ser explícita y defendible. Los costes variables de un pedido no son obvios: ¿cuenta el packaging? ¿Cuenta la parte proporcional del sueldo del cocinero? En este caso te quedas con los tres que puedes medir por pedido con los datos que tienes —ingredientes, comisión e incidencias— y dejas fuera los que no puedes atribuir a un pedido concreto sin inventártelos. Esa es una decisión razonable y defendible, y la clave es que la escribas: «margen de contribución = importe − ingredientes − comisión − incidencias», y al lado, «no incluyo packaging ni mano de obra porque no puedo atribuirlos por pedido con estos datos». Cuando Clara pregunte, no improvisas: enseñas la definición.

No caigas en la tentación de meter en el margen costes que no puedes medir por pedido. Es tentador «estimar» el packaging como un 3% o repartir el sueldo del cocinero entre los pedidos, pero cada estimación que añades es una suposición que alguien puede discutir, y encima no cambia el hallazgo (La Brasa deja el peor margen con o sin esos costes, porque el problema son la comisión y las incidencias, que sí mides). Un margen construido solo con costes medibles es más pequeño pero más sólido: cada término tiene detrás un dato, no una suposición. Prefiere siempre un número defendible a uno completo pero discutible.

Consejo de senior: escribe la limpieza como código reproducible, no como una serie de arreglos manuales sobre el fichero. Cada semana llega una exportación nueva con la misma suciedad, y si limpiaste a mano tendrás que volver a hacerlo todo; si escribiste un script que parsea, normaliza y deduplica, lo vuelves a lanzar y ya está. La limpieza reproducible es también la que puedes explicar y auditar: cuando alguien pregunte «¿por qué este pedido no cuenta?», tu respuesta está en una línea del script, no en tu memoria de lo que hiciste el martes. Un análisis que no puedes reproducir no es un análisis, es una anécdota.

### El orden de la limpieza: un pipeline, no un montón de arreglos

La limpieza tiene un orden lógico, y saltárselo cuesta caro. Primero se deduplica, porque todo lo que venga después —contar, sumar, promediar— sale mal si hay filas repetidas. Después se parsean los tipos (fechas e importes a sus formatos correctos), porque no puedes filtrar por fecha ni sumar importes que son texto. Luego se normalizan las categorías (estado, ciudad, marca, tipo de incidencia), porque agrupar por una columna sin normalizar parte cada grupo en trozos. Después se validan los outliers y las incoherencias (tiempos imposibles, coste mayor que precio), marcándolos para excluirlos. Y por último se tratan los nulos con significado, decidiendo qué hacer con cada uno. Hecho en ese orden, cada paso se apoya en el anterior; hecho en desorden, te encuentras arreglando lo mismo dos veces.

El pipeline de limpieza en orden: deduplicar, parsear, normalizar, validar y tratar los nulos. Hacerlo en este orden evita arreglar lo mismo dos veces y deja las tablas listas para el análisis

Un detalle fino sobre la deduplicación que distingue a quien ha sufrido estos datos: en los pedidos, deduplicar solo por pedido_id no siempre basta, porque el webhook del marketplace a veces reenvía el pedido con el mismo identificador y a veces con una pequeña variación. Por eso conviene mirar también la referencia externa (ref_externa), que es el identificador que asigna el propio marketplace. Si dos filas comparten pedido_id o comparten ref_externa, son el mismo pedido. En este caso la deduplicación por pedido_id resuelve la mayoría, pero saber que existe la ref_externa como segunda clave es el tipo de conocimiento que te salva cuando los números no cuadran del todo. Nacho lo sabe porque lo ha sufrido; ahora lo sabes tú.

Los ejercicios de hoy te llevan por la limpieza en el orden en que la harías de verdad —deduplicar, parsear fechas, parsear importes, normalizar el estado, marcar los invitados, apartar los productos rotos— y cierran con la primera consulta SQL sobre las tablas ya limpias: pedidos y GMV por marca, para confirmarle a Gonzalo, tal cual, que La Brasa es la número uno. Es verdad, y hay que decírselo. Lo que Gonzalo no sabe todavía es que ser la número uno en facturación no es lo mismo que ser la número uno en margen. Eso lo descubrirás tú, por capas, a partir de mañana.

Regístrate para guardar tu progreso.