
Modelado 1: DLNM — Modelo de Rezago Distribuido No Lineal
Equipo climasus4r
17/10/2026
Source:vignettes/mod-01-dlnm.Rmd
mod-01-dlnm.RmdObjetivo
Al final de este tutorial usted será capaz de ajustar un DLNM
(Distributed Lag Non-linear Model) para cuantificar la relación
entre temperatura y mortalidad diaria, interpretar la curva
exposición-respuesta acumulativa, la curva
rezago-respuesta y la superficie bidimensional
exposición × rezago, y conocer todas las visualizaciones disponibles en
sus_mod_plot_dlnm().
El DLNM es la piedra angular del sombrero
bioestadístico de climasus4r. Conecta la serie
diaria de salud (producida en los Tutoriales 1–4) con la exposición
climática alineada (Tutoriales 7–8) y produce estimaciones de riesgo
relativo listas para la cuantificación de carga atribuible (Tutorial de
Modelado 2), metaanálisis multiciudad (Tutorial de Modelado 5) y
análisis de vulnerabilidad (Tutorial de Modelado 9).
Contexto científico
La epidemiología ambiental moderna reconoce que la asociación entre temperatura y mortalidad es no lineal y rezagada en el tiempo. El calor extremo mata en los primeros 1–2 días (efecto agudo); el frío mata a lo largo de 1 a 3 semanas (efecto tardío y acumulativo). Modelar solo la temperatura del mismo día subestima sistemáticamente el riesgo del frío y puede enmascarar el pico de mortalidad en el invierno subtropical.
Gasparrini, Armstrong & Kenward (2010)
propusieron el DLNM para resolver este problema con una
cross-basis: el producto externo de dos bases de splines — una
en la dimensión de la exposición (capta la forma en
U/J: el riesgo sube en el frío y en el calor) y otra en la dimensión del
rezago (capta cómo se distribuye el efecto en los días
siguientes). La implementación en R es el paquete dlnm
(Gasparrini, 2011), sobre el cual
climasus4r construye sus_mod_dlnm().
El ajuste sigue las buenas prácticas de regresión de series temporales en epidemiología ambiental sistematizadas por Bhaskaran et al. (2013): modelo log-lineal quasi-Poisson, control de tendencia de largo plazo y estacionalidad mediante spline natural del tiempo, y riesgo relativo centrado en la mediana de la exposición como valor de referencia (Armstrong, 2006).
Las fórmulas detalladas de la crossbasis y del RR acumulativo se presentan en la sección Fórmulas más abajo.
Caso conductor. Investigamos la temperatura y los desenlaces de salud en personas mayores (60+) en la Región Metropolitana de São Paulo, 2014–2019 (enfermedades respiratorias J00–J99). Combinamos la serie diaria de defunciones (
dados/caso_serie.rds, Tutorial 4) con la temperatura de la estación INMET A701 (dados/caso_clima_estacao.rds, Tutorial 7), construimos la estructura de rezagos distribuidos y ajustamos el DLNM quasi-Poisson para estimar la curva exposición-respuesta y el rezago de pico.
Fórmulas
El DLNM modela el número esperado de eventos en la fecha como:
donde es la función cross-basis — producto externo de una base de spline sobre la exposición (temperatura en el rezago ) y de una base de spline sobre el rezago , con vector de coeficientes ; es un spline natural del tiempo para controlar la estacionalidad y la tendencia de largo plazo; y controla el efecto del día de la semana.
El efecto acumulativo de la exposición en relación con el valor de referencia (mediana de rezago-0) es:
Parámetros clave implementados por
sus_mod_dlnm():
| Parámetro | Notación | Controlado por |
|---|---|---|
| Base de la exposición | sobre | argvar |
| Base del rezago | sobre | arglag |
| Ventana de rezago | lag_max |
|
| gl estacionalidad | gl de | dof_per_year |
| Valor de referencia | ref_value |
Datos utilizados
| Entrada | Origen | Contenido | Papel |
|---|---|---|---|
dados/caso_serie.rds |
Tutorial 4 | Serie diaria de defunciones (mayores 60+) | Desenlace n_obitos
|
dados/caso_clima_estacao.rds |
Tutorial 7 | Temperatura diaria de la estación A701 | Exposición tair_dry_bulb_c
|
Las dos series se unen por fecha. La exposición se
expande entonces en 22 columnas de rezago distribuido
(tair_dry_bulb_c_lag0, …,
tair_dry_bulb_c_lag21), estructura que en un flujo real es
producida por
sus_climate_aggregate(temporal_strategy = "distributed_lag", lag_days = 21).
El script protege cada etapa con file.exists() y, si faltan
las entradas, genera una serie sintética mínima
realista (con señal de temperatura-mortalidad en V) para que el
código funcione incluso sin el pipeline completo.
Pipeline completo
Los bloques a continuación reproducen exactamente lo que ejecuta
scripts/modelagem_01_dlnm.R.
library(climasus4r)Bloque 0 — Cargar las entradas encadenadas
# Caminhos das entradas encadeadas
path_saude <- file.path("vignettes-pt", "dados", "caso_serie.rds")
path_clima <- file.path("vignettes-pt", "dados", "caso_clima_estacao.rds")
# Série diária de saúde
saude_diaria <- NULL
if (file.exists(path_saude)) {
caso_serie <- readRDS(path_saude)
saude_df <- dplyr::as_tibble(caso_serie)
saude_diaria <- saude_df |>
dplyr::summarise(n_obitos = sum(n_obitos, na.rm = TRUE), .by = date) |>
dplyr::arrange(date)
}
# Série diária de temperatura
clima_diario <- NULL
if (file.exists(path_clima)) {
caso_clima <- readRDS(path_clima)
clima_diario <- caso_clima$clima_diario |>
dplyr::transmute(date = as.Date(date), tair_dry_bulb_c) |>
dplyr::arrange(date)
}
# Fallback sintético realista (série em V, 6 anos)
if (is.null(clima_diario) || is.null(saude_diaria)) {
# ... veja scripts/modelagem_01_dlnm.R para a implementação completa
}
# Juntar por data
serie <- dplyr::inner_join(saude_diaria, clima_diario, by = "date") |>
dplyr::arrange(date) |>
tidyr::drop_na(n_obitos, tair_dry_bulb_c)Bloque 1 — Construir la estructura de rezagos distribuidos
En el flujo completo de climasus4r, las columnas
_lagN se producen con
sus_climate_aggregate(temporal_strategy = "distributed_lag", lag_days = 21).
Aquí, con la serie ya alineada por fecha, generamos los rezagos
directamente:
VAR_TEMP <- "tair_dry_bulb_c"
LAG_MAX <- 21L
df_lags <- serie
for (k in 0:LAG_MAX) {
df_lags[[paste0(VAR_TEMP, "_lag", k)]] <- dplyr::lag(serie$tair_dry_bulb_c, n = k)
}
# Seleciona apenas: date, desfecho, colunas de lag
df_lags <- df_lags |>
dplyr::select(
date, n_obitos,
dplyr::all_of(paste0(VAR_TEMP, "_lag", 0:LAG_MAX))
)
# Promove a climasus_df com stage = "climate", type = "distributed_lag"
df_dl <- dplyr::as_tibble(df_lags)
attr(df_dl, "sus_meta") <- list(
system = "SIM",
stage = "climate",
type = "distributed_lag",
backend = "tibble",
history = "[derivado] serie diaria -> lags distribuidos tair_dry_bulb_c"
)
class(df_dl) <- c("climasus_df", class(df_dl))Bloque 2 — sus_mod_dlnm(): ajuste del DLNM
quasi-Poisson
fit <- sus_mod_dlnm(
df = df_dl,
outcome_col = "n_obitos",
climate_col = "tair_dry_bulb_c",
lag_max = 21L,
covariates = NULL, # confundidores adicionais (opcional)
argvar = list(fun = "ns", df = 4), # spline natural, 4 gl exposição
arglag = list(fun = "ns", df = 3), # spline natural, 3 gl lag
family = "quasipoisson", # robusto à superdispersão
ns_df = NULL, # calculado automaticamente via dof_per_year
dof_per_year = 4L, # gl/ano para sazonalidade (use 6 para Brasil subtropical)
ref_value = NULL, # mediana de lag-0 = valor de referencia
pred_at = c(0.25, 0.50, 0.75, 0.90, 0.95, 0.99),
alpha = 0.05,
lang = "pt",
verbose = TRUE
)
# Inspecionar o resultado
print(fit)
fit$exposure_response # RR cumulativo nos percentis pred_at
fit$lag_response # RR por lag no percentil 75
fit$diagnostics # razão de dispersão, Ljung-Box, AICBloque 3 — sus_mod_plot_dlnm(): todas las
visualizaciones
# overall: curva exposição-resposta cumulativa (painel principal + histograma)
sus_mod_plot_dlnm(fit, type = "overall", lang = "pt")
# lag: curva lag-resposta no percentil 75 (RR específico e cumulativo)
sus_mod_plot_dlnm(fit, type = "lag", lang = "pt")
# surface: superfície 3-D bidimensional exposição × lag
# (interactive = FALSE usa contorno estático como fallback)
sus_mod_plot_dlnm(fit, type = "surface", interactive = FALSE, lang = "pt")
# contour: mapa de contorno 2-D log(RR) para publicação
sus_mod_plot_dlnm(fit, type = "contour", lang = "pt")
# slice: curvas de resposta por lag específico (valores > lag_max são descartados)
sus_mod_plot_dlnm(fit, type = "slice",
lags_at = c(0L, 3L, 7L, 14L, 21L), lang = "pt")
# distribution: histograma/densidade da exposição com percentis-chave
sus_mod_plot_dlnm(fit, type = "distribution", lang = "pt")
# series: série temporal desfecho (barras) + exposição lag-0 (eixo dual)
sus_mod_plot_dlnm(fit, type = "series", lang = "pt")
# output_type = "all": retorna lista nomeada $plot, $table, $data
out <- sus_mod_plot_dlnm(fit, type = "overall",
output_type = "all", lang = "pt")
out$table # lista de 3 gt_tbl: especificação, curva RR, diagnósticos
out$data # lista: $exposure_response, $lag_response, $data_daily, $diagnosticsBloque 4 — Guardar el ajuste para reutilización (M2 y M8)
# O objeto climasus_dlnm é salvo para alimentar
# Tutorial M2 (fração atribuível) e M8 (meta-análise multi-cidade).
# save_tbl() (definida em scripts/_setup_tutoriais.R) persiste em
# vignettes-pt/dados/mod_dlnm_fit.rds; o script também copia para
# dados/mod_dlnm_fit.rds (raiz do pacote) para encadeamento com M2 e M8.
save_tbl(fit, "mod_dlnm_fit")
saveRDS(fit, file.path("dados", "mod_dlnm_fit.rds"))Características de las funciones
sus_mod_dlnm()
Ajusta un DLNM a partir de un climasus_df en la etapa
"climate" / tipo "distributed_lag", producido
por
sus_climate_aggregate(temporal_strategy = "distributed_lag").
Construye la crossbasis, ajusta un GLM y precomputa la
crosspred en una grilla de 100 puntos entre el percentil 1
y el 99 de la exposición.
| Argumento | Predeterminado | Descripción |
|---|---|---|
df |
— |
climasus_df con date, desenlace y columnas
{var}_lagN
|
outcome_col |
"n_obitos" |
Nombre de la columna de conteo diario del desenlace |
climate_col |
NULL |
Base del nombre de la exposición (sin _lagN);
autodetecta por prioridad si NULL
|
lag_max |
NULL |
Rezago máximo; autodetecta el mayor N en las columnas
_lagN
|
covariates |
NULL |
Confundidores adicionales a incluir linealmente en el GLM |
argvar |
list(fun="ns", df=4) |
Base de la dimensión de exposición de la crossbasis |
arglag |
list(fun="ns", df=3) |
Base de la dimensión de rezago de la crossbasis |
family |
"quasipoisson" |
Familia GLM; "negbin" se redirige a
"quasipoisson"
|
ns_df |
NULL |
gl para el spline temporal; NULL usa
dof_per_year × n_años
|
dof_per_year |
4L |
gl por año para estacionalidad (recomendado 6 en Brasil subtropical) |
ref_value |
NULL |
Valor de referencia (RR = 1); NULL usa la mediana de
rezago-0 |
pred_at |
c(.25,.5,.75,.9,.95,.99) |
Cuantiles a reportar en la tabla
$exposure_response
|
alpha |
0.05 |
Nivel del IC (predeterminado: 95% IC) |
lang |
"pt" |
Idioma de los mensajes: "pt", "en",
"es"
|
Retorno — objeto climasus_dlnm (lista
S3):
| Componente | Contenido |
|---|---|
$model |
Objeto glm ajustado (compatible con
dlnm::crosspred()) |
$crossbasis |
Objeto dlnm::crossbasis (base exposición × rezago) |
$pred |
Objeto dlnm::crosspred — allRRfit/low/high
(efecto general) y matRRfit/low/high (específico por
rezago) |
$exposure_response |
Tibble: pct, exposure, rr,
lo, hi en los percentiles
pred_at
|
$lag_response |
Tibble: lag, rr, lo,
hi, rr_cum — RR por rezago en el P75 |
$models |
Tibble resumen de 1 fila: variable, n,
family, lag_max, rr,
lo, hi, lag_peak,
disp_ratio, aic_poisson
|
$data_daily |
Tibble agregada por fecha (desenlace sumado; clima en promedio) usada en el ajuste |
$diagnostics |
Lista: disp_ratio, disp_category,
autocorr_pval, has_autocorr,
aic_poisson, deviance,
df_residual
|
$meta |
Lista con todos los parámetros del ajuste + historial
sus_meta
|
Selección de la base (argvar /
arglag):
argvar |
Cuándo usar |
|---|---|
list(fun="ns", df=3) |
No linealidad suave; conservador |
list(fun="ns", df=4) |
Curva en U/J (calor + frío, predeterminado) |
list(fun="ns", df=5) |
Dosis-respuesta compleja; N grandes |
list(fun="lin") |
Prueba de hipótesis lineal |
arglag |
Cuándo usar |
|---|---|
list(fun="ns", df=3) |
Decaimiento suave; mortalidad por temperatura (predeterminado) |
list(fun="ns", df=4) |
Rezago bimodal; efecto del frío |
list(fun="poly", df=2) |
Conservador; muestras pequeñas |
Control de estacionalidad
(dof_per_year):
-
4gl/año: series de 1–2 años; estacionalidad anual -
6gl/año: Brasil subtropical; estacionalidad más pronunciada -
8gl/año: arbovirosis; variación sub-estacional
Métodos S3 disponibles: print(),
summary(), coef(), tidy()
(devuelve un tibble de una fila compatible con
dplyr::bind_rows()).
sus_mod_plot_dlnm()
Produce visualizaciones científicas y tablas estadísticas a partir de
un objeto climasus_dlnm. Implementa los siete tipos de
visualización estándar en epidemiología de DLNM.
| Argumento | Predeterminado | Descripción |
|---|---|---|
fit |
— | Objeto climasus_dlnm de
sus_mod_dlnm()
|
type |
"overall" |
Tipo de visualización (ver tabla abajo) |
output_type |
"plot" |
"plot" → ggplot2/plotly; "table" → gt;
"all" → lista $plot, $table, $data
|
exposure_at |
NULL |
Exposición focal para el gráfico "lag" (predeterminado:
P75) |
lags_at |
c(0,3,7,14,21) |
Rezagos mostrados en el tipo "slice" (recortados en
lag_max) |
pred_at |
c(.05,.10,.25,.50,.75,.90,.95,.99) |
Cuantiles en la tabla estadística |
interactive |
FALSE |
TRUE → plotly::ggplotly() / superficie 3-D
plotly |
color_palette |
"npg" |
Paleta ggsci (otras: "lancet",
"jama", "nejm") |
lang |
"pt" |
Idioma de las etiquetas: "pt", "en",
"es"
|
Tipos de visualización (type =)
type |
Qué muestra | Referencia |
|---|---|---|
"overall" |
Curva exposición-respuesta acumulativa (RR + IC 95%) con histograma de la exposición en el panel inferior | Gasparrini et al. (2010) |
"lag" |
RR específico por rezago y acumulativo en un nivel de exposición dado (predeterminado P75) | Gasparrini et al. (2010) |
"surface" |
Superficie 3-D bidimensional exposición × rezago
(interactive = TRUE → plotly 3-D; estático → contorno) |
Gasparrini (2011) |
"contour" |
Mapa de contorno 2-D (log(RR), escala divergente) para publicación | Gasparrini et al. (2014) |
"slice" |
Curvas de respuesta para rezagos específicos superpuestas (perfiles dosis-respuesta por tiempo de rezago) | Armstrong (2006) |
"distribution" |
Histograma + densidad kernel de la exposición con marcadores en los percentiles clave | Bhaskaran et al. (2013) |
"series" |
Serie temporal diaria del desenlace (barras) y exposición (línea, eje dual) | Bhaskaran et al. (2013) |
Retorno (output_type):
-
"plot"→ objetoggplotoplotly -
"table"→ lista degt_tbl(o tibble sigtno está disponible) con 3 secciones: (1) especificación del modelo, (2) curva RR en los percentiles, (3) diagnósticos del modelo -
"all"→ lista nombrada$plot,$table,$data(que contiene$exposure_response,$lag_response,$data_daily,$diagnostics)
Resultados ilustrados
Serie temporal: desenlace y exposición

