🇬🇧 English · 🇪🇸 Español
Estudios y comparaciones reproducibles
puremacro.study registra un estudio estadístico explícito: datos seleccionados,
transformaciones, unidades, supuestos de identificación, configuración del
estimador, resultados, diagnósticos, versiones y sumas de verificación. Reutiliza
los estimadores LP y VAR recursivo, los formatos de informes y el códec portátil
del runtime. No deduce una interpretación causal ni certifica un modelo económico.
Estimar y comparar proyecciones locales
Este ejemplo completo funciona sin conexión y utiliza datos sintéticos. La exogeneidad se declara como supuesto ilustrativo; el programa no la demuestra.
from dataclasses import replace
import numpy as np
import pandas as pd
from puremacro.study import (
VariableSpec, StudySpec, run_study, compare_studies,
save_study, load_study,
)
rng = np.random.default_rng(173)
observations = rng.normal(size=(240, 3))
for t in range(1, len(observations)):
observations[t] += 0.4 * observations[t - 1]
data = pd.DataFrame(
observations,
columns=["shock", "output", "price"],
index=pd.period_range("1960Q1", periods=240, freq="Q"),
)
data.attrs = {"source": "demostración sintética", "data_seed": 173}
spec = StudySpec(
name="LP, dos rezagos", method="lp",
variables=(
VariableSpec("shock", "puntos porcentuales"),
VariableSpec("output", "puntos de índice"),
VariableSpec("price", "puntos de índice"),
),
responses=("output",), shock="shock", controls=("price",),
frequency="Q", identification="observed_exogenous",
lags=2, horizon=8, shock_size=0.25, ci=0.90,
)
baseline = run_study(data, spec)
alternative = run_study(data, replace(spec, name="LP, cuatro rezagos", lags=4))
comparison = compare_studies(baseline, alternative)
print(comparison.to_frame())
print(baseline.audits)
# El directorio debe ser nuevo: no se sobrescriben resultados existentes.
baseline.write_report("study_output/lp_dos_rezagos")
# También puede conservarse el registro completo en memoria, en un navegador.
payload = save_study(baseline)
restored = load_study(payload)
assert restored.manifest == baseline.manifest
write_report genera tablas Markdown, LaTeX, Typst y CSV, manifest.json
y el archivo sin pérdida study.pmstudy. Markdown y el manifiesto explican
métodos, supuestos, muestras efectivas y alcance de los diagnósticos. CSV es un
formato de presentación; el archivo conserva tipos de índices y metadatos
admitidos de DataFrame.attrs. to_json() contiene el manifiesto y las tablas
de salida, pero no los datos de entrada. StudyResult y StudyComparison
son registros inmutables; sus tablas y diccionarios devueltos son copias.
Declarar transformaciones, unidades y frecuencia
Cada columna necesita un VariableSpec. Las transformaciones disponibles
son identity, log, diff y logdiff; scale multiplica la serie transformada.
unit describe esa cantidad final. Por ejemplo,
VariableSpec("output", "crecimiento logarítmico porcentual", "logdiff", 100)
calcula 100 * diff(log(output)) y requiere niveles positivos. No se convierte
a porcentajes ni se anualiza una variable a partir de su nombre.
El índice debe ser un PeriodIndex o DatetimeIndex único, creciente y
consecutivo. frequency="Q" indica periodos trimestrales; para fechas se usa
el desplazamiento de calendario correspondiente, como "QS" o "MS".
Los alias de fin de trimestre/mes admiten la escritura compatible con pandas.
Un periodo ausente o un valor no finito genera un error: nunca se comprime el
tiempo eliminando filas incompletas. Las columnas no declaradas también son errores.
sample_start y sample_end deben identificar observaciones del índice.
La selección ocurre antes de transformar. Diferenciar elimina exactamente la
primera observación seleccionada de todas las series; no se toma un valor de
fuera de esa muestra. Se guardan los datos originales seleccionados, no las
filas externas a la muestra. data.attrs puede registrar fuente, versión de
datos y notas de descarga. Los objetos de metadatos no admitidos generan un
error; no se descartan silenciosamente.
Interpretar los contratos de estimación
| Configuración | method="lp" |
method="var_cholesky" |
|---|---|---|
| Identificación | observed_exogenous: exogeneidad del shock observado condicionada a los regresores, declarada como supuesto |
recursive: orden contemporáneo recursivo declarado |
| Respuesta en horizonte h | y[t+h] - y[t-1] |
Impulso-respuesta en niveles en h |
| Tamaño del shock | Múltiplo de la unidad transformada del shock observado | Impacto sobre la variable del shock, en su unidad transformada |
lags=p |
Rezagos 1–p de cada respuesta, shock y controles; controles contemporáneos y constante | Rezagos 1–p de todas las variables ordenadas y constante; p ≥ 1 |
| Muestra efectiva | Primera fecha del shock max(1,p); última T-h-1 |
Fechas p a T−1 en el ajuste del VAR |
| Incertidumbre | Intervalos normales HAC puntuales; Bartlett con ancho h+1 | Intervalos percentiles puntuales mediante bootstrap residual opcional |
Los horizontes incluyen el extremo: horizon=8 devuelve 0 a 8. La muestra
de LP cambia con el horizonte y puede cambiar al modificar rezagos. Cada
resultado presenta n_obs; effective_by_horizon registra fechas del shock
y los regresores. Igualdad de la ventana de entrada no significa igualdad de
todas las observaciones usadas en la estimación.
En LP, los nombres de respuestas, shock y controles deben ser distintos.
Con varias respuestas, cada regresión usa los rezagos de su propia respuesta;
las otras respuestas no se añaden como controles. LP exige n_boot=0.
En VAR, el orden contiene todas las variables exactamente una vez y controls
está vacío:
recursive = replace(
spec, name="VAR recursivo", method="var_cholesky",
identification="recursive", controls=(),
ordering=("price", "shock", "output"), n_boot=199, seed=71,
)
var_result = run_study(data, recursive)
El VAR multiplica las respuestas por shock_size / P[k,k], donde P es el
factor de Cholesky estimado y k la posición del shock. Los intervalos usan
esta normalización fija; no se normaliza cada réplica a idéntico impacto.
Los shocks negativos invierten correctamente los extremos del intervalo.
n_boot=0 omite las columnas de intervalos. Las réplicas fallidas y la
inestabilidad estimada permanecen visibles en los diagnósticos. Una estimación
o intervalo exportado no finito genera un error.
compare_studies requiere al menos dos estudios con nombres únicos y el
mismo método, identificación, orden, horizonte, definiciones de variables,
convención de respuesta, tamaño/unidad del shock, y datos/metadatos originales
y transformados idénticos. Pueden cambiar rezagos, confianza, semilla y número
de réplicas, que quedan registrados. Se rechazan comparaciones mixtas LP/VAR:
un shock observado con respuesta acumulada no equivale automáticamente a una
innovación identificada recursivamente con respuesta en niveles.
Conservar y cargar evidencia
save_study(result, "study.pmstudy") crea un archivo nuevo;
save_study(result) devuelve bytes.
load_study(path_or_bytes, max_bytes=64 * 1024 * 1024) verifica el inventario
exacto, esquema, SHA-256 de tablas y manifiesto, unidades, tamaños muestrales
y valores numéricos finitos. Limita la expansión ZIP y las dimensiones NPY
antes de decodificar. Usa JSON y el códec NPZ versión 2 del runtime, incluidos
metadatos admitidos. No extrae rutas, deserializa pickle, importa callbacks
ni ejecuta estimadores. No requiere Parquet, una cuenta remota ni conexión.
Las sumas detectan cambios, pero no autentican al autor ni demuestran que
un resultado deliberadamente modificado y vuelto a firmar sea correcto.
Se registran semillas y versiones numéricas. La igualdad exacta al repetir
se limita al mismo entorno numérico; otras bibliotecas BLAS o versiones pueden
alterar el redondeo. Los estados son passed, failed, not_checked y
unsupported; la identificación permanece not_checked.
Registrar un modelo o escenario ya evaluado
ScenarioSpec, ScenarioResult y record_scenario registran resultados de
solucionadores existentes: DSGE, programación dinámica, HANK, ajuste estructural
o escenarios estáticos. No ejecutan otro solucionador. El código fuente queda
como texto inerte; se conservan parámetros, resultados, entradas, diagnósticos
y tablas adicionales de evidencia.
Este ejemplo registra una trayectoria calibrada explícitamente, sin afirmar que sus parámetros se estimaron a partir de las observaciones ilustrativas:
from puremacro.study import ScenarioSpec, record_scenario
scenario_spec = ScenarioSpec(
name="Respuesta calibrada ilustrativa", model="respuesta geométrica declarada",
variables=spec.variables, responses=spec.responses, shock=spec.shock,
frequency=spec.frequency, identification=spec.identification,
response_convention="y[t+h]-y[t-1]",
horizon=spec.horizon, shock_size=spec.shock_size,
)
outputs = pd.DataFrame({
"response": ["output"] * (spec.horizon + 1),
"horizon": range(spec.horizon + 1),
"estimate": [0.1 * 0.7**h for h in range(spec.horizon + 1)],
})
scenario = record_scenario(
outputs, scenario_spec,
inputs=baseline.inputs, transformed_inputs=baseline.data,
model_source="response[h] = 0.1 * persistence**h; pre-shock baseline = 0",
parameters={"persistence": 0.7, "impact": 0.1},
audits=pd.DataFrame([{
"check": "economic_identification", "status": "not_checked",
"detail": "La equivalencia de shocks externos se supone en esta ilustración.",
}]),
provenance={"purpose": "ilustración calibrada; parámetros no estimados"},
)
overlay = compare_studies(
baseline, scenario,
comparison_note="Se supone que la innovación del modelo corresponde al shock "
"exógeno declarado y a la misma base previa; sólo ilustrativo.",
)
scenario.write_report("study_output/respuesta_calibrada")
Las salidas contienen exactamente response, horizon, estimate, primero
por respuesta y después por horizonte 0..H. variables declara las unidades.
Los diagnósticos contienen check, status, detail; se conservan estados
fallidos, no admitidos o no comprobados. parameters contiene escalares reales
finitos. evidence={"nombre": dataframe} admite matrices de calibración,
covarianza conjunta real de momentos, objetivos usados/reservados para evaluación,
distribuciones, estados o registros de réplicas. Nunca se deduce una covarianza
a partir de bandas marginales de LP.
inputs puede ser una tabla estática del modelo. transformed_inputs es
obligatorio si hay transformaciones o escalas no identidad declaradas; no se
deducen ni recalculan transformaciones de datos del modelo. Para insumo-producto
estático son etiquetas explícitas apropiadas horizon=0,
frequency="annual_static", identification="calibrated_input_output_scenario"
y response_convention="static_level_change". Un escenario independiente no
requiere índice de fechas empírico.
Cualquier comparación con escenarios exige comparison_note, además de unidades,
transformaciones, tamaño de shock, identificación, frecuencia, horizonte y
convención idénticos, y entradas originales/transformadas exactamente iguales.
La nota no permite eludir un contrato incompatible. Estas comprobaciones validan
la comparabilidad declarada, no demuestran equivalencia económica. No se inventan
tamaños muestrales empíricos ni intervalos para escenarios.
save_study/load_study distinguen archivos empíricos y de escenarios. El
informe de escenario incluye model_source.txt; el archivo conserva evidencia
y procedencia sin pérdida. Se rechazan estructuras que el códec no conserva,
como nombres de niveles MultiIndex en columnas o atributos propios de subclases.
Conviértalas deliberadamente a tablas ordinarias, conservando la definición de
coordenadas en procedencia. Toda evidencia numérica debe ser finita; conserve
los estados de réplicas fallidas por separado, sin ocultarlas ni sustituirlas.
El flujo no automatiza identificación IV, estimación estructural ni selección de modelos. Para construir objetivos empíricos correlacionados y ajustar un callback, use el puente estructural y los flujos de investigación, y registre sus salidas reales.