GAA1104 · Big Data e Inteligencia de Negocios · Clase 13

La demanda: nivel, cola y estación · Control C4

Resumir la demanda con estadística descriptiva sin caer en las trampas —el promedio que esconde la cola, el percentil que se confunde con un porcentaje, la correlación que se deshace por dentro— y detectar patrón estacional y tendencia traducidos a decisión.

El notebook de esta clase: https://gaa1104.vercel.app/notebooks/clase-13.ipynb

Ing. en Administración · mención Finanzas · Prof. Tomás Fernández

Clase 13 · el gráfico de hoy

Diagrama de caja (nivel y cola) y línea (estacionalidad)

Dos preguntas: cuánto se vende normalmente y cuánto se sale de lo normal (distribución), y si eso se repite cada año (tiempo).

La forma la decide el trabajo que hace el dato —magnitud, identidad, polaridad, cambio en el tiempo, distribución—, no el gusto. Regla del curso: un solo eje.

Apertura — Active recall · 8 min

Lo que quedó planteado la clase anterior (se proyecta la pregunta, NO la respuesta):

«Ya tienes el dato limpio y modelado. Si te preguntan cuánto compra "un cliente típico", ¿contestas con el promedio?»

Y antes, los 90 segundos que cierran la otra pregunta de la clase 12 —la que respondía el notebook espejo del sábado:

Apertura — Active recall · 8 min
«pandas dio 1.009.103 y SQL 1.009.104. ¿Cuál era la fila culpable y qué convención la explica?» → el redondeo del medio decimal: DuckDB ROUND() va half-up, numpy va al par. Una fila. Se cierra aquí y no se vuelve a tocar.

Dos preguntas más, en silencio, 60 segundos (espaciado 1–3–7–21):

Apertura — Active recall · 8 min
  1. (clase 11) ¿Por qué dim_fecha se construye una sola vez, y qué columnas le pusimos que no estaban en el archivo original?
  2. (clase 5) Sobre la venta de línea de Austral usamos el IQR. ¿Qué es exactamente un atípico según ese criterio, y por qué se pregunta antes de borrarlo?

Par (1 min) → plenaria (2 min).

Cerrar la pregunta colgada con ellos. Recoger las respuestas y no corregir todavía: hoy la clase es la corrección.

Apertura — Active recall · 8 min
«El que dijo "depende" tiene razón y no sabe cuánta. Vamos a ponerle número al "depende".»
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

La demanda de la alimentaria (12 min)

Partir por la Harina, que es la bien portada, para instalar el vocabulario:

h = f18[f18.producto == "Harina de trigo"].groupby("fecha").cantidad.sum()
h.describe()
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
Harina de trigo, demanda diaria
días1.096
promedio152,0
desviación55,7
mínimo44
P25112
mediana (P50)144
P75188
P90232
máximo377
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

El nombre de lo que están haciendo, dicho una vez y en voz alta: «esto se llama análisis exploratorio de datos, EDA. Es el requisito 2.4 del Parcial 2 y ahí aparece con ese nombre. No es un paso previo al trabajo: es el trabajo.»

La pregunta de negocio que ordena el bloque: «si compras 152 unidades de harina todos los días, ¿cuántos días te quedas corto?» La respuesta es casi la mitad de los días: la mediana es 144.

Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
«El promedio planifica para el cliente promedio. El cliente promedio no existe. Existe el día de 44 unidades y existe el día de 377.»

Y la tabla completa, que es donde aparece el problema de verdad:

Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
d = f18.groupby(["producto", "fecha"]).cantidad.sum().reset_index()
t = d.groupby("producto").cantidad.agg(
        promedio="mean", mediana="median", desv="std",
        p75=lambda s: s.quantile(0.75), maximo="max")
t["cv"] = (t.desv / t.promedio).round(2)
t.sort_values("cv", ascending=False)
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
productopromediomedianadesviaciónP75máximoCV
Azúcar128,3117,0247,8147,08.2051,93
Sal148,7143,0199,1171,06.5671,34
Levadura51,146,061,662,01.9741,21
Aceite vegetal87,984,031,1109,02300,35
Arroz104,399,536,2126,03020,35
Cacao44,340,020,058,01290,45
Café65,662,024,780,01670,38
Harina de trigo152,0144,055,7188,03770,37
Leche en polvo77,474,027,794,02180,36
Mantequilla61,258,024,177,01540,39
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