Figura 1. Serie temporal diaria de defunciones de personas mayores (barras verdes) y temperatura media (línea naranja, eje secundario escalado), RMSP 2014–2019. El patrón estacional es visible: los picos de mortalidad coinciden con el invierno subtropical (junio–agosto), cuando las temperaturas mínimas diarias caen por debajo del umbral de confort térmico. La exposición al calor extremo (verano austral, diciembre–febrero) produce un efecto más agudo.
Distribución de la temperatura

Figura 2. Distribución de la temperatura diaria (rezago 0) con percentiles clave marcados (P10, P25, P50 — referencia, P75, P90, P99). La mediana es el valor de referencia (RR = 1) del DLNM. La franja P25–P75 representa las condiciones térmicas “habituales”; los valores por encima del P90 y por debajo del P10 definen los umbrales de calor y frío extremos.
Curva exposición-respuesta acumulativa
(type = "overall")

Figura 3. Curva exposición-respuesta acumulativa (RR e IC 95% por el método delta). El patrón en V es característico de la asociación temperatura-mortalidad en el Brasil subtropical: riesgo elevado en el frío moderado (efecto tardío y acumulado) y, en menor magnitud, en el calor extremo (efecto agudo). La línea discontinua horizontal marca RR = 1; la línea punteada vertical marca la mediana de la exposición (valor de referencia). Los puntos coloreados indican los cuantiles reportados en la Tabla 1.
Curva rezago-respuesta (type = "lag")

