Script
El modelo estrella, armado
Cuatro tablas como las que entrega cualquier sistema de gestión, unidas en una tabla lista para analizar, con la dimensión de fechas construida y los importes llevados a pesos constantes.
Proyecto en R y Python · ZIP · 515 KB
No usa ningún paquete: corre con R recién instalado y con Python recién instalado.
Las cuatro cosas que existe para no dejar pasar
Una fila no es una venta. Un comprobante trae varias líneas, una por producto. Contar filas y llamarlas «ventas» exagera por ese factor —en los datos del ejemplo, 3,3—. El proyecto informa las dos cosas y nunca confunde una con la otra.
Una unión no puede cambiar la cantidad de filas. Si bajan, hubo huérfanos y la unión interna los borró. Si suben, hay una clave repetida en una dimensión y multiplicó. El segundo caso es el peligroso: el total sube, y a un total que creció nadie le busca el error. El paso 3 cuenta antes y después.
La dimensión de fechas no tiene huecos. Un mes sin ventas vale cero, no vale «no existe». Si falta de la tabla, el gráfico une los meses vecinos y disimula el hueco. El día de la semana se calcula con el día juliano y no con la clase de fecha de cada idioma, para que los dos motores no discrepen por el calendario.
Deflactar es dividir, no restar. Restarle la inflación acumulada a la variación nominal da otro número, y con la inflación argentina da un número absurdo. El informe muestra las dos cuentas una al lado de la otra.
Y una quinta que los datos del ejemplo dejan ver solos: si entre las dos puntas de la comparación abrió una sucursal, el total compara una empresa con otra. Por eso el informe trae, además del total, la variación a sucursales comparables.
Qué escribe
| salidas/ventas-modelo.csv | La tabla lista: una fila por línea de venta, con las dimensiones unidas y las medidas en pesos corrientes y constantes |
|---|---|
| salidas/dimension-fechas.csv | Un día por fila, sin huecos, con año, trimestre, mes, nombre del mes y día de la semana |
| salidas/por-mes.csv | El agregado mensual, incluidos los meses sin ventas |
| salidas/por-rubro.csv | Por categoría de producto, con margen |
| salidas/por-sucursal.csv | Por sucursal |
| salidas/por-tipo-de-cliente.csv | Por tipo de comercio |
| salidas/avisos.csv | Lo que conviene mirar antes de usar los números: huérfanos, claves repetidas, meses sin índice |
| salidas/resumen.csv | Las cifras de control, para comparar entre corridas |
| salidas/modelo-estrella.md | El informe en texto, con las dos variaciones una al lado de la otra |
Los datos del ejemplo
Vienen de Distribuidora Norte, un conjunto simulado con semilla fija: no describe ninguna empresa real y no se puede citar. Lo que sí es real es el IPC, que es el del INDEC, y por eso el ejercicio de deflactar da lo que daría con una empresa de verdad.
El paquete trae dos de las cinco sucursales —una que existe desde el principio y otra que abre en marzo de 2023— y el padrón de clientes completo, que incluye comercios que nunca compraron en esas dos. Las tres cosas están a propósito: son las que el paso 1 detecta.
Para correrlo con datos propios alcanza con editar datos/parametros.csv y apuntar cada archivo al tuyo.
Por qué los dos idiomas escriben lo mismo
Los dos escriben los nueve archivos idénticos byte a byte, y el verificador lo comprueba en cada corrida. No es un alarde: es la única forma de saber que las dos implementaciones hacen la misma cuenta y no dos parecidas. Costó unificar tres cosas, y las tres son las razones habituales por las que dos programas que «hacen lo mismo» dan números distintos.
El salto de línea. Python en Windows traduce \n a CRLF y R no. Los dos escriben LF explícito.
El redondeo. Las dos cuentas daban el mismo número —132483,7605, bit por bit— y round() lo resolvía al revés en cada lenguaje: Python devolvía ,761 y R, ,760. Los dos dicen redondear al par, pero el valor cae en un empate y cada implementación lo cruza para su lado. Se usa printf, que en los dos redondea correctamente el valor binario verdadero.
La suma. sum() de R acumula en long double de 80 bits y Python en double; sobre miles de sumandos los totales difieren en el décimo dígito. R usa una suma propia que acumula de a un double, como Python. Que acumular en más precisión sea «mejor» no viene al caso: lo que importa es que las dos hagan exactamente la misma cuenta.
Cómo está verificado
Se descomprime lo publicado, se corre entero y se compara. El zip tiene que ser el que produce el generador, archivo por archivo y byte a byte, y el generador tiene que ser reproducible. Las cuentas —la unión, los agregados y los deflactados— se rehacen con merge y groupby de pandas, que no comparten una línea con el proyecto. Y con R en la máquina se corre también la versión de R y se exige que escriba los nueve archivos idénticos.
Más cinco variantes del archivo de entrada que cambian lo que el proyecto tiene que detectar: una clave repetida en una dimensión, un producto huérfano, un mes sin IPC, un mes del rango sin ventas y las devoluciones apagadas. En esta última el total sube, porque las notas de crédito restaban. Son 164 comprobaciones.
Y dos cosas que el zip tiene que cumplir para que todo lo anterior signifique algo: que ningún archivo lleve CRLF —si el zip arrastrara los fines de línea que deja el checkout de git en Windows, el archivo publicado no sería el mismo en todas las máquinas— y que las nueve salidas coincidan con una referencia guardada, que se compara siempre y no se reescribe sola.