Parar. Proyectar. Callarse. La columna del máximo grita sola. «El azúcar vende 128 al día en promedio y tuvo un día de 8.205. ¿Qué pasó ese día?» Que lo busquen ellos:

f18.nlargest(3, "cantidad")[["id_venta", "fecha", "producto", "cantidad"]]
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
id_ventafechaproductocantidad
59092018-07-19Azúcar8.205
145762020-10-17Sal6.567
93852019-06-12Levadura1.974

Tres líneas. De 11.541. Y la medición que cierra el bloque:

lim = d[d.cantidad < 1000]
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
productopromedio conpromedio sinmediana conmediana sinCV conCV sin
Azúcar128,3120,9117,0117,01,930,35
Sal148,7142,9143,0143,01,340,31
Levadura51,149,346,046,01,210,42

El número del día, escrito en la pizarra:

Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
Una línea de 11.541 movió el promedio diario del azúcar 7,4 unidades. La mediana no se movió ni un decimal.
«La mediana no es "el promedio para cobardes". Es el estadístico que no se puede secuestrar por un dato. Y en un archivo que sale de un ERP siempre hay un dato capaz de secuestrarte el promedio.»
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

CV = desviación / promedio. Ruido relativo, sin unidades, comparable entre productos. 0,35 es demanda tranquila; 1,93 es o un producto salvaje o un dato sucio — y la primera tarea es averiguar cuál de los dos.

El espejo del caso propio: el DSO no se informa con el promedio (8 min)

Volver a Austral. Declarar en voz alta que el dataset es sintético.

«La demanda de una alimentaria y la cartera de una distribuidora no se parecen en nada. La distribución sí. Y la decisión también.»
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

La tabla l ya está creada desde antes de clase —se proyecta y se comenta, no se teclea; el SELECT sí se corre en vivo:

Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
CREATE OR REPLACE TABLE l AS
SELECT UPPER(strip_accents(TRIM(segmento)))              AS segmento,
       TRY_CAST(fecha_emision AS DATE)                   AS f_emision,
       TRY_CAST(NULLIF(fecha_pago,'') AS DATE)           AS f_pago
FROM read_csv_auto('datos/finanzas/ventas_extracto.csv', all_varchar = true);

SELECT segmento,
       COUNT(*)                                  AS lineas_pagadas,
       ROUND(AVG(f_pago - f_emision), 1)         AS promedio,
       MEDIAN(f_pago - f_emision)                AS mediana,
       QUANTILE_CONT(f_pago - f_emision, 0.75)   AS p75,
       QUANTILE_CONT(f_pago - f_emision, 0.90)   AS p90,
       MAX(f_pago - f_emision)                   AS maximo
FROM l
WHERE f_pago IS NOT NULL AND f_emision IS NOT NULL
GROUP BY 1
ORDER BY promedio DESC;
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
segmentolíneas pagadaspromediomedianaP75P90máximo
Público31791,686,0113146164
Construcción1.30872,572,097118163
Minería91160,661,082101142
Industrial1.55156,257,07498134
Retail1.25455,757,07497134
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min

Tres lecturas, en este orden. La tercera es la que vale.

  1. Sólo un segmento tiene cola. En Público el promedio está 5,6 días sobre la mediana. En Industrial y Retail el promedio está por debajo: la cola también puede caer para el otro lado.
  2. El P75 y el P90 cuentan lo que el promedio calla. La cuarta parte de las líneas de Público pasa los 113 días y una de cada diez pasa los 146.
  3. La decisión, verificada en vivo:
Promedio, mediana y la cola: la alimentaria y el DSO · 20 min
SELECT COUNT(*) FROM l WHERE segmento = 'PUBLICO' AND (f_pago - f_emision) > 120;
-- 64
64 de las 317 líneas pagadas del sector Público —una de cada cinco— se cobraron después de 120 días. Si tu política de cobranza gatilla en el promedio (91,6), esas 64 ya llevaban un mes de atraso cuando la alerta sonó. Si gatilla en el P75 (113), llegas antes.
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min