Figura 4. Curva rezago-respuesta para el percentil 75 de la exposición. La línea sólida muestra el RR específico por rezago (efecto en cada día aislado); la línea discontinua muestra el RR acumulativo hasta ese rezago. El rezago de pico — día en que el efecto específico por rezago es máximo — se reporta en los diagnósticos del modelo.
Mapa de contorno (type = "contour")

Figura 5. Mapa de contorno del log(RR) en función de la temperatura (eje y) y del día de rezago (eje x). Los colores cálidos (naranja/rojo) indican exceso de riesgo; los colores fríos (verde) indican riesgo reducido en relación con la mediana. El mapa evidencia simultáneamente la forma (no linealidad respecto a la temperatura) y la distribución temporal (cómo se prolonga el efecto a lo largo de los rezagos). Las isolíneas blancas marcan los niveles ±0,1 y ±0,2 de log(RR).
Curvas por rezago específico (type = "slice")

Figura 6. Curvas de exposición-respuesta para rezagos seleccionados (0, 3, 7, 14 y 21 días). Cada curva muestra cómo cambia el perfil dosis-respuesta a lo largo del tiempo. El efecto del calor extremo es más pronunciado en el rezago 0–3 (respuesta aguda); el efecto del frío moderado persiste hasta el rezago 14–21 (respuesta tardía y distribuida).
Resumen estadístico del modelo
| Parametro | Valor |
|---|---|
| Exposicao climatica | tair_dry_bulb_c |
| Desfecho | n_obitos |
| Familia GLM | quasipoisson |
| Lag maximo (dias) | 21 |
| Base exposicao (argvar) | ns(df=4) |
| Base lag (arglag) | ns(df=3) |
| gl/ano (sazonalidade) | 4 |
| N observacoes (dias) | 2170 |
| Valor de referencia (degC) | 18.8 |
| RR cumulativo no P75 | 0.7355 |
| IC 95% RR (P75) | [0.4858; 1.1134] |
| Lag de efeito pico (dias) | 0 |
| Razao de dispersao (phi) | 1.132 |
| Categoria dispersao | adequate |
| AIC (Poisson re-fit) | 8051.1 |
| Ljung-Box p-valor | 0.0138 |
| Autocorrelacao residual | Sim (p < 0.05) |
| pct | exposure | rr | lo | hi |
|---|---|---|---|---|
| 0.25 | 15.980 | 0.7695253 | 0.5363601 | 1.104052 |
| 0.50 | 18.805 | 1.0003781 | 0.9938598 | 1.006939 |
| 0.75 | 21.950 | 0.7354524 | 0.4857910 | 1.113422 |
| 0.90 | 23.711 | 0.8367105 | 0.4584446 | 1.527086 |
| 0.95 | 24.376 | 0.9651056 | 0.4831790 | 1.927710 |
| 0.99 | 25.673 | 1.3823675 | 0.5457206 | 3.501682 |
Tabla 2. Riesgo Relativo acumulativo (IC 95%) para cada percentil de la distribución de temperatura, en relación con la mediana (RR = 1). Valores por encima de 1,00 indican exceso de riesgo; valores por debajo de 1,00 indican protección relativa. Percentiles por encima del P90 y por debajo del P10 indican exposiciones extremas.
Interpretación y buenas prácticas
Razón de dispersión
().
El quasi-Poisson se recomienda cuando
(Armstrong, 2006). La razón de dispersión se reporta en
fit$diagnostics$disp_ratio; la categoría
"adequate" indica
.
Autocorrelación residual. La prueba de Ljung-Box (10
rezagos de los residuos de devianza) detecta autocorrelación remanente
tras el control de estacionalidad. Si es positiva
(has_autocorr = TRUE), aumente dof_per_year o
agregue covariates = "dow" (día de la semana) (Bhaskaran et
al., 2013).
Grados de libertad del spline temporal. La regla
general es dof_per_year = 4 para 1–2 años de datos y
dof_per_year = 6 para el Brasil subtropical (estacionalidad
más acentuada). Para arbovirosis con picos sub-estacionales,
dof_per_year = 8. Visualice el residuo estacional con
fit$data_daily para verificar el ajuste.
Valor de referencia. El predeterminado es la mediana
de la exposición de rezago-0. En estudios comparativos multiciudad, use
un valor fijo común (ref_value = 20.0) para garantizar la
comparabilidad en las curvas exposición-respuesta.
Encadenamiento. El objeto
mod_dlnm_fit.rds (componente climasus_dlnm) es
la entrada de los tutoriales:
-
M2 (Fracción Atribuible):
sus_mod_af(fit = fit, ...) -
M5 (Metaanálisis):
sus_mod_pool(fits = list(rmsp = fit, ...), ...) - M8 (Bayes Espacio-Tiempo): modelo con offset basado en el RR estimado
Referencias
Armstrong, B. (2006). Models for the relationship between ambient temperature and daily deaths. Epidemiology, 17(6), 624–631. https://doi.org/10.1097/01.ede.0000239732.50999.58
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., 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. (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. (2014). 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
Saldanha, R. F., Bastos, R. R., & Barcellos, C. (2019). Microdatasus: pacote para download e pré-processamento de microdados do DATASUS. Cadernos de Saúde Pública, 35(9), e00032419.