# Comparing samplers

In this tutorial, we compare two different sampling schemes on the
`mcycle` dataset with a Gaussian location-scale regression model and two
splines for the mean and the standard deviation. The `mcycle` dataset is
a “data frame giving a series of measurements of head acceleration in a
simulated motorcycle accident, used to test crash helmets” (from the
help page). It contains the following two variables:

- `times`: in milliseconds after impact
- `accel`: in g

We set up the model in Python with
[Liesel-GAM](https://github.com/liesel-devs/liesel_gam), using
{class}`liesel_gam.TermBuilder` for the P-spline terms. See the
[Liesel-GAM documentation and
examples](https://github.com/liesel-devs/liesel_gam#readme) for more
information about additive terms and predictors. We load the data set
from R with [ryp](https://github.com/Wainberg/ryp) and then continue
with a pure Python model specification and sampling workflow.

``` python
from pathlib import Path

import jax.numpy as jnp
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import tensorflow_probability.substrates.jax.bijectors as tfb
import tensorflow_probability.substrates.jax.distributions as tfd

import liesel.goose as gs
import liesel.model as lsl
import liesel_gam as gam
from ryp import r, to_py
```

    Warning message:
    package ‘arrow’ was built under R version 4.5.2

We start by loading the data set from the R package `MASS` and
converting it to a pandas data frame.

``` python
r("library(MASS)")
r("data(mcycle); mcycle <- as.data.frame(mcycle)")

mcycle = to_py("mcycle", format="pandas")
```

``` python
fig, ax = plt.subplots(figsize=(8, 4))
sns.scatterplot(data=mcycle, x="times", y="accel", color="0.25", s=35, ax=ax)
ax.set(xlabel="time after impact", ylabel="acceleration", title="mcycle data")
plt.show()
```

<img src="04-mcycle_files/figure-commonmark/data-output-1.png"
id="data" />

Next, we build the Gaussian location-scale model. Both distributional
parameters use an additive predictor with an intercept and a P-spline in
`times`. The scale predictor uses an exponential inverse link to keep
the standard deviation positive. The `TermBuilder` is initialized with
an IWLS MCMC specification, so the P-spline regression coefficients are
sampled with IWLS kernels by default. The additive predictor intercepts
also use their default IWLS inference specification. The smoothing
variances of the P-splines receive Gibbs kernels automatically.

``` python
tb = gam.TermBuilder.from_df(mcycle, default_inference=gs.MCMCSpec(gs.IWLSKernel))

loc = gam.AdditivePredictor("loc")
scale = gam.AdditivePredictor("scale", inv_link=jnp.exp)

loc_smooth = tb.ps("times", k=20, prefix="loc.")
scale_smooth = tb.ps("times", k=20, prefix="scale.")

loc += loc_smooth
scale += scale_smooth

response_dist = lsl.Dist(tfd.Normal, loc=loc, scale=scale)
y = lsl.Var.new_obs(mcycle["accel"], response_dist, name="y")
model = lsl.Model(y)
```

## Metropolis-in-Gibbs

First, we run the model with the inference specifications attached
during model construction. This gives a Metropolis-in-Gibbs sampling
scheme with IWLS kernels for the regression coefficients
($\boldsymbol{\beta}$) and Gibbs kernels for the smoothing variances
($\tau^2$) of the splines.

``` python
iwls_results = gs.LieselMCMC(model).run_for_epochs(
    seed=1, num_chains=4, adaptation=1000, posterior=10000, posterior_thinning=10, show_progress=False
)
```

    liesel.goose.builder - WARNING - No jitter functions provided for position keys '$\\beta_{loc.ps(times)}$', '$\\beta_{scale.ps(times)}$', '$\\tau_{loc.ps(times)}^2$', '$\\beta_{0,loc}$', '$\\tau_{scale.ps(times)}^2$', '$\\beta_{0,scale}$'. The initial values for these keys won't be jittered
    liesel.goose.engine - INFO - Initializing kernels...
    liesel.goose.engine - INFO - Done
    liesel.goose.engine - INFO - Finished warmup

Clearly, the performance of the sampler could be better, especially for
the intercept of the mean. The corresponding chain exhibits a very
strong autocorrelation.

``` python
gs.Summary(iwls_results)
```

<p>
<strong>Parameter summary:</strong>
</p>
<table border="0" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>
</th>
<th>
</th>
<th>
kernel
</th>
<th>
mean
</th>
<th>
sd
</th>
<th>
q_0.05
</th>
<th>
q_0.5
</th>
<th>
q_0.95
</th>
<th>
sample_size
</th>
<th>
ess_bulk
</th>
<th>
ess_tail
</th>
<th>
rhat
</th>
</tr>
<tr>
<th>
parameter
</th>
<th>
index
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
<tr>
<th>
$\beta_{0,loc}$
</th>
<th>
()
</th>
<td>
kernel_03
</td>
<td>
-25.208
</td>
<td>
1.965
</td>
<td>
-28.423
</td>
<td>
-25.268
</td>
<td>
-21.903
</td>
<td>
4000
</td>
<td>
120.032
</td>
<td>
385.818
</td>
<td>
1.028
</td>
</tr>
<tr>
<th>
$\beta_{0,scale}$
</th>
<th>
()
</th>
<td>
kernel_05
</td>
<td>
2.724
</td>
<td>
0.074
</td>
<td>
2.604
</td>
<td>
2.723
</td>
<td>
2.850
</td>
<td>
4000
</td>
<td>
949.963
</td>
<td>
2440.940
</td>
<td>
1.006
</td>
</tr>
<tr>
<th rowspan="19" valign="top">
$\beta_{loc.ps(times)}$
</th>
<th>
(0,)
</th>
<td>
kernel_00
</td>
<td>
-2.524
</td>
<td>
10.793
</td>
<td>
-20.055
</td>
<td>
-2.508
</td>
<td>
15.091
</td>
<td>
4000
</td>
<td>
3090.440
</td>
<td>
3276.193
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(1,)
</th>
<td>
kernel_00
</td>
<td>
-11.400
</td>
<td>
10.177
</td>
<td>
-29.560
</td>
<td>
-10.567
</td>
<td>
4.041
</td>
<td>
4000
</td>
<td>
2135.275
</td>
<td>
3303.146
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(2,)
</th>
<td>
kernel_00
</td>
<td>
1.511
</td>
<td>
9.359
</td>
<td>
-13.859
</td>
<td>
1.501
</td>
<td>
17.137
</td>
<td>
4000
</td>
<td>
2723.351
</td>
<td>
3657.486
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(3,)
</th>
<td>
kernel_00
</td>
<td>
-3.298
</td>
<td>
9.018
</td>
<td>
-17.980
</td>
<td>
-3.070
</td>
<td>
11.133
</td>
<td>
4000
</td>
<td>
3003.180
</td>
<td>
3224.344
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(4,)
</th>
<td>
kernel_00
</td>
<td>
-9.468
</td>
<td>
8.766
</td>
<td>
-24.269
</td>
<td>
-9.294
</td>
<td>
4.662
</td>
<td>
4000
</td>
<td>
2946.688
</td>
<td>
3561.010
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(5,)
</th>
<td>
kernel_00
</td>
<td>
-10.234
</td>
<td>
8.355
</td>
<td>
-24.368
</td>
<td>
-9.992
</td>
<td>
3.258
</td>
<td>
4000
</td>
<td>
3195.905
</td>
<td>
3687.709
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(6,)
</th>
<td>
kernel_00
</td>
<td>
-0.227
</td>
<td>
7.849
</td>
<td>
-13.745
</td>
<td>
-0.047
</td>
<td>
12.472
</td>
<td>
4000
</td>
<td>
3188.969
</td>
<td>
3367.629
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(7,)
</th>
<td>
kernel_00
</td>
<td>
0.419
</td>
<td>
7.437
</td>
<td>
-11.654
</td>
<td>
0.466
</td>
<td>
12.509
</td>
<td>
4000
</td>
<td>
3357.348
</td>
<td>
3797.740
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(8,)
</th>
<td>
kernel_00
</td>
<td>
10.635
</td>
<td>
6.672
</td>
<td>
-0.471
</td>
<td>
10.726
</td>
<td>
21.610
</td>
<td>
4000
</td>
<td>
3461.824
</td>
<td>
3833.617
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(9,)
</th>
<td>
kernel_00
</td>
<td>
-15.581
</td>
<td>
5.801
</td>
<td>
-25.258
</td>
<td>
-15.562
</td>
<td>
-6.206
</td>
<td>
4000
</td>
<td>
2838.619
</td>
<td>
3115.745
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(10,)
</th>
<td>
kernel_00
</td>
<td>
-7.541
</td>
<td>
4.845
</td>
<td>
-15.574
</td>
<td>
-7.503
</td>
<td>
0.180
</td>
<td>
4000
</td>
<td>
2585.065
</td>
<td>
3339.469
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(11,)
</th>
<td>
kernel_00
</td>
<td>
-23.790
</td>
<td>
4.381
</td>
<td>
-30.970
</td>
<td>
-23.769
</td>
<td>
-16.681
</td>
<td>
4000
</td>
<td>
3224.815
</td>
<td>
3611.463
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(12,)
</th>
<td>
kernel_00
</td>
<td>
9.344
</td>
<td>
3.253
</td>
<td>
4.176
</td>
<td>
9.314
</td>
<td>
14.688
</td>
<td>
4000
</td>
<td>
3408.073
</td>
<td>
3576.336
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(13,)
</th>
<td>
kernel_00
</td>
<td>
-10.210
</td>
<td>
2.572
</td>
<td>
-14.541
</td>
<td>
-10.185
</td>
<td>
-6.134
</td>
<td>
4000
</td>
<td>
3188.634
</td>
<td>
3555.224
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(14,)
</th>
<td>
kernel_00
</td>
<td>
12.244
</td>
<td>
1.879
</td>
<td>
9.131
</td>
<td>
12.266
</td>
<td>
15.183
</td>
<td>
4000
</td>
<td>
3281.223
</td>
<td>
3167.412
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(15,)
</th>
<td>
kernel_00
</td>
<td>
2.256
</td>
<td>
1.234
</td>
<td>
0.241
</td>
<td>
2.267
</td>
<td>
4.175
</td>
<td>
4000
</td>
<td>
2110.177
</td>
<td>
2477.466
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(16,)
</th>
<td>
kernel_00
</td>
<td>
-3.140
</td>
<td>
0.642
</td>
<td>
-4.201
</td>
<td>
-3.127
</td>
<td>
-2.140
</td>
<td>
4000
</td>
<td>
3402.169
</td>
<td>
3136.336
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(17,)
</th>
<td>
kernel_00
</td>
<td>
0.913
</td>
<td>
0.241
</td>
<td>
0.514
</td>
<td>
0.918
</td>
<td>
1.291
</td>
<td>
4000
</td>
<td>
1220.134
</td>
<td>
3013.988
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(18,)
</th>
<td>
kernel_00
</td>
<td>
2.982
</td>
<td>
0.922
</td>
<td>
1.511
</td>
<td>
3.001
</td>
<td>
4.396
</td>
<td>
4000
</td>
<td>
3292.914
</td>
<td>
2480.931
</td>
<td>
1.001
</td>
</tr>
<tr>
<th rowspan="19" valign="top">
$\beta_{scale.ps(times)}$
</th>
<th>
(0,)
</th>
<td>
kernel_01
</td>
<td>
0.005
</td>
<td>
0.141
</td>
<td>
-0.217
</td>
<td>
0.004
</td>
<td>
0.237
</td>
<td>
4000
</td>
<td>
1005.204
</td>
<td>
1235.971
</td>
<td>
1.005
</td>
</tr>
<tr>
<th>
(1,)
</th>
<td>
kernel_01
</td>
<td>
0.026
</td>
<td>
0.147
</td>
<td>
-0.195
</td>
<td>
0.017
</td>
<td>
0.264
</td>
<td>
4000
</td>
<td>
978.142
</td>
<td>
1155.381
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(2,)
</th>
<td>
kernel_01
</td>
<td>
0.004
</td>
<td>
0.138
</td>
<td>
-0.221
</td>
<td>
-0.001
</td>
<td>
0.225
</td>
<td>
4000
</td>
<td>
909.014
</td>
<td>
1177.690
</td>
<td>
1.005
</td>
</tr>
<tr>
<th>
(3,)
</th>
<td>
kernel_01
</td>
<td>
0.030
</td>
<td>
0.144
</td>
<td>
-0.189
</td>
<td>
0.022
</td>
<td>
0.269
</td>
<td>
4000
</td>
<td>
943.784
</td>
<td>
855.745
</td>
<td>
1.008
</td>
</tr>
<tr>
<th>
(4,)
</th>
<td>
kernel_01
</td>
<td>
0.031
</td>
<td>
0.143
</td>
<td>
-0.191
</td>
<td>
0.023
</td>
<td>
0.272
</td>
<td>
4000
</td>
<td>
972.728
</td>
<td>
1371.227
</td>
<td>
1.005
</td>
</tr>
<tr>
<th>
(5,)
</th>
<td>
kernel_01
</td>
<td>
-0.037
</td>
<td>
0.142
</td>
<td>
-0.277
</td>
<td>
-0.030
</td>
<td>
0.176
</td>
<td>
4000
</td>
<td>
775.126
</td>
<td>
997.943
</td>
<td>
1.008
</td>
</tr>
<tr>
<th>
(6,)
</th>
<td>
kernel_01
</td>
<td>
0.058
</td>
<td>
0.145
</td>
<td>
-0.147
</td>
<td>
0.043
</td>
<td>
0.327
</td>
<td>
4000
</td>
<td>
711.239
</td>
<td>
1034.814
</td>
<td>
1.009
</td>
</tr>
<tr>
<th>
(7,)
</th>
<td>
kernel_01
</td>
<td>
0.011
</td>
<td>
0.129
</td>
<td>
-0.202
</td>
<td>
0.008
</td>
<td>
0.225
</td>
<td>
4000
</td>
<td>
905.230
</td>
<td>
1052.320
</td>
<td>
1.007
</td>
</tr>
<tr>
<th>
(8,)
</th>
<td>
kernel_01
</td>
<td>
0.102
</td>
<td>
0.143
</td>
<td>
-0.092
</td>
<td>
0.080
</td>
<td>
0.368
</td>
<td>
4000
</td>
<td>
401.649
</td>
<td>
851.800
</td>
<td>
1.009
</td>
</tr>
<tr>
<th>
(9,)
</th>
<td>
kernel_01
</td>
<td>
-0.092
</td>
<td>
0.128
</td>
<td>
-0.326
</td>
<td>
-0.079
</td>
<td>
0.093
</td>
<td>
4000
</td>
<td>
743.720
</td>
<td>
1043.326
</td>
<td>
1.009
</td>
</tr>
<tr>
<th>
(10,)
</th>
<td>
kernel_01
</td>
<td>
0.115
</td>
<td>
0.117
</td>
<td>
-0.055
</td>
<td>
0.107
</td>
<td>
0.317
</td>
<td>
4000
</td>
<td>
626.941
</td>
<td>
1213.472
</td>
<td>
1.007
</td>
</tr>
<tr>
<th>
(11,)
</th>
<td>
kernel_01
</td>
<td>
0.010
</td>
<td>
0.112
</td>
<td>
-0.175
</td>
<td>
0.013
</td>
<td>
0.189
</td>
<td>
4000
</td>
<td>
818.491
</td>
<td>
1079.558
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(12,)
</th>
<td>
kernel_01
</td>
<td>
0.199
</td>
<td>
0.130
</td>
<td>
0.024
</td>
<td>
0.180
</td>
<td>
0.433
</td>
<td>
4000
</td>
<td>
309.252
</td>
<td>
739.200
</td>
<td>
1.017
</td>
</tr>
<tr>
<th>
(13,)
</th>
<td>
kernel_01
</td>
<td>
0.136
</td>
<td>
0.101
</td>
<td>
-0.015
</td>
<td>
0.129
</td>
<td>
0.311
</td>
<td>
4000
</td>
<td>
502.829
</td>
<td>
1151.172
</td>
<td>
1.005
</td>
</tr>
<tr>
<th>
(14,)
</th>
<td>
kernel_01
</td>
<td>
-0.079
</td>
<td>
0.086
</td>
<td>
-0.231
</td>
<td>
-0.068
</td>
<td>
0.043
</td>
<td>
4000
</td>
<td>
378.195
</td>
<td>
797.000
</td>
<td>
1.011
</td>
</tr>
<tr>
<th>
(15,)
</th>
<td>
kernel_01
</td>
<td>
0.042
</td>
<td>
0.056
</td>
<td>
-0.041
</td>
<td>
0.038
</td>
<td>
0.140
</td>
<td>
4000
</td>
<td>
579.289
</td>
<td>
1196.250
</td>
<td>
1.008
</td>
</tr>
<tr>
<th>
(16,)
</th>
<td>
kernel_01
</td>
<td>
0.026
</td>
<td>
0.034
</td>
<td>
-0.035
</td>
<td>
0.029
</td>
<td>
0.074
</td>
<td>
4000
</td>
<td>
392.207
</td>
<td>
889.776
</td>
<td>
1.013
</td>
</tr>
<tr>
<th>
(17,)
</th>
<td>
kernel_01
</td>
<td>
-0.063
</td>
<td>
0.013
</td>
<td>
-0.082
</td>
<td>
-0.064
</td>
<td>
-0.042
</td>
<td>
4000
</td>
<td>
624.398
</td>
<td>
1136.041
</td>
<td>
1.005
</td>
</tr>
<tr>
<th>
(18,)
</th>
<td>
kernel_01
</td>
<td>
0.111
</td>
<td>
0.050
</td>
<td>
0.027
</td>
<td>
0.111
</td>
<td>
0.191
</td>
<td>
4000
</td>
<td>
518.138
</td>
<td>
983.889
</td>
<td>
1.010
</td>
</tr>
<tr>
<th>
$\tau_{loc.ps(times)}^2$
</th>
<th>
()
</th>
<td>
kernel_02
</td>
<td>
138.693
</td>
<td>
68.000
</td>
<td>
62.523
</td>
<td>
122.988
</td>
<td>
270.299
</td>
<td>
4000
</td>
<td>
2808.853
</td>
<td>
3448.898
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
$\tau_{scale.ps(times)}^2$
</th>
<th>
()
</th>
<td>
kernel_04
</td>
<td>
0.021
</td>
<td>
0.020
</td>
<td>
0.003
</td>
<td>
0.015
</td>
<td>
0.060
</td>
<td>
4000
</td>
<td>
209.585
</td>
<td>
560.265
</td>
<td>
1.022
</td>
</tr>
</tbody>
</table>
<p>
<strong>Acceptance probabilities:</strong>
</p>
<table border="0" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>
</th>
<th>
</th>
<th>
</th>
<th>
acceptance_probability
</th>
<th>
position_moved
</th>
</tr>
<tr>
<th>
kernel
</th>
<th>
positions
</th>
<th>
phase
</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
<tr>
<th rowspan="2" valign="top">
kernel_00
</th>
<th rowspan="2" valign="top">
$\beta_{loc.ps(times)}$
</th>
<th>
posterior
</th>
<td>
0.865
</td>
<td>
0.865
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.794
</td>
<td>
0.796
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_01
</th>
<th rowspan="2" valign="top">
$\beta_{scale.ps(times)}$
</th>
<th>
posterior
</th>
<td>
0.868
</td>
<td>
0.868
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.793
</td>
<td>
0.795
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_02
</th>
<th rowspan="2" valign="top">
$\tau_{loc.ps(times)}^2$
</th>
<th>
posterior
</th>
<td>
1.000
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
1.000
</td>
<td>
1.000
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_03
</th>
<th rowspan="2" valign="top">
$\beta_{0,loc}$
</th>
<th>
posterior
</th>
<td>
0.922
</td>
<td>
0.922
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.922
</td>
<td>
0.919
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_04
</th>
<th rowspan="2" valign="top">
$\tau_{scale.ps(times)}^2$
</th>
<th>
posterior
</th>
<td>
1.000
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
1.000
</td>
<td>
1.000
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_05
</th>
<th rowspan="2" valign="top">
$\beta_{0,scale}$
</th>
<th>
posterior
</th>
<td>
0.906
</td>
<td>
0.905
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.912
</td>
<td>
0.912
</td>
</tr>
</tbody>
</table>

``` python
gs.plot_trace(iwls_results)
```

<img src="04-mcycle_files/figure-commonmark/iwls-traces-output-1.png"
id="iwls-traces" />

To confirm that the chains have converged to reasonable values, we plot
the posterior mean of the location predictor together with a 90%
credible interval:

``` python
def plot_loc_estimate(results, model, title):
    samples = results.get_posterior_samples()
    loc_samples = model.vars["loc"].predict(samples)
    loc_summary = gs.SamplesSummary.from_array(
        loc_samples,
        name="loc",
        which=["mean", "quantiles"],
    )
    loc_summary_df = loc_summary.to_dataframe().reset_index()

    loc_summary_df["times"] = mcycle["times"].to_numpy()
    plot_data = (
        loc_summary_df[["times", "mean", "q_0.05", "q_0.95"]]
        .groupby("times", as_index=False)
        .mean()
        .sort_values("times")
    )

    fig, ax = plt.subplots(figsize=(8, 5))
    ax.fill_between(
        plot_data["times"],
        plot_data["q_0.05"],
        plot_data["q_0.95"],
        color=sns.color_palette()[1],
        alpha=0.25,
        label="90% credible interval",
    )
    sns.lineplot(
        data=plot_data,
        x="times",
        y="mean",
        color=sns.color_palette()[1],
        linewidth=2,
        label="posterior mean",
        ax=ax,
    )
    sns.scatterplot(
        data=mcycle,
        x="times",
        y="accel",
        color="0.25",
        s=25,
        ax=ax,
        label="observed data",
    )
    ax.set(xlabel="time after impact", ylabel="acceleration", title=title)
    plt.show()


plot_loc_estimate(iwls_results, model, "Estimated mean function (IWLS/Gibbs)")
```

<img src="04-mcycle_files/figure-commonmark/iwls-spline-output-1.png"
id="iwls-spline" />

## NUTS sampler

As an alternative, we use NUTS kernels for the spline-specific parameter
blocks. The helper below copies the model graph, log-transforms the
smoothing variances by bijecting them with an exponential bijector, and
assigns one NUTS kernel group per additive term.

``` python
def strategy_term_blocked(
    model: lsl.Model, predictors: list[str], kernel_constructor, **kwargs
):
    model = model.copy()
    for k, v in model.parameters.items():
        if "tau" in k:
            v.biject(tfb.Exp(), inference="drop")

    for predictor_name in predictors:
        predictor = model.vars[predictor_name]
        if predictor.intercept:
            predictor.intercept.inference = gs.MCMCSpec(
                kernel_constructor, kernel_kwargs=kwargs
            )

        for term in predictor.terms.values():
            for param in model.parental_submodel(term).parameters.values():
                model.parameters[param.name].inference = gs.MCMCSpec(
                    kernel_constructor, kernel_group=term.name, kernel_kwargs=kwargs
                )

    return model
```

``` python
nuts_model = strategy_term_blocked(model, ["loc", "scale"], gs.NUTSKernel)
```

The resulting model contains transformed smoothing variances on the
unconstrained log scale. Here is the transformed model graph:

``` python
nuts_model.plot()
```

<img
src="04-mcycle_files/figure-commonmark/transformed-graph-output-1.png"
id="transformed-graph" />

Now we can run the sampler from the `MCMCSpec` objects stored in the
model. In complex models like this one, it can be beneficial to sample
the parameters of each additive term in a separate NUTS block.

``` python
nuts_results = gs.LieselMCMC(nuts_model).run_for_epochs(
    seed=1, num_chains=4, adaptation=1000, posterior=1000, show_progress=False
)
```

    liesel.goose.builder - WARNING - No jitter functions provided for position keys '$\\beta_{loc.ps(times)}$', 'h($\\tau_{loc.ps(times)}^2$)', '$\\beta_{scale.ps(times)}$', 'h($\\tau_{scale.ps(times)}^2$)', '$\\beta_{0,loc}$', '$\\beta_{0,scale}$'. The initial values for these keys won't be jittered
    liesel.goose.engine - INFO - Initializing kernels...
    liesel.goose.engine - INFO - Done
    liesel.goose.engine - INFO - Finished warmup

The blocked NUTS strategy overall seems to do a good job and can yield
higher effective sample sizes than the IWLS sampler, especially for the
spline coefficients of the scale model.

``` python
gs.Summary(nuts_results)
```

<p>
<strong>Parameter summary:</strong>
</p>
<table border="0" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>
</th>
<th>
</th>
<th>
kernel
</th>
<th>
mean
</th>
<th>
sd
</th>
<th>
q_0.05
</th>
<th>
q_0.5
</th>
<th>
q_0.95
</th>
<th>
sample_size
</th>
<th>
ess_bulk
</th>
<th>
ess_tail
</th>
<th>
rhat
</th>
</tr>
<tr>
<th>
parameter
</th>
<th>
index
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
<tr>
<th>
$\beta_{0,loc}$
</th>
<th>
()
</th>
<td>
kernel_02
</td>
<td>
-25.059
</td>
<td>
2.030
</td>
<td>
-28.648
</td>
<td>
-24.870
</td>
<td>
-22.147
</td>
<td>
4000
</td>
<td>
15.520
</td>
<td>
32.866
</td>
<td>
1.187
</td>
</tr>
<tr>
<th>
$\beta_{0,scale}$
</th>
<th>
()
</th>
<td>
kernel_03
</td>
<td>
2.723
</td>
<td>
0.075
</td>
<td>
2.605
</td>
<td>
2.720
</td>
<td>
2.851
</td>
<td>
4000
</td>
<td>
724.755
</td>
<td>
1476.686
</td>
<td>
1.010
</td>
</tr>
<tr>
<th rowspan="19" valign="top">
$\beta_{loc.ps(times)}$
</th>
<th>
(0,)
</th>
<td>
kernel_00
</td>
<td>
-2.691
</td>
<td>
10.873
</td>
<td>
-21.098
</td>
<td>
-2.417
</td>
<td>
14.901
</td>
<td>
4000
</td>
<td>
3172.592
</td>
<td>
2247.690
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(1,)
</th>
<td>
kernel_00
</td>
<td>
-11.200
</td>
<td>
10.141
</td>
<td>
-28.765
</td>
<td>
-10.610
</td>
<td>
4.500
</td>
<td>
4000
</td>
<td>
3030.145
</td>
<td>
2713.784
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(2,)
</th>
<td>
kernel_00
</td>
<td>
1.270
</td>
<td>
9.206
</td>
<td>
-14.030
</td>
<td>
1.372
</td>
<td>
16.822
</td>
<td>
4000
</td>
<td>
2862.872
</td>
<td>
2738.921
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(3,)
</th>
<td>
kernel_00
</td>
<td>
-3.303
</td>
<td>
9.319
</td>
<td>
-18.766
</td>
<td>
-3.385
</td>
<td>
11.490
</td>
<td>
4000
</td>
<td>
3138.678
</td>
<td>
2631.457
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(4,)
</th>
<td>
kernel_00
</td>
<td>
-9.211
</td>
<td>
9.045
</td>
<td>
-24.604
</td>
<td>
-9.011
</td>
<td>
5.188
</td>
<td>
4000
</td>
<td>
2791.808
</td>
<td>
2890.167
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(5,)
</th>
<td>
kernel_00
</td>
<td>
-10.299
</td>
<td>
8.634
</td>
<td>
-24.580
</td>
<td>
-10.163
</td>
<td>
3.489
</td>
<td>
4000
</td>
<td>
2653.144
</td>
<td>
2468.135
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(6,)
</th>
<td>
kernel_00
</td>
<td>
-0.358
</td>
<td>
7.721
</td>
<td>
-13.335
</td>
<td>
-0.316
</td>
<td>
12.184
</td>
<td>
4000
</td>
<td>
2836.645
</td>
<td>
2753.970
</td>
<td>
1.000
</td>
</tr>
<tr>
<th>
(7,)
</th>
<td>
kernel_00
</td>
<td>
0.310
</td>
<td>
7.274
</td>
<td>
-11.583
</td>
<td>
0.183
</td>
<td>
12.060
</td>
<td>
4000
</td>
<td>
2184.684
</td>
<td>
2109.061
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(8,)
</th>
<td>
kernel_00
</td>
<td>
10.700
</td>
<td>
6.754
</td>
<td>
-0.415
</td>
<td>
10.721
</td>
<td>
21.872
</td>
<td>
4000
</td>
<td>
2166.268
</td>
<td>
2440.807
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(9,)
</th>
<td>
kernel_00
</td>
<td>
-15.616
</td>
<td>
5.868
</td>
<td>
-25.287
</td>
<td>
-15.578
</td>
<td>
-6.413
</td>
<td>
4000
</td>
<td>
1839.594
</td>
<td>
2270.726
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(10,)
</th>
<td>
kernel_00
</td>
<td>
-7.430
</td>
<td>
4.891
</td>
<td>
-15.548
</td>
<td>
-7.313
</td>
<td>
0.655
</td>
<td>
4000
</td>
<td>
1608.507
</td>
<td>
1894.589
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(11,)
</th>
<td>
kernel_00
</td>
<td>
-23.869
</td>
<td>
4.395
</td>
<td>
-31.197
</td>
<td>
-23.822
</td>
<td>
-16.657
</td>
<td>
4000
</td>
<td>
1557.058
</td>
<td>
2153.137
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(12,)
</th>
<td>
kernel_00
</td>
<td>
9.163
</td>
<td>
3.302
</td>
<td>
3.914
</td>
<td>
9.056
</td>
<td>
14.694
</td>
<td>
4000
</td>
<td>
1368.387
</td>
<td>
1572.625
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(13,)
</th>
<td>
kernel_00
</td>
<td>
-10.188
</td>
<td>
2.546
</td>
<td>
-14.477
</td>
<td>
-10.144
</td>
<td>
-6.160
</td>
<td>
4000
</td>
<td>
1493.362
</td>
<td>
1663.695
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(14,)
</th>
<td>
kernel_00
</td>
<td>
12.304
</td>
<td>
1.850
</td>
<td>
9.305
</td>
<td>
12.302
</td>
<td>
15.380
</td>
<td>
4000
</td>
<td>
1466.403
</td>
<td>
1652.025
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(15,)
</th>
<td>
kernel_00
</td>
<td>
2.301
</td>
<td>
1.207
</td>
<td>
0.335
</td>
<td>
2.313
</td>
<td>
4.246
</td>
<td>
4000
</td>
<td>
525.186
</td>
<td>
1066.279
</td>
<td>
1.011
</td>
</tr>
<tr>
<th>
(16,)
</th>
<td>
kernel_00
</td>
<td>
-3.127
</td>
<td>
0.635
</td>
<td>
-4.149
</td>
<td>
-3.109
</td>
<td>
-2.140
</td>
<td>
4000
</td>
<td>
1243.732
</td>
<td>
1208.124
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(17,)
</th>
<td>
kernel_00
</td>
<td>
0.909
</td>
<td>
0.237
</td>
<td>
0.517
</td>
<td>
0.920
</td>
<td>
1.275
</td>
<td>
4000
</td>
<td>
358.475
</td>
<td>
1228.922
</td>
<td>
1.019
</td>
</tr>
<tr>
<th>
(18,)
</th>
<td>
kernel_00
</td>
<td>
3.025
</td>
<td>
0.909
</td>
<td>
1.563
</td>
<td>
3.063
</td>
<td>
4.408
</td>
<td>
4000
</td>
<td>
1136.638
</td>
<td>
1239.738
</td>
<td>
1.003
</td>
</tr>
<tr>
<th rowspan="19" valign="top">
$\beta_{scale.ps(times)}$
</th>
<th>
(0,)
</th>
<td>
kernel_01
</td>
<td>
-0.003
</td>
<td>
0.136
</td>
<td>
-0.225
</td>
<td>
-0.002
</td>
<td>
0.214
</td>
<td>
4000
</td>
<td>
4443.099
</td>
<td>
2264.366
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(1,)
</th>
<td>
kernel_01
</td>
<td>
0.027
</td>
<td>
0.140
</td>
<td>
-0.187
</td>
<td>
0.017
</td>
<td>
0.265
</td>
<td>
4000
</td>
<td>
4525.058
</td>
<td>
2201.980
</td>
<td>
1.006
</td>
</tr>
<tr>
<th>
(2,)
</th>
<td>
kernel_01
</td>
<td>
0.009
</td>
<td>
0.142
</td>
<td>
-0.216
</td>
<td>
0.007
</td>
<td>
0.237
</td>
<td>
4000
</td>
<td>
4704.091
</td>
<td>
2311.857
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(3,)
</th>
<td>
kernel_01
</td>
<td>
0.029
</td>
<td>
0.139
</td>
<td>
-0.182
</td>
<td>
0.021
</td>
<td>
0.260
</td>
<td>
4000
</td>
<td>
4996.028
</td>
<td>
2397.078
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(4,)
</th>
<td>
kernel_01
</td>
<td>
0.029
</td>
<td>
0.142
</td>
<td>
-0.185
</td>
<td>
0.021
</td>
<td>
0.255
</td>
<td>
4000
</td>
<td>
3931.107
</td>
<td>
1988.719
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(5,)
</th>
<td>
kernel_01
</td>
<td>
-0.031
</td>
<td>
0.144
</td>
<td>
-0.283
</td>
<td>
-0.025
</td>
<td>
0.193
</td>
<td>
4000
</td>
<td>
4520.747
</td>
<td>
2062.157
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(6,)
</th>
<td>
kernel_01
</td>
<td>
0.056
</td>
<td>
0.142
</td>
<td>
-0.150
</td>
<td>
0.043
</td>
<td>
0.302
</td>
<td>
4000
</td>
<td>
3017.528
</td>
<td>
1983.617
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
(7,)
</th>
<td>
kernel_01
</td>
<td>
0.011
</td>
<td>
0.128
</td>
<td>
-0.193
</td>
<td>
0.010
</td>
<td>
0.223
</td>
<td>
4000
</td>
<td>
4661.542
</td>
<td>
2274.334
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(8,)
</th>
<td>
kernel_01
</td>
<td>
0.091
</td>
<td>
0.143
</td>
<td>
-0.106
</td>
<td>
0.073
</td>
<td>
0.346
</td>
<td>
4000
</td>
<td>
1973.721
</td>
<td>
2407.005
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(9,)
</th>
<td>
kernel_01
</td>
<td>
-0.089
</td>
<td>
0.130
</td>
<td>
-0.317
</td>
<td>
-0.078
</td>
<td>
0.102
</td>
<td>
4000
</td>
<td>
2292.575
</td>
<td>
2297.788
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(10,)
</th>
<td>
kernel_01
</td>
<td>
0.115
</td>
<td>
0.119
</td>
<td>
-0.059
</td>
<td>
0.106
</td>
<td>
0.326
</td>
<td>
4000
</td>
<td>
2682.038
</td>
<td>
2554.996
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(11,)
</th>
<td>
kernel_01
</td>
<td>
0.008
</td>
<td>
0.113
</td>
<td>
-0.176
</td>
<td>
0.012
</td>
<td>
0.182
</td>
<td>
4000
</td>
<td>
3215.945
</td>
<td>
2081.179
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
(12,)
</th>
<td>
kernel_01
</td>
<td>
0.204
</td>
<td>
0.124
</td>
<td>
0.028
</td>
<td>
0.191
</td>
<td>
0.427
</td>
<td>
4000
</td>
<td>
887.550
</td>
<td>
1979.028
</td>
<td>
1.007
</td>
</tr>
<tr>
<th>
(13,)
</th>
<td>
kernel_01
</td>
<td>
0.136
</td>
<td>
0.099
</td>
<td>
-0.009
</td>
<td>
0.128
</td>
<td>
0.311
</td>
<td>
4000
</td>
<td>
1220.217
</td>
<td>
2118.627
</td>
<td>
1.006
</td>
</tr>
<tr>
<th>
(14,)
</th>
<td>
kernel_01
</td>
<td>
-0.083
</td>
<td>
0.085
</td>
<td>
-0.235
</td>
<td>
-0.075
</td>
<td>
0.042
</td>
<td>
4000
</td>
<td>
1018.994
</td>
<td>
1940.152
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(15,)
</th>
<td>
kernel_01
</td>
<td>
0.041
</td>
<td>
0.058
</td>
<td>
-0.047
</td>
<td>
0.036
</td>
<td>
0.147
</td>
<td>
4000
</td>
<td>
1154.360
</td>
<td>
1768.703
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(16,)
</th>
<td>
kernel_01
</td>
<td>
0.024
</td>
<td>
0.033
</td>
<td>
-0.032
</td>
<td>
0.026
</td>
<td>
0.076
</td>
<td>
4000
</td>
<td>
1211.201
</td>
<td>
1874.812
</td>
<td>
1.004
</td>
</tr>
<tr>
<th>
(17,)
</th>
<td>
kernel_01
</td>
<td>
-0.063
</td>
<td>
0.013
</td>
<td>
-0.083
</td>
<td>
-0.064
</td>
<td>
-0.041
</td>
<td>
4000
</td>
<td>
1241.131
</td>
<td>
1596.560
</td>
<td>
1.003
</td>
</tr>
<tr>
<th>
(18,)
</th>
<td>
kernel_01
</td>
<td>
0.109
</td>
<td>
0.050
</td>
<td>
0.032
</td>
<td>
0.107
</td>
<td>
0.193
</td>
<td>
4000
</td>
<td>
1358.989
</td>
<td>
1609.352
</td>
<td>
1.002
</td>
</tr>
<tr>
<th>
h($\tau_{loc.ps(times)}^2$)
</th>
<th>
()
</th>
<td>
kernel_00
</td>
<td>
4.832
</td>
<td>
0.427
</td>
<td>
4.156
</td>
<td>
4.819
</td>
<td>
5.555
</td>
<td>
4000
</td>
<td>
1907.703
</td>
<td>
2540.741
</td>
<td>
1.001
</td>
</tr>
<tr>
<th>
h($\tau_{scale.ps(times)}^2$)
</th>
<th>
()
</th>
<td>
kernel_01
</td>
<td>
-4.208
</td>
<td>
0.847
</td>
<td>
-5.668
</td>
<td>
-4.171
</td>
<td>
-2.850
</td>
<td>
4000
</td>
<td>
385.967
</td>
<td>
730.356
</td>
<td>
1.014
</td>
</tr>
</tbody>
</table>
<p>
<strong>Acceptance probabilities:</strong>
</p>
<table border="0" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>
</th>
<th>
</th>
<th>
</th>
<th>
acceptance_probability
</th>
<th>
position_moved
</th>
</tr>
<tr>
<th>
kernel
</th>
<th>
positions
</th>
<th>
phase
</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
<tr>
<th rowspan="2" valign="top">
kernel_00
</th>
<th rowspan="2" valign="top">
$\beta_{loc.ps(times)}$, h($\tau_{loc.ps(times)}^2$)
</th>
<th>
posterior
</th>
<td>
0.882
</td>
<td>
NaN
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.793
</td>
<td>
NaN
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_01
</th>
<th rowspan="2" valign="top">
$\beta_{scale.ps(times)}$, h($\tau_{scale.ps(times)}^2$)
</th>
<th>
posterior
</th>
<td>
0.877
</td>
<td>
NaN
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.795
</td>
<td>
NaN
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_02
</th>
<th rowspan="2" valign="top">
$\beta_{0,loc}$
</th>
<th>
posterior
</th>
<td>
0.858
</td>
<td>
NaN
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.792
</td>
<td>
NaN
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_03
</th>
<th rowspan="2" valign="top">
$\beta_{0,scale}$
</th>
<th>
posterior
</th>
<td>
0.878
</td>
<td>
NaN
</td>
</tr>
<tr>
<th>
warmup
</th>
<td>
0.793
</td>
<td>
NaN
</td>
</tr>
</tbody>
</table>
<p>
<strong>Error summary:</strong>
</p>
<table border="0" class="dataframe">
<thead>
<tr style="text-align: right;">
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
count
</th>
<th>
sample_size
</th>
<th>
sample_size_total
</th>
<th>
relative
</th>
</tr>
<tr>
<th>
kernel
</th>
<th>
positions
</th>
<th>
error_code
</th>
<th>
error_msg
</th>
<th>
phase
</th>
<th>
</th>
<th>
</th>
<th>
</th>
<th>
</th>
</tr>
</thead>
<tbody>
<tr>
<th rowspan="4" valign="top">
kernel_00
</th>
<th rowspan="4" valign="top">
$\beta_{loc.ps(times)}$, h($\tau_{loc.ps(times)}^2$)
</th>
<th rowspan="2" valign="top">
1
</th>
<th rowspan="2" valign="top">
divergent transition
</th>
<th>
warmup
</th>
<td>
369
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.092
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
26
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.007
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
2
</th>
<th rowspan="2" valign="top">
maximum tree depth
</th>
<th>
warmup
</th>
<td>
389
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.097
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
0
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
<tr>
<th rowspan="4" valign="top">
kernel_01
</th>
<th rowspan="4" valign="top">
$\beta_{scale.ps(times)}$, h($\tau_{scale.ps(times)}^2$)
</th>
<th rowspan="2" valign="top">
1
</th>
<th rowspan="2" valign="top">
divergent transition
</th>
<th>
warmup
</th>
<td>
277
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.069
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
0
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
2
</th>
<th rowspan="2" valign="top">
maximum tree depth
</th>
<th>
warmup
</th>
<td>
1
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
0
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_02
</th>
<th rowspan="2" valign="top">
$\beta_{0,loc}$
</th>
<th rowspan="2" valign="top">
1
</th>
<th rowspan="2" valign="top">
divergent transition
</th>
<th>
warmup
</th>
<td>
59
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.015
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
0
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
<tr>
<th rowspan="2" valign="top">
kernel_03
</th>
<th rowspan="2" valign="top">
$\beta_{0,scale}$
</th>
<th rowspan="2" valign="top">
1
</th>
<th rowspan="2" valign="top">
divergent transition
</th>
<th>
warmup
</th>
<td>
47
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.012
</td>
</tr>
<tr>
<th>
posterior
</th>
<td>
0
</td>
<td>
4000
</td>
<td>
4000
</td>
<td>
0.000
</td>
</tr>
</tbody>
</table>

``` python
gs.plot_trace(nuts_results)
```

<img src="04-mcycle_files/figure-commonmark/nuts-traces-output-1.png"
id="nuts-traces" />

Again, here is the posterior mean function with a 90% credible interval:

``` python
plot_loc_estimate(nuts_results, nuts_model, "Estimated mean function (NUTS)")
```

<img src="04-mcycle_files/figure-commonmark/nuts-spline-output-1.png"
id="nuts-spline" />