El boxplot: los tres atípicos, vistos (6 min)

Hasta aquí la cola se leyó en una tabla. Ahora se mira, y es el primer gráfico de verdad del ramo.

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
import matplotlib.pyplot as plt

d.boxplot(column="cantidad", by="producto", figsize=(11, 5), rot=45)
plt.suptitle("")
plt.title("Demanda diaria por producto — con los tres días atípicos")
plt.ylabel("unidades por día")
plt.show()
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min

Cómo se lee una caja, dicho una vez y despacio —y no es materia nueva, es el IQR de la clase 5 dibujado:

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min

Parar. Proyectar. Callarse otra vez. El gráfico sale ilegible: tres puntos aplastan las diez cajas contra el eje. Y esa fealdad es el hallazgo.

«Miren lo que hicieron tres filas de 11.541: dejaron el gráfico inservible. Eso es exactamente lo que le hicieron al promedio, sólo que en la tabla no se veía tan feo.»
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
lim.boxplot(column="cantidad", by="producto", figsize=(11, 5), rot=45)
plt.suptitle("")
plt.title("Demanda diaria por producto — sin los tres días atípicos")
plt.ylabel("unidades por día")
plt.show()

La regla de la que nadie se escapa, y se dicta: los dos gráficos van al informe, los dos, con la leyenda de qué se sacó y por qué. Publicar sólo el segundo es publicar un dato limpio sin decir que se limpió.

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
Por qué esto se hace en Python y no en Power BI. El Encargo del Parcial 3 pide textual, en su Etapa 3.3, «compara peso y talla entre las tres categorías con diagramas de caja», y el entregable de sus Etapas 1 a 3 es un .ipynb. La caja tiene que salir del notebook.
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min

El percentil, que es una decisión (3 min)

P75 = el punto que deja al 75 % de los días por debajo. Si compras el P75, te quedas corto uno de cada cuatro días. Si compras el P90, uno de cada diez. Si compras el máximo histórico, ninguno — y pagas bodega todo el año para el peor día que existió.
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
DecisiónPercentilQué te cuesta pasarteQué te cuesta quedarte corto
Stock de seguridad (compras)Base = mediana, colchón = P75Capital inmovilizado, bodega, mermaQuiebre de stock, venta perdida, cliente que se va
Política de cobranza (Finanzas)Gatillo = P75 de los días de cobroLlamar a un cliente que iba a pagar igual: cuesta relaciónEnterarse tarde: cuesta capital de trabajo
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
«El percentil que eliges es tu aversión al riesgo, puesta en un número. No lo elige la estadística: lo elige quien paga la bodega o quien firma la línea de crédito. Tú lo calculas y le muestras el costo de cada opción.»

Harina: base 144 (mediana), colchón hasta 188 (P75), 232 si el quiebre es caro (P90). 44 unidades de colchón. Ése es el número que se lleva el jefe de compras, no el 152.

La correlación que se deshace por dentro (6 min)

El número, primero, sin comentarios:

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
f18[["cantidad", "precio_unitario"]].corr()
r = −0,225 entre cantidad y precio unitario, sobre las 11.541 líneas desde 2018.

La tentación, con las palabras exactas con que la dice un gerente: «bajemos el precio un 10 % y vendemos más.» No discutir. Medir —se proyecta corrido, tres líneas:

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
for p, g in f18.groupby("producto"):
    print(p, round(g[["cantidad", "precio_unitario"]].corr().iloc[0, 1], 3))
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
productor dentro del producto
Leche en polvo+0,176
Arroz+0,137
Café+0,118
Sal+0,041
Harina de trigo+0,036
Azúcar+0,016
Levadura+0,004
Mantequilla+0,004
Cacao+0,002
Aceite vegetal−0,056
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
La correlación global es −0,225. Dentro de cada producto va de −0,056 a +0,176, y OCHO de los diez son positivos.

Silencio. Y después, de dónde salía el −0,225:

m = f18.groupby("producto").agg(precio=("precio_unitario", "mean"),
                                cantidad=("cantidad", "mean"))
