
Tutorial 9: Epidemiological Modeling (DLNM and Attributable Burden)
climasus4r Team
17/10/2026
Source:vignettes/09-modelagem.Rmd
09-modelagem.RmdObjective
By the end of this tutorial you will be able to turn a daily
health series aligned with climate into epidemiological
inference: estimate the non-linear and lagged
relationship between temperature and mortality with a
DLNM (sus_mod_dlnm()), quantify the
attributable burden of heat and cold
(sus_mod_af()), and get to know the other tools in
climasus4r’s biostatistical toolkit — interrupted time
series (sus_mod_its()), case-crossover
(sus_mod_casecrossover()), and multi-city meta-analysis
(sus_mod_pool()).
Modeling is the last stage of the pipeline. It consumes the output of the previous stages (the aggregated health series from Tutorial 4 and the aligned climate exposure from Tutorials 7 and 8) and produces relative risk estimates, attributable fractions, and publication-ready visualizations.
Scientific context
Modern environmental epidemiology treats the association between an environmental exposure (temperature, pollutants) and a daily count outcome (deaths, hospitalizations) as a problem that is both non-linear and lagged in time. Extreme heat kills on the same day or within 1–2 days (acute effect); cold kills over one to two weeks (delayed, prolonged effect). Modeling only the day’s temperature underestimates the effect of cold.
The Distributed Lag Non-linear Model (DLNM) of
Gasparrini, Armstrong & Kenward (2010) solves this
with a cross-basis: the outer product of two spline
bases — one on the exposure dimension (captures the
U/J-shaped curve: risk rising with both cold and heat) and one on the
lag dimension (captures how the effect is distributed
over the days). The R implementation is the dlnm package
(Gasparrini, 2011), on top of which
climasus4r builds sus_mod_dlnm().
The fit follows the good practices for time-series regression in environmental epidemiology systematized by Bhaskaran et al. (2013): a log-linear quasi-Poisson model (robust to overdispersion), control for long-term trend and seasonality via a natural spline of time, and adjustment for day of week. The attributable fraction decomposes the total burden of events into heat and cold components (Gasparrini & Armstrong, 2013).
Guiding case. We investigate temperature and health outcomes in older adults (60+) in the São Paulo Metropolitan Region, 2014–2019 (respiratory J00–J99 and cardiovascular I00–I99). Here we join the daily death series (
caso_serie.rds, Tutorial 4) with the temperature from INMET station A701 (São Paulo – Mirante de Santana), already aligned (caso_clima_estacao.rds, Tutorial 7), build the distributed-lag structure, and fit the DLNM to estimate the exposure-response curve and the burden attributable to heat and cold.
Data used
| Input | Source | Content | Role |
|---|---|---|---|
dados/caso_serie.rds |
Tutorial 4 | Daily death series (older adults 60+) | Outcome n_obitos
|
dados/caso_clima_estacao.rds |
Tutorial 7 | Daily temperature from station A701 | Exposure tair_dry_bulb_c
|
The two series are joined by date. The exposure is
then expanded into distributed-lag columns
(tair_dry_bulb_c_lag0, …,
tair_dry_bulb_c_lag21), a structure that in a full workflow
is produced by
sus_climate_aggregate(temporal_strategy = "distributed_lag", lag_days = 21).
The script guards this stage with
requireNamespace()/tryCatch() and, if the
inputs are missing, generates a minimal, realistic synthetic
series (with a V-shaped temperature-mortality signal) purely
for illustration.
Full pipeline
The blocks below reproduce what
scripts/tutorial_09_modelagem.R executes.
library(climasus4r)Building the DLNM input (distributed lags)
sus_mod_dlnm() expects a climasus_df at
stage "climate" and type "distributed_lag",
with the date column, the outcome, and the
{variable}_lag0 … _lagL columns. In a real workflow this
comes from sus_climate_aggregate():
df_dl <- sus_climate_aggregate(
health_data = caso_serie, # health series (from Tutorial 4)
climate_data = caso_clima_estacao, # aligned temperature (from Tutorial 7)
climate_var = "tair_dry_bulb_c",
temporal_strategy = "distributed_lag",
lag_days = 21,
lang = "pt"
)Fitting the DLNM (sus_mod_dlnm)
Cross-basis with exposure on a natural spline with 4
degrees of freedom (captures the U/J heat+cold shape) and
lag on a natural spline with 3 degrees (smooth decay),
quasi-Poisson family, and automatic seasonal control
(dof_per_year = 6, recommended for subtropical Brazil):
fit <- sus_mod_dlnm(
df = df_dl,
outcome_col = "n_obitos",
climate_col = "tair_dry_bulb_c",
lag_max = 21,
argvar = list(fun = "ns", df = 4), # exposure dimension
arglag = list(fun = "ns", df = 3), # lag dimension
family = "quasipoisson",
dof_per_year = 6L, # df/year for seasonality
lang = "pt"
)
print(fit)
fit$exposure_response # cumulative RR on the percentile grid
fit$lag_response # RR by lag at the 75th percentile
fit$diagnostics$disp_ratioAttributable fraction from heat and cold
(sus_mod_af)
From the fitted DLNM, we decompose the total burden of deaths into heat (above the comfort threshold) and cold (below it) components, with confidence intervals from Monte Carlo simulation:
af <- sus_mod_af(
fit = fit,
by = "month", # optional monthly breakdown
nsim = 200L, # use 5000L for publication-quality results
lang = "pt"
)
print(af)
af$total # total AF/AN, heat and cold
af$by_period # monthly breakdownVisualizing (sus_mod_plot_dlnm)
The package offers ready-made scientific visualizations — exposure-response curve, lag-response, 3-D surface, contour, lag slices, distribution, and time series:
sus_mod_plot_dlnm(fit, type = "overall", lang = "pt") # exposure-response
sus_mod_plot_dlnm(fit, type = "contour", lang = "pt") # log(RR) surface
sus_mod_plot_dlnm(fit, type = "lag", lang = "pt") # lag-responseFunction characteristics
sus_mod_dlnm() — Distributed Lag Non-linear Model
| Argument | Essential? | Description |
|---|---|---|
df |
yes |
climasus_df at stage = "climate",
type = "distributed_lag"
|
outcome_col |
yes | Daily count column (e.g., "n_obitos") |
climate_col |
optional | Exposure base without the _lagN suffix;
NULL auto-detects |
lag_max |
optional | Maximum lag; NULL detects it from the largest
_lagN present |
argvar |
optional |
Exposure basis (e.g.,
list(fun = "ns", df = 4)) |
arglag |
optional |
Lag basis (e.g.,
list(fun = "ns", df = 3)) |
family |
optional |
"quasipoisson" (default), "poisson",
"negbin" (→ quasi) |
ns_df / dof_per_year
|
optional | Degrees of freedom of the time spline (seasonality) |
ref_value |
optional | Reference value (RR = 1); NULL uses the median |
covariates |
optional | Additional linear confounders (humidity, etc.) |
Returns: a climasus_dlnm object with
$model (GLM), $crossbasis, $pred
(crosspred on a 100-point grid),
$exposure_response, $lag_response,
$models (one-line summary), $data_daily, and
$diagnostics (dispersion, Ljung-Box autocorrelation,
AIC).
sus_mod_af() — Attributable Fraction and Number
| Argument | Description |
|---|---|
fit |
climasus_dlnm object from
sus_mod_dlnm()
|
threshold |
Threshold separating heat (above) and cold (below);
NULL uses ref_value
|
pred_at |
Percentiles of the ranges (default
c(0.75, 0.90, 0.95, 0.99)) |
by |
Temporal breakdown: "month", "year",
"season", or NULL
|
nsim |
Monte Carlo simulations for the CI (default 1000L) |
alpha |
Significance level (default 0.05 → 95% CI) |
Returns: climasus_af with
$total (total/heat/cold), $by_quantile,
$by_period, $daily, and
$custom.
sus_mod_its() — Interrupted Time Series
Segmented Poisson regression for the effect of interventions (policies, extreme events, pandemics) on a count series (Bernal et al., 2017).
| Argument | Description |
|---|---|
data |
climasus_df
(aggregate/climate) or data.frame
with date + count |
outcome_col / date_col
|
Outcome and date columns |
interruption_dates |
One or more intervention dates (within the range) |
harmonics |
Sinusoidal pairs for seasonality (default 2L) |
family |
"quasipoisson" (default) or "poisson"
|
counterfactual |
Projects the pre-intervention trend (default TRUE) |
Returns: climasus_its with
$effects (level and trend change),
$counterfactual, and $segments.
sus_mod_casecrossover() — Time-Stratified
Case-Crossover
Each time stratum (month/week) acts as its own control, removing trend and seasonality by design (Maclure, 1991; Armstrong et al., 2014).
| Argument | Description |
|---|---|
data |
climasus_df or data.frame with
date, outcome, and exposure |
outcome_col / exposure_col
|
Outcome and exposure (no auto-detection) |
stratum |
"month" (default), "week", or an existing
column |
lag |
Single integer lag or vector (moving average, e.g.,
0:6) |
method |
"conditional_poisson" (default) or
"clogit"
|
family |
"quasipoisson" (default) or "poisson"
|
Returns: climasus_casecrossover with
$or_table (OR, CI, p) and $diagnostics.
sus_mod_pool() — Multi-City Meta-Analysis
Combines DLNM estimates from several cities via multivariate
meta-analysis (REML via mvmeta), producing a
pooled curve, heterogeneity (Q, I²), and per-city BLUPs (Gasparrini et
al., 2012).
| Argument | Description |
|---|---|
fits |
Named list of climasus_dlnm objects
(same cross-basis) |
method |
"reml" (default), "ml", or
"fixed"
|
blup |
Computes per-city BLUP predictions (default TRUE) |
pred_at |
Percentiles of the pooled table |
Returns: climasus_pool with
$exposure_response, $lag_response,
$heterogeneity, $city_table, and
$blup_preds.
Statistical formulation
The DLNM models the daily count as quasi-Poisson with mean . The log-linear predictor combines the cross-basis — the effect of the exposure history — with control for seasonality (natural spline of time) and day of week (DOW):
The cross-basis is the outer product of a basis on exposure
(argvar) and a basis on lag (arglag). The
cumulative effect across all lags, at exposure level
relative to the reference
,
is
The cumulative attributable fraction associated with the exposure history follows the form (Gasparrini & Armstrong, 2013):
with the daily attributable number summed over the period and the CI obtained by Monte Carlo simulation of the cross-basis coefficients.
Modeling decisions
-
Overdispersion. Death counts typically have
variance greater than the mean
().
The quasi-Poisson family inflates the standard error by
and is recommended when the dispersion ratio exceeds ~1.5 (Armstrong,
2006). The negative binomial (NB) alternative is incompatible with
dlnm::crossbasis(), andclimasus4rredirects it to quasi-Poisson. -
Choice of knots and lags. For
exposure,
nswith 4 df captures the U/J shape (cold + heat) well; 3 df is more conservative, 5 df requires a large N. For lag,nswith 3 df models the smooth decay typical of the temperature-mortality relationship; a maximum lag of 21 days covers the delayed, prolonged effect of cold (Gasparrini, 2011). -
Seasonality. The natural spline of time absorbs
long-term trend and the annual cycle that confound the association. A
value of ~
dof_per_year = 4is recommended for 1–2 years and 6–8 for subtropical Brazil and diseases with strong seasonality (Bhaskaran et al., 2013). Underfitting leaves residual autocorrelation (Ljung-Box test in$diagnostics); overfitting consumes the signal.
Illustrated results

