Skip to content

Commit 9394bb8

Browse files
committed
feedback part 1
1 parent 87f40f8 commit 9394bb8

File tree

2 files changed

+166
-172
lines changed

2 files changed

+166
-172
lines changed

examples/time_series/Time_Series_Generative_Graph.ipynb

Lines changed: 131 additions & 171 deletions
Large diffs are not rendered by default.

examples/time_series/Time_Series_Generative_Graph.myst.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,19 @@ kernelspec:
2121

2222
+++
2323

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 {func}`~pytensor.scan.basic.scan` to loop efficiently inside a PyMC model.
24+
In this notebook, we show how to model and fit a time series model starting from a generative graph. In particular, we explain how to use {func}`~pytensor.scan.basic.scan` to loop efficiently inside a PyMC model.
25+
26+
:::{admonition} **Motivation**
27+
:class: note
28+
29+
Why would we do that, instead of just using {class}`~pm.distributions.timeseries.AR`? What are the benefits?
30+
31+
The pre-built time series models in PyMC are very useful and easy to use. Nevertheless, they are not flexible enough to model more complex time series models. By using a generative graph, we can model any time series model we want, as long as we can define it in terms of a generative graph. For example:
32+
33+
- Auto-regressive models with different noise distribution (e.g. {class}`~pm.distributions.continuous.StudentT` noise).
34+
- Exponential smoothing models.
35+
- ARIMA-GARCH models.
36+
:::
2537

2638
For this example, we consider an autoregressive model AR(2). Recall that an AR(2) model is defined as:
2739

@@ -61,17 +73,39 @@ We start by encoding the generative graph of the AR(2) model as a function `ar_d
6173

6274
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 {func}`~pytensor.scan.basic.scan` operation.
6375

76+
:::{admonition} **What are all of these functions?**
77+
:class: note
78+
79+
At first, it might seem a bit overwhelming to see all these functions. However, they are just helper functions to define the generative graph of the AR(2) model.
80+
81+
- {func}`~pymc.pytensorfcollect_default_updates` tells PyMC that the random variable (RV) in the generative graph should be updated in every iteration of the loop.
82+
83+
- {func}`~pytensor.scan.basic.scan` is an efficient way to loop inside a PyMC model. It is similar to the `for` loop in Python, but it is optimized for `pytensor`. We need to specify the following arguments:
84+
85+
- `fn`: The function that defines the transition steep.
86+
- `outputs_info`: The is the list of variables or dictionaries describing the initial state of the outputs computed recurrently.
87+
- `non_sequences`: The list of arguments that are passed to `fn` at each steps. In this case are the autoregressive coefficients and the noise standard deviation of the AR(2) model.
88+
- `n_steps`: The number of steps to loop.
89+
- `strict`: If `True`, all the shared variables used in `fn` must be provided as a part of `non_sequences` or `sequences` (In this example we do not use the argument `sequences`, which is the list of variables or dictionaries describing the sequences `scan` has to iterate over. In this case we can simply loop over the time steps).
90+
:::
91+
92+
Let's see concrete implementations:
93+
6494
```{code-cell} ipython3
6595
lags = 2 # Number of lags
6696
trials = 100 # Time series length
6797
6898
6999
def ar_dist(ar_init, rho, sigma, size):
100+
# This is the transition function for the AR(2) model.
101+
# We take as inputs previous steps and then specify the autoregressive relationship.
102+
# Finally, we add Gaussian noise to the model.
70103
def ar_step(x_tm2, x_tm1, rho, sigma):
71104
mu = x_tm1 * rho[0] + x_tm2 * rho[1]
72105
x = mu + pm.Normal.dist(sigma=sigma)
73106
return x, collect_default_updates([x])
74107
108+
# Here we actually "loop" over the time series.
75109
ar_innov, _ = pytensor.scan(
76110
fn=ar_step,
77111
outputs_info=[{"initial": ar_init, "taps": range(-lags, 0)}],

0 commit comments

Comments
 (0)