m.corr()      # -0,695
Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min
«El −0,225 no dice "cuando el precio baja, se vende más". Dice "la sal es barata y el cacao es caro". La correlación entre los diez promedios es −0,695: ahí estaba todo el efecto.»

La regla, en la pizarra, y es la que se cobra en el requisito 2.3: una correlación calculada sobre una mezcla de cosas distintas mide la mezcla. Antes de interpretar un r, pregunta: ¿sobre qué población lo calculé?

Desviación y percentiles, dibujados: el boxplot, el P75 y la correlación · 15 min

Y el apunte de vocabulario, 30 segundos, porque el enunciado lo usa: la covarianza es la misma medición sin normalizar, cov(x, y) = r · σx · σy. Queda en unidades de x por unidades de y —pesos-por-días, unidades-por-peso— y nadie sabe si eso es mucho o poco. r está entre −1 y +1 y no tiene unidad: por eso se interpreta.

«Y ojo con el orden en que pasó esto: primero apareció un número que confirmaba lo que el gerente quería oír, y recién después lo abrimos. Los números que te dan la razón son los que hay que revisar dos veces.»
Respiro · 5 min

Pararse, estirar, subir la luz un segundo. Clase nocturna: el cuerpo también aprende.

La bisagra · 4 min

Se responde aquí la pregunta que abrió la clase, y se abre la segunda mitad:

«Tienes el nivel —la mediana— y tienes la cola —el P75 y los tres atípicos—. Ahora mira el mismo número mes a mes.»

Se dibuja en la pizarra en diez segundos y ese dibujo queda ahí el resto de la clase: una recta que sube y una onda montada encima.

La bisagra · 4 min
La tendencia es el piso que se mueve; la estación es la respiración encima del piso. Y las dos se planifican distinto: la estacionalidad vuelve todos los años y te dice cuándo comprar; la tendencia no vuelve y te dice cuánto más que el año pasado.

Y la trampa, ya armada, dicha antes de calcular nada:

«Vamos a buscar el patrón del año, y el azúcar va a aparecer con un pico de invierno espectacular. Guárdense la fila 5909.»
Índice estacional, tendencia, día de semana y granularidad · 22 min

Definición, en una frase, y se copia:

Índice estacional de un mes = lo que ese mes vende, dividido por lo que vendería un mes típico. 1,00 = mes típico. 1,25 = un 25 % sobre lo típico. 0,70 = un 30 % bajo lo típico.

La fórmula operativa del curso, escrita en la pizarra antes de tocar el teclado:

índice(producto, mes) = unidades del mes / (unidades totales del producto / 12)
Índice estacional, tendencia, día de semana y granularidad · 22 min
f18 = f18.copy()
f18["mes"] = f18.fecha.dt.month

def indices(df):
    total = df.groupby("producto").cantidad.sum()
    pm    = df.groupby(["producto", "mes"]).cantidad.sum().reset_index()
    pm["indice"] = (pm.cantidad / (pm.producto.map(total) / 12)).round(2)
    return pm.pivot(index="producto", columns="mes", values="indice")

indices(f18)
Índice estacional, tendencia, día de semana y granularidad · 22 min

Del cuadro que aparece se marcan en rojo dos números y nada más, porque son los que hacen la lección:

Índice estacional, tendencia, día de semana y granularidad · 22 min
«El azúcar sube un 67 % en julio y la sal un 40 % en octubre. Son los índices más grandes del cuadro. ¿Alguien tiene una explicación de negocio para el azúcar de julio?»

Van a inventar una: el invierno, la repostería, las conservas. Anotarla en la pizarra tal cual, con el nombre de quien la dijo.

Índice estacional, tendencia, día de semana y granularidad · 22 min

El índice que mentía

«Al empezar el bloque les pedí que se guardaran la fila 5909. ¿Qué fecha tenía?» → 19 de julio de 2018.
indices(f18[f18.cantidad < 1000])
Índice estacional, tendencia, día de semana y granularidad · 22 min
productomescon el atípicosin el atípico
Azúcarjulio1,671,03
Saloctubre1,400,95
Levadurajunio1,090,69

Escribirlo grande, y borrar de la pizarra la explicación de negocio que ellos inventaron:

