chore: auto-commit (26 archivos)
- python/functions/bigquery/bq_auth.md - python/functions/bigquery/bq_load_from_file.md - python/functions/bigquery/bq_load_from_gcs.md - python/functions/bigquery/client.py - python/functions/bigquery/queries.py - python/functions/datascience/__init__.py - python/functions/datascience/decode_qr_image.py - python/functions/datascience/load_bq_table_to_duckdb.md - python/functions/datascience/load_bq_table_to_duckdb.py - python/functions/pipelines/profile_bq_table.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
---
|
||||
name: forecast_seasonal_median
|
||||
kind: function
|
||||
lang: py
|
||||
domain: datascience
|
||||
version: "1.0.0"
|
||||
purity: pure
|
||||
signature: "def forecast_seasonal_median(history: list[dict], horizon_dates: list[str], as_of: str, dow_weeks: int = 8, trend_recent_weeks: int = 4, trend_clip: tuple = (0.5, 2.0)) -> list[dict]"
|
||||
description: "Forecast diario por mediana estacional (mismo dia de semana) mas factor de tendencia acotado, para una o varias series temporales. Base estacional = mediana del valor en las ultimas dow_weeks fechas con el mismo dia de semana que la fecha objetivo (dias ausentes = 0, para series intermitentes). Factor de tendencia por serie = razon de la suma de las ultimas trend_recent_weeks semanas frente a las trend_recent_weeks anteriores, clipped a trend_clip. y_pred = max(0, base * factor). Funcion pura y determinista (solo stdlib, sin I/O ni datetime.now). Nucleo del forecast de ventas diarias Aurgi (dia x centro x subcategoria CGQ)."
|
||||
tags: [forecast, bigquery, timeseries, seasonal, median, baseline, sales, aurgi, python]
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: ""
|
||||
imports: []
|
||||
params:
|
||||
- name: history
|
||||
desc: "lista de observaciones {series_id: str, date: 'YYYY-MM-DD', value: float}. Filas duplicadas (misma serie+fecha) se suman. Los dias sin fila dentro de las ventanas cuentan como valor 0 (series intermitentes: sin fila = sin venta)"
|
||||
- name: horizon_dates
|
||||
desc: "fechas futuras a predecir, strings ISO 'YYYY-MM-DD'. Tipicamente as_of+1..as_of+horizon"
|
||||
- name: as_of
|
||||
desc: "fecha de corte 'YYYY-MM-DD': ultimo dia de historia utilizable, inclusive. Todas las ventanas se calculan hacia atras desde aqui"
|
||||
- name: dow_weeks
|
||||
desc: "numero de fechas del mismo dia de semana que la objetivo a promediar (mediana) para la base estacional. Default 8 (8 semanas)"
|
||||
- name: trend_recent_weeks
|
||||
desc: "tamano en semanas de cada una de las dos ventanas de tendencia (reciente y anterior). Default 4: compara 4 semanas recientes vs las 4 previas"
|
||||
- name: trend_clip
|
||||
desc: "tupla (min, max) al que se acota el factor de tendencia. Default (0.5, 2.0): la prediccion no puede caer a menos de la mitad ni superar el doble por tendencia"
|
||||
output: "list[dict]: una fila {series_id: str, date: str, y_pred: float} por cada serie presente en history y cada fecha de horizon_dates. Ordenada por series_id (asc) y luego por el orden de horizon_dates. y_pred siempre >= 0.0"
|
||||
tested: true
|
||||
tests:
|
||||
- "serie regular con patron semanal claro da la mediana correcta"
|
||||
- "serie intermitente: los dias ausentes cuentan como 0 en la mediana"
|
||||
- "serie con tendencia creciente aplica factor >1 acotado a trend_clip"
|
||||
- "sin datos en la ventana anterior, el factor de tendencia es 1.0"
|
||||
- "horizon de 7 dias produce una fila por serie y fecha, ordenadas"
|
||||
test_file_path: "python/functions/datascience/forecast_seasonal_median_test.py"
|
||||
file_path: "python/functions/datascience/forecast_seasonal_median.py"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```python
|
||||
from datascience import forecast_seasonal_median
|
||||
|
||||
# Historia diaria por serie (centro|subcategoria). Sin fila = sin venta = 0.
|
||||
history = [
|
||||
{"series_id": "12|NEUMATICOS", "date": "2026-06-23", "value": 1450.0},
|
||||
{"series_id": "12|NEUMATICOS", "date": "2026-06-16", "value": 1380.0},
|
||||
{"series_id": "12|NEUMATICOS", "date": "2026-06-09", "value": 1500.0},
|
||||
# ... mas historia (idealmente >= 8 semanas para la base estacional) ...
|
||||
]
|
||||
|
||||
# as_of = ultimo dia cerrado; predice los 7 dias siguientes.
|
||||
horizon = ["2026-06-30", "2026-07-01", "2026-07-02", "2026-07-03",
|
||||
"2026-07-04", "2026-07-05", "2026-07-06"]
|
||||
|
||||
preds = forecast_seasonal_median(history, horizon, as_of="2026-06-29")
|
||||
for p in preds:
|
||||
print(p["series_id"], p["date"], round(p["y_pred"], 2))
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando necesites un baseline de forecast diario robusto y explicable para series
|
||||
con estacionalidad semanal fuerte (ventas por dia de la semana) y posibles huecos
|
||||
(dias sin venta). Es el nucleo puro del pipeline `run_sales_forecast`: se llama una
|
||||
vez con toda la historia agregada y devuelve todas las predicciones de golpe.
|
||||
Usala como punto de partida antes de modelos mas pesados (Prophet, ARIMA, gradient
|
||||
boosting): captura el patron dia-de-semana + una correccion de tendencia acotada
|
||||
sin dependencias externas ni entrenamiento. Ideal para muchas series a la vez
|
||||
(miles de pares centro x subcategoria) donde entrenar un modelo por serie no
|
||||
compensa.
|
||||
|
||||
## Notas
|
||||
|
||||
- Funcion pura y determinista: no hace I/O, no llama `datetime.now()`; el corte
|
||||
temporal siempre es el argumento `as_of` explicito. Solo stdlib (datetime,
|
||||
statistics), sin numpy ni pandas.
|
||||
- La base estacional toma las fechas EXACTAS del calendario: la mas reciente
|
||||
<= as_of con el mismo dia de semana que la objetivo, y de ahi 7 dias hacia atras
|
||||
por punto (hasta `dow_weeks` puntos). Una fecha ausente en `history` cuenta como
|
||||
0, por lo que la mediana refleja bien las series intermitentes.
|
||||
- El factor de tendencia se calcula UNA vez por serie (no depende de la fecha
|
||||
objetivo) como razon de sumas de dos ventanas contiguas de `trend_recent_weeks`
|
||||
semanas. Denominador 0 => factor 1.0 (evita division por cero y no infla series
|
||||
que arrancan). El clip a `trend_clip` evita que un pico reciente dispare la
|
||||
prediccion.
|
||||
- `y_pred = max(0.0, base * factor)`: nunca negativo. No modela festivos ni eventos
|
||||
puntuales; para eso se necesitaria una capa de calendario adicional.
|
||||
- Para que la base estacional sea fiable conviene aportar >= `dow_weeks` semanas de
|
||||
historia. Con menos historia, los puntos ausentes (=0) empujan la mediana hacia
|
||||
abajo.
|
||||
Reference in New Issue
Block a user