Skip to content

Commit 78cd6b9

Browse files
committed
intro
1 parent 243f366 commit 78cd6b9

File tree

2 files changed

+94
-8
lines changed

2 files changed

+94
-8
lines changed

examples/time_series/Time_Series_Generative_Graph.ipynb

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,32 @@
1616
":::"
1717
]
1818
},
19+
{
20+
"cell_type": "markdown",
21+
"id": "91d1fcf3",
22+
"metadata": {},
23+
"source": [
24+
"In This notebook, we show to model and fit a time series model starting from a generative graph. In particular, we explain how to use {class}`~pytensor.scan` to loop efficiently inside a PyMC model.\n",
25+
"\n",
26+
"For this example, we consider an autoregressive model AR(2). Recall that an AR(2) model is defined as:\n",
27+
"\n",
28+
"$$\n",
29+
"\\begin{align*}\n",
30+
"y_t &= \\rho_1 y_{t-1} + \\rho_2 y_{t-2} + \\varepsilon_t, \\quad \\varepsilon_t \\sim \\mathcal{N}(0, \\sigma^2)\n",
31+
"\\end{align*}\n",
32+
"$$\n",
33+
"\n",
34+
"That is, we have a recursive linear model in term of the first two lags of the time series."
35+
]
36+
},
1937
{
2038
"cell_type": "code",
2139
"execution_count": 1,
2240
"id": "elect-softball",
2341
"metadata": {
24-
"tags": []
42+
"tags": [
43+
"hide-input"
44+
]
2545
},
2646
"outputs": [
2747
{
@@ -52,18 +72,30 @@
5272
"rng = np.random.default_rng(42)"
5373
]
5474
},
75+
{
76+
"cell_type": "markdown",
77+
"id": "dd1d7055",
78+
"metadata": {},
79+
"source": [
80+
"## Define AR(2) Process\n",
81+
"\n",
82+
"We start by encoding the generative graph of the AR(2) model as a function `ar_dist`. The strategy is to pass this function as a custom distribution via {class}`pm.CustomDist` inside a PyMC model. \n",
83+
"\n",
84+
"We need to specify the initial state (`ar_init`), the autoregressive coefficients (`rho`), and the standard deviation of the noise (`sigma`). Given such parameters, we can define the generative graph of the AR(2) model using the {class}`~pytensor.scan` operation."
85+
]
86+
},
5587
{
5688
"cell_type": "code",
5789
"execution_count": 2,
5890
"id": "25029181",
5991
"metadata": {},
6092
"outputs": [],
6193
"source": [
62-
"lags = 2\n",
63-
"trials = 100\n",
94+
"lags = 2 # Number of lags\n",
95+
"trials = 100 # Time series length\n",
6496
"\n",
6597
"\n",
66-
"def ar_dist(ar_init, rho, sigma, size):\n",
98+
"def ar_dist(ar_init, rho, sigma):\n",
6799
" def ar_step(x_tm2, x_tm1, rho, sigma):\n",
68100
" mu = x_tm1 * rho[0] + x_tm2 * rho[1]\n",
69101
" x = mu + pm.Normal.dist(sigma=sigma)\n",
@@ -80,6 +112,16 @@
80112
" return ar_innov"
81113
]
82114
},
115+
{
116+
"cell_type": "markdown",
117+
"id": "9af4ec18",
118+
"metadata": {},
119+
"source": [
120+
"## Generate AR(2) Graph\n",
121+
"\n",
122+
"Now that we have implemented the AR(2) step, we can assign priors to the parameters `rho` and `sigma`. We also specify the initial state `ar_init` as a zero vector."
123+
]
124+
},
83125
{
84126
"cell_type": "code",
85127
"execution_count": 3,
@@ -261,7 +303,9 @@
261303
"id": "7346db65",
262304
"metadata": {},
263305
"source": [
264-
"## Prior"
306+
"## Prior\n",
307+
"\n",
308+
"Let's sample from the prior distribution to see how the AR(2) model behaves."
265309
]
266310
},
267311
{
@@ -335,6 +379,16 @@
335379
"ax.set_title(\"AR(2) Prior Samples\", fontsize=18, fontweight=\"bold\")"
336380
]
337381
},
382+
{
383+
"cell_type": "markdown",
384+
"id": "37da4c81",
385+
"metadata": {},
386+
"source": [
387+
"It is not surprising that the prior distribution is a stationary process around zero given that the prior of the `rho` parameter is far from one.\n",
388+
"\n",
389+
"Let's look into individual samples to get a feeling of the heterogeneity of the generated series:"
390+
]
391+
},
338392
{
339393
"cell_type": "code",
340394
"execution_count": 6,

examples/time_series/Time_Series_Generative_Graph.myst.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,23 @@ kernelspec:
1919
:author: Juan Orduz and Ricardo Vieira
2020
:::
2121

22+
+++
23+
24+
In This notebook, we show to model and fit a time series model starting from a generative graph. In particular, we explain how to use {class}`~pytensor.scan` to loop efficiently inside a PyMC model.
25+
26+
For this example, we consider an autoregressive model AR(2). Recall that an AR(2) model is defined as:
27+
28+
$$
29+
\begin{align*}
30+
y_t &= \rho_1 y_{t-1} + \rho_2 y_{t-2} + \varepsilon_t, \quad \varepsilon_t \sim \mathcal{N}(0, \sigma^2)
31+
\end{align*}
32+
$$
33+
34+
That is, we have a recursive linear model in term of the first two lags of the time series.
35+
2236
```{code-cell} ipython3
37+
:tags: [hide-input]
38+
2339
import arviz as az
2440
import matplotlib.pyplot as plt
2541
import numpy as np
@@ -39,12 +55,18 @@ plt.rcParams["figure.facecolor"] = "white"
3955
rng = np.random.default_rng(42)
4056
```
4157

58+
## Define AR(2) Process
59+
60+
We start by encoding the generative graph of the AR(2) model as a function `ar_dist`. The strategy is to pass this function as a custom distribution via {class}`pm.CustomDist` inside a PyMC model.
61+
62+
We need to specify the initial state (`ar_init`), the autoregressive coefficients (`rho`), and the standard deviation of the noise (`sigma`). Given such parameters, we can define the generative graph of the AR(2) model using the {class}`~pytensor.scan` operation.
63+
4264
```{code-cell} ipython3
43-
lags = 2
44-
trials = 100
65+
lags = 2 # Number of lags
66+
trials = 100 # Time series length
4567
4668
47-
def ar_dist(ar_init, rho, sigma, size):
69+
def ar_dist(ar_init, rho, sigma):
4870
def ar_step(x_tm2, x_tm1, rho, sigma):
4971
mu = x_tm1 * rho[0] + x_tm2 * rho[1]
5072
x = mu + pm.Normal.dist(sigma=sigma)
@@ -61,6 +83,10 @@ def ar_dist(ar_init, rho, sigma, size):
6183
return ar_innov
6284
```
6385

86+
## Generate AR(2) Graph
87+
88+
Now that we have implemented the AR(2) step, we can assign priors to the parameters `rho` and `sigma`. We also specify the initial state `ar_init` as a zero vector.
89+
6490
```{code-cell} ipython3
6591
coords = {
6692
"lags": range(-lags, 0),
@@ -95,6 +121,8 @@ pm.model_to_graphviz(model)
95121

96122
## Prior
97123

124+
Let's sample from the prior distribution to see how the AR(2) model behaves.
125+
98126
```{code-cell} ipython3
99127
with model:
100128
prior = pm.sample_prior_predictive(samples=100, random_seed=rng)
@@ -120,6 +148,10 @@ ax.set_xlabel("trials")
120148
ax.set_title("AR(2) Prior Samples", fontsize=18, fontweight="bold")
121149
```
122150

151+
It is not surprising that the prior distribution is a stationary process around zero given that the prior of the `rho` parameter is far from one.
152+
153+
Let's look into individual samples to get a feeling of the heterogeneity of the generated series:
154+
123155
```{code-cell} ipython3
124156
fig, ax = plt.subplots(
125157
nrows=5, ncols=1, figsize=(12, 12), sharex=True, sharey=True, layout="constrained"

0 commit comments

Comments
 (0)