Índice estacional, tendencia, día de semana y granularidad · 22 min
El pico de invierno del azúcar no existe. Es una línea. La sal no tiene estación. El repunte de junio de la levadura tampoco existe.

Silencio de cuatro segundos. Y después:

Índice estacional, tendencia, día de semana y granularidad · 22 min
«Yo les mostré un número, ustedes le encontraron una explicación de negocio convincente en once segundos, y era falsa. El cerebro le pone historia a cualquier número. Por eso la limpieza va antes que el análisis: no porque sea prolijo, sino porque después de ver el gráfico ya no puedes des-verlo.»
Índice estacional, tendencia, día de semana y granularidad · 22 min

Y el hallazgo que sí sirve para comprar, del cuadro limpio: la sal no tiene estacionalidad —amplitud 0,20 contra 0,84 del cacao (pico 1,39 en marzo, valle 0,55 en septiembre)—. «A la sal la compras parejo todo el año y dejas de gastar reuniones en ella. Ese hallazgo vale más que los nueve picos juntos, y sólo aparece después de limpiar.»

Tendencia: el piso que sube

f18.groupby(f18.fecha.dt.year).cantidad.sum()
Índice estacional, tendencia, día de semana y granularidad · 22 min
añounidadesvariación
2018320.873—
2019333.110+3,8 %
2020355.121+6,6 %

CAGR 2018 → 2020: +5,2 % anual compuesto. Y la mirada de economista, 30 segundos: aquí medimos unidades, así que el crecimiento es real, no inflación. Si midiéramos pesos habría que deflactar antes de celebrar.

Índice estacional, tendencia, día de semana y granularidad · 22 min

Día de semana: la tercera frecuencia

lim = f18[f18.cantidad < 1000]          # el dato limpio, siempre
dia = lim.groupby("fecha").cantidad.sum()
dia.groupby(dia.index.dayofweek).mean().round(1)

Lunes 1.080,2 · martes 1.081,3 · miércoles 991,4 · jueves 990,4 · viernes 995,3 · sábado 592,3 · domingo 603,9.

Índice estacional, tendencia, día de semana y granularidad · 22 min
El fin de semana vende un 44–45 % menos que el lunes. Con el mes y el año defines cuánto compras; con el día de semana defines cómo lo repartes en la bodega. Tres decisiones distintas para tres personas distintas.

Las tres frecuencias, juntas en el dibujo de la bisagra: la recta que sube (año), la onda larga encima (mes) y el diente de sierra chico (semana).

Índice estacional, tendencia, día de semana y granularidad · 22 min

Qué proyectar y con qué granularidad (requisito 3.2)

proyección del mes = base × índice estacional del mes × tendencia, donde base = mediana diaria (no el promedio) y tendencia ≈ +5 % anual.
Índice estacional, tendencia, día de semana y granularidad · 22 min
DecisiónGranularidadPor qué
Compras al proveedormensualEl pedido tiene plazo de reposición y mínimo; el ruido diario se promedia
Programación de bodegasemanal, con el perfil de día de semanaEl fin de semana cae 44 % y el galpón se organiza con eso
Reposición en saladiariaAquí sí importa el día
Índice estacional, tendencia, día de semana y granularidad · 22 min
«La granularidad no se elige por elegancia: se elige por el costo de equivocarse. Si comprar de más y comprar de menos cuestan parecido, ve grueso. Si el quiebre es carísimo, ve fino y usa un percentil alto.»

La plantilla del mini-reporte estacional se proyecta y se fotografía —Producto · base (mediana diaria) · mes pico e índice · mes valle e índice · amplitud · CV limpio · colchón (P75 − mediana) · granularidad recomendada · una frase para el jefe de compras—. Se completa para dos productos, en casa, dentro de clase-13.ipynb, entrega del sábado 31-10 23:59.

Espejo del caso propio: la mezcla explica más que el precio · 12 min

Austral, otra vez. Declarar en voz alta que el dataset es sintético.

La pregunta que trae el Gerente de Administración y Finanzas:

