|
| 1 | +--- |
| 2 | +jupytext: |
| 3 | + text_representation: |
| 4 | + extension: .md |
| 5 | + format_name: myst |
| 6 | + format_version: 0.13 |
| 7 | +kernelspec: |
| 8 | + display_name: pymc_env |
| 9 | + language: python |
| 10 | + name: python3 |
| 11 | +--- |
| 12 | + |
| 13 | +(arima_garch_1_1)= |
| 14 | +# Time Series Models Derived From a Generative Graph |
| 15 | + |
| 16 | +:::{post} March, 2024 |
| 17 | +:tags: time-series, |
| 18 | +:category: intermediate, reference |
| 19 | +:author: Juan Orduz and Ricardo Vieira |
| 20 | +::: |
| 21 | + |
| 22 | +```{code-cell} ipython3 |
| 23 | +import arviz as az |
| 24 | +import matplotlib.pyplot as plt |
| 25 | +import numpy as np |
| 26 | +import pymc as pm |
| 27 | +import pytensor |
| 28 | +import pytensor.tensor as pt |
| 29 | +
|
| 30 | +from pymc.pytensorf import collect_default_updates |
| 31 | +
|
| 32 | +az.style.use("arviz-darkgrid") |
| 33 | +plt.rcParams["figure.figsize"] = [12, 7] |
| 34 | +plt.rcParams["figure.dpi"] = 100 |
| 35 | +plt.rcParams["figure.facecolor"] = "white" |
| 36 | +
|
| 37 | +%config InlineBackend.figure_format = "retina" |
| 38 | +
|
| 39 | +rng = np.random.default_rng(42) |
| 40 | +``` |
| 41 | + |
| 42 | +```{code-cell} ipython3 |
| 43 | +lags = 2 |
| 44 | +trials = 100 |
| 45 | +
|
| 46 | +
|
| 47 | +def ar_dist(ar_init, rho, sigma, size): |
| 48 | + def ar_step(x_tm2, x_tm1, rho, sigma): |
| 49 | + mu = x_tm1 * rho[0] + x_tm2 * rho[1] |
| 50 | + x = mu + pm.Normal.dist(sigma=sigma) |
| 51 | + return x, collect_default_updates([x]) |
| 52 | +
|
| 53 | + ar_innov, _ = pytensor.scan( |
| 54 | + fn=ar_step, |
| 55 | + outputs_info=[{"initial": ar_init, "taps": range(-lags, 0)}], |
| 56 | + non_sequences=[rho, sigma], |
| 57 | + n_steps=trials - lags, |
| 58 | + strict=True, |
| 59 | + ) |
| 60 | +
|
| 61 | + return ar_innov |
| 62 | +``` |
| 63 | + |
| 64 | +```{code-cell} ipython3 |
| 65 | +coords = { |
| 66 | + "lags": range(-lags, 0), |
| 67 | + "steps": range(trials - lags), |
| 68 | + "trials": range(trials), |
| 69 | +} |
| 70 | +with pm.Model(coords=coords, check_bounds=False) as model: |
| 71 | + rho = pm.Normal(name="rho", mu=0, sigma=0.2, dims=("lags",)) |
| 72 | + sigma = pm.HalfNormal(name="sigma", sigma=0.2) |
| 73 | +
|
| 74 | + ar_init_obs = pm.MutableData(name="ar_init_obs", value=np.zeros(lags), dims=("lags",)) |
| 75 | + ar_init = pm.Normal(name="ar_init", observed=ar_init_obs, dims=("lags",)) |
| 76 | +
|
| 77 | + ar_innov_obs = pm.MutableData("ar_innov_obs", np.zeros(trials - lags), dims=("steps",)) |
| 78 | + ar_innov = pm.CustomDist( |
| 79 | + "ar_dist", |
| 80 | + ar_init, |
| 81 | + rho, |
| 82 | + sigma, |
| 83 | + dist=ar_dist, |
| 84 | + observed=ar_innov_obs, |
| 85 | + dims=("steps",), |
| 86 | + ) |
| 87 | +
|
| 88 | + ar = pm.Deterministic( |
| 89 | + name="ar", var=pt.concatenate([ar_init, ar_innov], axis=-1), dims=("trials",) |
| 90 | + ) |
| 91 | +
|
| 92 | +
|
| 93 | +pm.model_to_graphviz(model) |
| 94 | +``` |
| 95 | + |
| 96 | +## Prior |
| 97 | + |
| 98 | +```{code-cell} ipython3 |
| 99 | +with model: |
| 100 | + prior = pm.sample_prior_predictive(samples=100, random_seed=rng) |
| 101 | +``` |
| 102 | + |
| 103 | +```{code-cell} ipython3 |
| 104 | +_, ax = plt.subplots() |
| 105 | +for i, hdi_prob in enumerate((0.94, 0.64), 1): |
| 106 | + hdi = az.hdi(prior.prior["ar"], hdi_prob=hdi_prob)["ar"] |
| 107 | + lower = hdi.sel(hdi="lower") |
| 108 | + upper = hdi.sel(hdi="higher") |
| 109 | + ax.fill_between( |
| 110 | + x=np.arange(trials), |
| 111 | + y1=lower, |
| 112 | + y2=upper, |
| 113 | + alpha=(i - 0.2) * 0.2, |
| 114 | + color="C0", |
| 115 | + label=f"{hdi_prob:.0%} HDI", |
| 116 | + ) |
| 117 | +ax.plot(prior.prior["ar"].mean(("chain", "draw")), color="C0", label="Mean") |
| 118 | +ax.legend(loc="upper right") |
| 119 | +ax.set_xlabel("trials") |
| 120 | +ax.set_title("AR(2) Prior Samples", fontsize=18, fontweight="bold") |
| 121 | +``` |
| 122 | + |
| 123 | +```{code-cell} ipython3 |
| 124 | +fig, ax = plt.subplots( |
| 125 | + nrows=5, ncols=1, figsize=(12, 12), sharex=True, sharey=True, layout="constrained" |
| 126 | +) |
| 127 | +chosen_draw = 2 |
| 128 | +for i, axi in enumerate(ax, start=chosen_draw): |
| 129 | + axi.plot( |
| 130 | + prior.prior["ar"].isel(draw=i, chain=0), |
| 131 | + color="C0" if i == chosen_draw else "black", |
| 132 | + ) |
| 133 | + axi.set_title(f"Sample {i}", fontsize=18, fontweight="bold") |
| 134 | +ax[-1].set_xlabel("trials") |
| 135 | +``` |
| 136 | + |
| 137 | +## Posterior |
| 138 | + |
| 139 | +```{code-cell} ipython3 |
| 140 | +prior_draw = prior.prior.isel(chain=0, draw=chosen_draw) |
| 141 | +
|
| 142 | +ar_init_obs.set_value(prior_draw["ar"].values[:lags]) |
| 143 | +ar_innov_obs.set_value(prior_draw["ar"].values[lags:]) |
| 144 | +ar_obs = prior_draw["ar"].to_numpy() |
| 145 | +rho_true = prior_draw["rho"].to_numpy() |
| 146 | +sigma_true = prior_draw["sigma"].to_numpy() |
| 147 | +
|
| 148 | +print(f"rho_true={np.round(rho_true, 3)}, {sigma_true=:.3f}") |
| 149 | +``` |
| 150 | + |
| 151 | +```{code-cell} ipython3 |
| 152 | +with model: |
| 153 | + trace = pm.sample(random_seed=rng) |
| 154 | +``` |
| 155 | + |
| 156 | +```{code-cell} ipython3 |
| 157 | +axes = az.plot_trace( |
| 158 | + data=trace, |
| 159 | + var_names=["rho", "sigma"], |
| 160 | + compact=True, |
| 161 | + lines=[ |
| 162 | + ("rho", {}, rho_true), |
| 163 | + ("sigma", {}, sigma_true), |
| 164 | + ], |
| 165 | + backend_kwargs={"figsize": (12, 5), "layout": "constrained"}, |
| 166 | +) |
| 167 | +plt.gcf().suptitle("AR(2) Model Trace", fontsize=18, fontweight="bold") |
| 168 | +``` |
| 169 | + |
| 170 | +```{code-cell} ipython3 |
| 171 | +axes = az.plot_posterior( |
| 172 | + trace, var_names=["rho", "sigma"], ref_val=[*rho_true, sigma_true], figsize=(15, 5) |
| 173 | +) |
| 174 | +plt.gcf().suptitle("AR(2) Model Parameters Posterior", fontsize=18, fontweight="bold") |
| 175 | +``` |
| 176 | + |
| 177 | +## Posterior Predictive |
| 178 | + |
| 179 | +```{code-cell} ipython3 |
| 180 | +with model: |
| 181 | + post_pred = pm.sample_posterior_predictive(trace, random_seed=rng) |
| 182 | +``` |
| 183 | + |
| 184 | +```{code-cell} ipython3 |
| 185 | +post_pred_ar = post_pred.posterior_predictive["ar"] |
| 186 | +
|
| 187 | +_, ax = plt.subplots() |
| 188 | +for i, hdi_prob in enumerate((0.94, 0.64), 1): |
| 189 | + hdi = az.hdi(post_pred_ar, hdi_prob=hdi_prob)["ar"] |
| 190 | + lower = hdi.sel(hdi="lower") |
| 191 | + upper = hdi.sel(hdi="higher") |
| 192 | + ax.fill_between( |
| 193 | + x=np.arange(trials), |
| 194 | + y1=lower, |
| 195 | + y2=upper, |
| 196 | + alpha=(i - 0.2) * 0.2, |
| 197 | + color="C0", |
| 198 | + label=f"{hdi_prob:.0%} HDI", |
| 199 | + ) |
| 200 | +ax.plot(prior.prior["ar"].mean(("chain", "draw")), color="C0", label="Mean") |
| 201 | +ax.plot(ar_obs, color="black", label="Observed") |
| 202 | +ax.legend(loc="upper right") |
| 203 | +ax.set_xlabel("trials") |
| 204 | +ax.set_title("AR(2) Posterior Predictive Samples", fontsize=18, fontweight="bold") |
| 205 | +``` |
| 206 | + |
| 207 | +```{code-cell} ipython3 |
| 208 | +fig, ax = plt.subplots( |
| 209 | + nrows=5, ncols=1, figsize=(12, 12), sharex=True, sharey=True, layout="constrained" |
| 210 | +) |
| 211 | +for i, axi in enumerate(ax): |
| 212 | + axi.plot(post_pred.posterior_predictive["ar"].isel(draw=i, chain=0), color="C0") |
| 213 | + axi.set_title(f"Sample {i}") |
| 214 | +
|
| 215 | +ax[-1].set_xlabel("trials") |
| 216 | +
|
| 217 | +fig.suptitle("AR(2) Posterior Predictive Samples", fontsize=18, fontweight="bold", y=1.05) |
| 218 | +``` |
| 219 | + |
| 220 | +## Authors |
| 221 | +- Authored by [Juan Orduz](https://juanitorduz.github.io/) and [Ricardo Vieira](https://github.com/ricardoV94) in March 2024 |
| 222 | + |
| 223 | ++++ |
| 224 | + |
| 225 | +## References |
| 226 | +:::{bibliography} |
| 227 | +:filter: docname in docnames |
| 228 | +::: |
| 229 | + |
| 230 | ++++ |
| 231 | + |
| 232 | +## Watermark |
| 233 | + |
| 234 | +```{code-cell} ipython3 |
| 235 | +%load_ext watermark |
| 236 | +%watermark -n -u -v -iv -w -p pytensor |
| 237 | +``` |
| 238 | + |
| 239 | +:::{include} ../page_footer.md |
| 240 | +::: |
0 commit comments