Figure 1. DLNM response surface:
log(RR) of deaths in older adults as a function of
temperature (y-axis) and lag day (x-axis). Orange tones indicate
increased risk (heat, short lags); green tones, reduced risk. The
two-dimensional structure reveals when, after each exposure level, the
effect manifests.

Figure 2. Cumulative exposure-response curve (RR and 95% CI) summed over all lags, centered on the median temperature (reference, RR = 1). The U/J shape — rising risk with both cold and heat — is the classic signature of the temperature-mortality relationship.

Figure 3. Lag-response curve at the 75th percentile of exposure: lag-specific RR between temperature and death. It allows distinguishing the acute effect (short lags) from the delayed, prolonged effect (long lags).

Figure 4. Attributable fraction of deaths (%) decomposed into total, heat, and cold, with 95% Monte Carlo CI. In subtropical climates the cold fraction tends to exceed the heat fraction, reflecting the prolonged effect of low temperatures on older adults.
| Parametro | Valor |
|---|---|
| Exposicao climatica | tair_dry_bulb_c |
| Desfecho | n_obitos |
| Familia GLM | quasipoisson |
| Lag maximo (dias) | 21 |
| N observacoes (dias) | 2170 |
| Valor de referencia (°C) | 18.8 |
| RR no P75 (vs referencia) | 0.922 [0.725; 1.172] |
| Lag de efeito pico (dias) | 21 |
| Razao de dispersao | 1.05 (adequate) |
| AIC (Poisson) | 11800.7 |
| Componente | FA (%) | NA (obitos) | N obitos |
|---|---|---|---|
| Total | 5.78 [-7.25; 17.87] | 1624 [-2037; 5018] | 28081 |
| Calor | 2.90 [-3.93; 8.48] | 814 [-1102; 2382] | 28081 |
| Frio | 2.89 [-3.35; 9.49] | 811 [-941; 2665] | 28081 |
References
- Armstrong, B. (2006). Models for the relationship between ambient temperature and daily mortality. Epidemiology, 17(6), 624–631.
- Bernal, J. L., Cummins, S., & Gasparrini, A. (2017). Interrupted time series regression for the evaluation of public health interventions: a tutorial. International Journal of Epidemiology, 46(1), 348–355. https://doi.org/10.1093/ije/dyw098
- Bhaskaran, K., Gasparrini, A., Hajat, S., Smeeth, L., & Armstrong, B. (2013). Time series regression studies in environmental epidemiology. International Journal of Epidemiology, 42(4), 1187–1195. https://doi.org/10.1093/ije/dyt092
- Gasparrini, A. (2011). Distributed lag linear and non-linear models in R: the package dlnm. Journal of Statistical Software, 43(8), 1–20. https://doi.org/10.18637/jss.v043.i08
- Gasparrini, A., Armstrong, B., & Kenward, M. G. (2010). Distributed lag non-linear models. Statistics in Medicine, 29(21), 2224–2234. https://doi.org/10.1002/sim.3940
- Gasparrini, A., & Armstrong, B. (2013). Reducing and meta-analysing estimates from distributed lag non-linear models. BMC Medical Research Methodology, 14, 70. https://doi.org/10.1186/1471-2288-14-70
- Maclure, M. (1991). The case-crossover design: a method for studying transient effects on the risk of acute events. American Journal of Epidemiology, 133(2), 144–153.
Navigation
- Back to tutorial hub
- Previous: Tutorial 8 — Gridded Climate and Environmental Data
- This is the last tutorial in the
climasus4rpipeline series.