«El margen bruto del trimestre bajó de 26,2 % a 22,6 %. ¿Estamos vendiendo más barato?»
Espejo del caso propio: la mezcla explica más que el precio · 12 min
Ojo: se reconstruye l. La primera mitad de la clase ya usó una tabla l —la de segmentos y
fechas de pago— y ésta es otra, con otras columnas y para otra pregunta. En el curso de 36 vivían
en noches distintas y no se pisaban; fusionadas, la segunda reemplaza a la primera. Por eso va
CREATE OR REPLACE y por eso se dice en voz alta: «ojo, estamos rearmando l, ya no es la de hace
una hora»
. Si alguien vuelve a correr una consulta de la primera mitad después de esto, le va a dar
error de columna — y ése es un buen momento para explicar por qué.
Espejo del caso propio: la mezcla explica más que el precio · 12 min

Paso 1 — la base, con la fecha rescatada. Las 21 líneas escritas en dd/mm/yyyy no se pierden: TRY_CAST las convierte en NULL en silencio, y el COALESCE las recupera con el formato explícito.

Espejo del caso propio: la mezcla explica más que el precio · 12 min
CREATE OR REPLACE TABLE l AS
SELECT numero_factura, linea_negocio,
       COALESCE(TRY_CAST(fecha_emision AS DATE),
                TRY_STRPTIME(fecha_emision, '%d/%m/%Y')::DATE)  AS fe,
       TRY_CAST(cantidad AS DOUBLE)          AS cant,
       TRY_CAST(precio_unitario AS DOUBLE)   AS precio,
       TRY_CAST(costo_unitario AS DOUBLE)    AS costo,
       TRY_CAST(descuento_factura AS DOUBLE) AS dsc,
       TRY_CAST(flete_despacho AS DOUBLE)    AS flt
FROM read_csv_auto('datos/finanzas/ventas_extracto.csv', all_varchar = true)
WHERE moneda = 'CLP';
Espejo del caso propio: la mezcla explica más que el precio · 12 min

Paso 2 — el reparto de la clase 10. El descuento y el flete viven al grano factura y están repetidos en cada línea. Acá se dividen entre las líneas, en proporción a lo que cada una aporta a la venta de esa factura. Es la única forma de sumarlos sin contarlos dos veces.

Espejo del caso propio: la mezcla explica más que el precio · 12 min
CREATE OR REPLACE TABLE rep AS
WITH tot AS (SELECT numero_factura, SUM(cant*precio) AS vf FROM l GROUP BY 1)
SELECT l.*,
       EXTRACT(year FROM l.fe) || '-Q' || EXTRACT(quarter FROM l.fe)        AS trim,
       CASE WHEN t.vf <> 0 THEN l.dsc * (l.cant*l.precio) / t.vf ELSE 0 END AS dsc_lin,
       CASE WHEN t.vf <> 0 THEN l.flt * (l.cant*l.precio) / t.vf ELSE 0 END AS flt_lin
FROM l JOIN tot t USING (numero_factura);
Espejo del caso propio: la mezcla explica más que el precio · 12 min

Paso 3 — recién ahora, el margen. Año a año y por línea de negocio, con el descuento y el flete ya repartidos:

SELECT EXTRACT(year FROM fe) AS anio, linea_negocio,
       ROUND(100.0 * (SUM(cant*precio) - SUM(cant*costo)
                      - SUM(dsc_lin) - SUM(flt_lin)) / SUM(cant*precio), 1) AS margen
FROM rep GROUP BY 1, 2 ORDER BY 2, 1;
Espejo del caso propio: la mezcla explica más que el precio · 12 min
línea de negocio202220232024período
Seguridad32,533,632,232,8 %
Herramientas27,326,627,227,1 %
Eléctrico18,719,720,619,7 %
Fijaciones12,411,613,412,5 %
Espejo del caso propio: la mezcla explica más que el precio · 12 min
«El margen de cada línea es una roca. Seguridad se mueve entre 32,2 y 33,6 en tres años. Nadie está vendiendo más barato. Entonces, ¿de dónde salieron los 3,6 puntos que perdió el total?»

Y el segundo cuadro, que es la respuesta:

Espejo del caso propio: la mezcla explica más que el precio · 12 min
-- la tasa de margen de cada línea en el período completo (la que se "congela")
CREATE OR REPLACE TABLE q AS
SELECT trim, linea_negocio, SUM(cant*precio) AS vta,
       SUM(cant*precio) - SUM(cant*costo) - SUM(dsc_lin) - SUM(flt_lin) AS mb
FROM rep GROUP BY 1, 2;

CREATE OR REPLACE TABLE tasa AS SELECT linea_negocio, SUM(mb)/SUM(vta) AS mg FROM q GROUP BY 1;

SELECT q.trim,
       ROUND(100.0 * SUM(q.mb) / SUM(q.vta), 1)        AS margen_real,
       ROUND(100.0 * SUM(q.vta * t.mg) / SUM(q.vta), 1) AS margen_tasa_fija,
       ROUND(100.0 * SUM(CASE WHEN q.linea_negocio = 'Seguridad'
                              THEN q.vta ELSE 0 END) / SUM(q.vta), 1) AS pct_seguridad
FROM q JOIN tasa t USING (linea_negocio)
GROUP BY 1 ORDER BY 1;
Espejo del caso propio: la mezcla explica más que el precio · 12 min

Cómo se lee la columna del medio, despacio, porque es la idea difícil de la noche:

«Congelé la tasa de margen de cada línea en su valor del período completo —Seguridad 32,8, Herramientas 27,1, Eléctrico 19,7, Fijaciones 12,5— y dejé cambiar sólo la mezcla: cuánto pesó cada línea en la venta de ese trimestre. Es el margen que habrías tenido si ningún precio se hubiera movido nunca.»

Los números del bloque, en la pizarra:

Espejo del caso propio: la mezcla explica más que el precio · 12 min
El margen real recorre 3,6 puntos (22,6 % a 26,2 %). Sólo con la mezcla, sin tocar un precio, recorre 1,5 puntos (23,8 % a 25,3 %): el 42 % del movimiento.
Y la mezcla se mueve muchísimo: Seguridad pesó 10,2 % de la venta en 2022-Q3 y 27,8 % en 2024-Q1.
Espejo del caso propio: la mezcla explica más que el precio · 12 min
«La respuesta al gerente no es "sí" ni "no". Es: casi la mitad de lo que se movió el margen no fue el precio, fue qué vendimos. El trimestre malo fue el trimestre en que Seguridad —la línea de 32,8 %— cayó al 10 % de la venta. Y eso no se arregla con una circular de descuentos: se arregla en la fuerza de venta.»
Control quincenal C4 · 10 min

Individual, sin apuntes, 8 minutos de reloj; autocorrección con la pauta proyectada, 2 minutos. Es formativo (0 %) y hay que decirlo antes de repartirlo: mide si el método quedó, no la nota. Estructura y pauta intactas (recall/controles-24.md; contenido en controles.md): 4 selecciones múltiples · 1 V/F justificado · 1 mini-problema, 16 puntos.

El docente registra en qué ítem falló la sala completa — ése es el bloque que se re-entrena en la apertura de la clase 14.

Los dos expertos de negocio interno · 8 min

Los dos en la misma lámina, uno detrás del otro. Se proyectan las preguntas concretas y no se responden:

Los dos expertos de negocio interno · 8 min
Tesorería: «¿El DSO se informa como promedio o como mediana? ¿Y cuál mira el banco cuando evalúa nuestra línea de crédito?»

Los dos números son verdaderos. El promedio (91,6) es el que cuadra con el capital de trabajo, porque la plata inmovilizada es plata × días y la cola sí pesa en la caja. La mediana (86,0) describe la experiencia típica de cobranza. Un informe que entrega uno solo de los dos y no dice cuál eligió está escondiendo la mitad. Y esto no se decide con SQL: se decide con Tesorería y con quien firma el covenant del banco.
Los dos expertos de negocio interno · 8 min
Jefe de compras (alimentaria) / Gerencia Comercial (Austral): «Diciembre sube todos los años. ¿Es campaña real del cliente, o somos nosotros adelantando despachos para cerrar la meta?»

Si sube porque el cliente compra, el índice es real y hay que comprar para diciembre. Si sube porque nosotros empujamos despachos el día 28, enero va a estar hundido y tu índice de enero también está mentido. La serie se ve idéntica en los dos casos. La diferencia está en la cabeza del jefe de compras, no en el CSV.
Los dos expertos de negocio interno · 8 min

Y la tercera, para el jefe de bodega, en 30 segundos: «la línea 5909 vendió 8.205 unidades de azúcar el 19 de julio de 2018. ¿Es un pedido real de un mayorista, o son 82,05 con la coma corrida?» Si es real, es un cliente que hay que modelar aparte; si es un error, es una fila que hay que corregir antes de que ensucie el índice.

Regla del curso en la pizarra, otra vez: una consulta SQL sin una conversación previa es una opinión con formato de tabla.

Síntesis, entrega y ticket · 16 min

Construida con ellos, no dictada (6 min):

Síntesis, entrega y ticket · 16 min
  1. «¿Cuándo el promedio miente?» → cuando hay cola. Y siempre hay cola.
  2. «¿Cuál fue el número que probó que la mediana aguanta?» → una línea movió el promedio del azúcar 7,4 unidades y la mediana cero.
  3. «¿Qué parte de la caja es el IQR y qué son los puntos de arriba?» → del P25 al P75; los puntos son los atípicos de la clase 5, ahora dibujados.
  4. «¿Qué es un percentil?» → una decisión con el costo ya elegido.
  5. «¿Por qué el −0,225 no autorizaba bajar el precio?» → porque medía la diferencia entre productos, no el comportamiento de uno.
  6. «¿Qué le pasó al pico de julio del azúcar?» → nunca existió: 1,67 sucio, 1,03 limpio.
  7. «¿Las tres frecuencias?» → tendencia (+5,2 % anual), estación (el mes), día de semana (−44 % el fin de semana).
  8. «¿Qué explica el 42 % del movimiento del margen de Austral?» → la mezcla de venta, no el precio.
Síntesis, entrega y ticket · 16 min

La consigna de entrega, dicha en voz alta y por segunda vez con este nombre (4 min):

Síntesis, entrega y ticket · 16 min
«Lo que se sube es el notebook, clase-13.ipynb, no una tabla suelta ni un pantallazo. Adentro tienen que estar cuatro cosas: la tabla de estadísticos, los dos boxplot con una celda de texto cada uno diciendo qué se ve y qué se sacó, el cuadro de índices con y sin los atípicos, y la plantilla de los dos productos como celda de texto. Y no es una manía mía: el entregable de la etapa 1 del Parcial 2 es un .ipynb y el de las tres primeras etapas del encargo de cierre también. Es condición de entrega, no criterio de nota — un trabajo impecable en otro formato simplemente no se recibe.

El mini-reporte estacional de los dos productos se termina en casa y se entrega el sábado 31-10, 23:59, dentro del mismo archivo.»
Síntesis, entrega y ticket · 16 min

El simulacro del Parcial 2, anunciado con una semana de anticipación (3 min):

Síntesis, entrega y ticket · 16 min
«El martes 03-11 es el simulacro, en grupo, con el gemelo: un archivo distinto, del mismo negocio, más sucio que el oficial — a propósito. Dos horas. Los grupos y los tres roles —modelador (teclea), verificadora (pregunta "¿de dónde salió ese número?" antes de que el número salga del computador) y redactor (escribe requisitos y decisiones)— se publican esta semana en klearning y se releen al abrir la clase 14. Los tres firman.»
Síntesis, entrega y ticket · 16 min

Ticket TC13 (3 min, se proyecta). Anki del día: mediana vs promedio, CV, P75 como decisión, índice estacional, r vs covarianza.

Preguntas que quedan planteadas (se escriben en la pizarra y no se responden hoy):

Síntesis, entrega y ticket · 16 min
→ Sabes limpiar, modelar, resumir y detectar patrón. En grupo, con dos horas y un archivo nuevo: ¿quién hace qué, y en qué orden?

→ Si el índice del azúcar mentía por una fila, ¿cuál es la primera consulta que corres en el simulacro antes de calcular nada?
Clase 13 · cierre

Ticket de salida

Tres minutos. No suma nota. Sirve para armar la apertura de la próxima clase.

El notebook de esta clase:
https://gaa1104.vercel.app/notebooks/clase-13.ipynb

← → avanzar · P proyector · F pantalla completa
GAA1104 · C13 · La demanda: nivel, cola y estación · Control C4 20:30 1 / 1