R es un lenguaje de programación y un entorno de trabajo orientado a:
# Operaciones aritméticas simples
2 + 2
## [1] 4
5 * 3
## [1] 15
10 / 4
## [1] 2.5
# Asignación de objetos
x <- 10
y <- 3
x + y
## [1] 13
x^2
## [1] 100
# Crear un vector numérico
edades <- c(23, 35, 41, 29, 50, 60)
# Resumen rápido
summary(edades)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 23.00 30.50 38.00 39.67 47.75 60.00
# Operaciones sobre vectores
edades + 1 # agrega 1 a cada edad
## [1] 24 36 42 30 51 61
edades[edades >= 40] # filtra edades ≥ 40
## [1] 41 50 60
# Crear un data frame sencillo
pacientes <- data.frame(
id = 1:6,
sexo = c("Mujer", "Hombre", "Mujer", "Hombre", "Mujer", "Hombre"),
edad = c(23, 35, 41, 29, 50, 60),
hipertenso = c(0, 1, 1, 0, 1, 1) # 0 = No, 1 = Sí
)
pacientes
## id sexo edad hipertenso
## 1 1 Mujer 23 0
## 2 2 Hombre 35 1
## 3 3 Mujer 41 1
## 4 4 Hombre 29 0
## 5 5 Mujer 50 1
## 6 6 Hombre 60 1
knitr::kable(
pacientes,
caption = "Ejemplo de base de datos clínica mínima (pacientes simulados)."
)
| id | sexo | edad | hipertenso |
|---|---|---|---|
| 1 | Mujer | 23 | 0 |
| 2 | Hombre | 35 | 1 |
| 3 | Mujer | 41 | 1 |
| 4 | Hombre | 29 | 0 |
| 5 | Mujer | 50 | 1 |
| 6 | Hombre | 60 | 1 |
library(dplyr)
pacientes_resumen <- pacientes |>
group_by(sexo) |>
summarise(
n = n(),
edad_prom = mean(edad),
prop_hta = mean(hipertenso) # proporción con hipertensión
)
pacientes_resumen
## # A tibble: 2 × 4
## sexo n edad_prom prop_hta
## <chr> <int> <dbl> <dbl>
## 1 Hombre 3 41.3 0.667
## 2 Mujer 3 38 0.667
knitr::kable(
pacientes_resumen,
digits = 2,
caption = "Resumen de pacientes por sexo: tamaño de muestra, edad promedio y proporción de hipertensos."
)
| sexo | n | edad_prom | prop_hta |
|---|---|---|---|
| Hombre | 3 | 41.33 | 0.67 |
| Mujer | 3 | 38.00 | 0.67 |
library(ggplot2)
ggplot(pacientes, aes(x = edad)) +
geom_histogram(binwidth = 10, boundary = 0) +
labs(
title = "Distribución de edades en la muestra simulada",
x = "Edad (años)",
y = "Frecuencia"
)
pacientes |>
mutate(hta_factor = ifelse(hipertenso == 1, "Hipertenso", "No hipertenso")) |>
ggplot(aes(x = sexo, fill = hta_factor)) +
geom_bar(position = "fill") +
scale_y_continuous(labels = scales::percent) +
labs(
title = "Proporción de hipertensión por sexo (datos simulados)",
x = "Sexo",
y = "Proporción",
fill = "Condición"
)
Los paquetes son bibliotecas que extienden las capacidades de R.
install.packages(c("tidyverse", "ggplot2", "readxl"))
library(tidyverse)
library(ggplot2)
library(readxl)
.R): archivo de texto donde se
guarda código organizado. Permite reproducir análisis..Rmd): integra
texto, código y resultados (tablas, gráficas) en un mismo documento
reproducible (HTML, PDF, presentación, RPubs).La epidemiología es la disciplina que estudia la distribución y los determinantes de los eventos relacionados con la salud en poblaciones humanas, y la aplicación de este conocimiento al control de problemas de salud.
Incidencia: mide cuántos casos nuevos de una enfermedad ocurren en un período específico en una población en riesgo.
Prevalencia: mide cuántas personas presentan la enfermedad en un momento determinado (o período), independientemente de cuándo inició.
Este paquete proporciona datos y ejemplos del Epidemiologist R Handbook. El documento está diseñado para no fallar aunque no esté instalado; en ese caso, solo se mostrará un mensaje.
# Intentar cargar epirhandbook si está disponible
if (requireNamespace("epirhandbook", quietly = TRUE)) {
library(epirhandbook)
} else {
message(
"El paquete 'epirhandbook' no está instalado.\n",
"Si desea usar los datos del Epidemiologist R Handbook, ejecute en la consola:\n",
" install.packages('remotes')\n",
" remotes::install_github('appliedepi/epirhandbook')\n"
)
}
# Paquetes de trabajo general
paquetes_basicos <- c("tidyverse", "ggplot2", "readxl", "dplyr")
instalar_si_falta <- function(pk) {
if (!requireNamespace(pk, quietly = TRUE)) {
install.packages(pk)
}
library(pk, character.only = TRUE)
}
invisible(lapply(paquetes_basicos, instalar_si_falta))
Este bloque se deja como ejemplo y está desactivado
(eval=FALSE) para que el documento HTML se pueda compilar
sin depender de interacción con RStudio ni de archivos locales. Úselo
manualmente en su sesión si desea descargar los datos.
# Ejecutar manualmente en una sesión interactiva de RStudio para descargar los datos:
# (creará una carpeta y descargará los archivos de ejemplo)
library(epirhandbook)
epirhandbook::get_data("all")
# Ejemplo: lectura desde Excel (ajustar ruta según la carpeta donde descargó los datos)
#en una carpeta "EPIDATA/linelist_cleaned.xlsx"
Si previamente descargó los datos del Handbook y los almacenó en una
carpeta, por ejemplo EPIDATA/, puede usar el siguiente
código para leer la base linelist_cleaned.xlsx. Este bloque
asume que el archivo existe en esa ruta; si no, ajusta
la ruta o desactiva el chunk.
En esta sección se utilizará una base de datos tipo linelist, donde cada fila representa un caso y cada columna corresponde a una variable epidemiológica o clínica (edad, sexo, fecha de inicio de síntomas, desenlace, etc.).
Nota: este bloque asume que ya descargó los datos del Epidemiologist R Handbook y que el archivo
linelist_cleaned.xlsxse encuentra en una carpeta accesible (por ejemplo,EPIDATA/). Ajuste la ruta según su organización local.
# Ajustar la ruta si es necesario
ruta_linelist <- "C:/Users/fidel/OneDrive/Documentos/EPIDATA/linelist_cleaned.xlsx" #esto lo tienes que personalizar a tu path
if (!file.exists(ruta_linelist)) {
stop(
paste0(
"No se encontró el archivo ", ruta_linelist, ".\n",
"Verifique la ruta o descargue la base con epirhandbook::get_data('all')."
)
)
}
linelist <- readxl::read_excel(ruta_linelist)
# Vista general de las primeras filas
head(linelist)
## # A tibble: 6 × 30
## case_id generation date_infection date_onset
## <chr> <dbl> <dttm> <dttm>
## 1 5fe599 4 2014-05-08 00:00:00 2014-05-13 00:00:00
## 2 8689b7 4 NA 2014-05-13 00:00:00
## 3 11f8ea 2 NA 2014-05-16 00:00:00
## 4 b8812a 3 2014-05-04 00:00:00 2014-05-18 00:00:00
## 5 893f25 3 2014-05-18 00:00:00 2014-05-21 00:00:00
## 6 be99c8 3 2014-05-03 00:00:00 2014-05-22 00:00:00
## # ℹ 26 more variables: date_hospitalisation <dttm>, date_outcome <dttm>,
## # outcome <chr>, gender <chr>, age <dbl>, age_unit <chr>, age_years <dbl>,
## # age_cat <chr>, age_cat5 <chr>, hospital <chr>, lon <dbl>, lat <dbl>,
## # infector <chr>, source <chr>, wt_kg <dbl>, ht_cm <dbl>, ct_blood <dbl>,
## # fever <chr>, chills <chr>, cough <chr>, aches <chr>, vomit <chr>,
## # temp <dbl>, time_admission <chr>, bmi <dbl>, days_onset_hosp <dbl>
# Dimensiones de la base (número de filas y columnas)
dim(linelist)
## [1] 5888 30
# Nombres de las variables
names(linelist)
## [1] "case_id" "generation" "date_infection"
## [4] "date_onset" "date_hospitalisation" "date_outcome"
## [7] "outcome" "gender" "age"
## [10] "age_unit" "age_years" "age_cat"
## [13] "age_cat5" "hospital" "lon"
## [16] "lat" "infector" "source"
## [19] "wt_kg" "ht_cm" "ct_blood"
## [22] "fever" "chills" "cough"
## [25] "aches" "vomit" "temp"
## [28] "time_admission" "bmi" "days_onset_hosp"
# Resumen general de las variables
summary(linelist)
## case_id generation date_infection
## Length:5888 Min. : 0.00 Min. :2014-03-19 00:00:00.0
## Class :character 1st Qu.:13.00 1st Qu.:2014-09-06 00:00:00.0
## Mode :character Median :16.00 Median :2014-10-11 00:00:00.0
## Mean :16.56 Mean :2014-10-22 21:47:24.2
## 3rd Qu.:20.00 3rd Qu.:2014-12-05 00:00:00.0
## Max. :37.00 Max. :2015-04-27 00:00:00.0
## NA's :2087
## date_onset date_hospitalisation
## Min. :2014-04-07 00:00:00.00 Min. :2014-04-17 00:00:00.00
## 1st Qu.:2014-09-16 00:00:00.00 1st Qu.:2014-09-19 00:00:00.00
## Median :2014-10-23 00:00:00.00 Median :2014-10-23 00:00:00.00
## Mean :2014-11-03 01:16:26.93 Mean :2014-11-03 14:33:49.89
## 3rd Qu.:2014-12-19 00:00:00.00 3rd Qu.:2014-12-17 00:00:00.00
## Max. :2015-04-30 00:00:00.00 Max. :2015-04-30 00:00:00.00
## NA's :256
## date_outcome outcome gender
## Min. :2014-04-19 00:00:00.00 Length:5888 Length:5888
## 1st Qu.:2014-09-26 00:00:00.00 Class :character Class :character
## Median :2014-11-01 00:00:00.00 Mode :character Mode :character
## Mean :2014-11-12 21:21:48.55
## 3rd Qu.:2014-12-28 00:00:00.00
## Max. :2015-06-04 00:00:00.00
## NA's :936
## age age_unit age_years age_cat
## Min. : 0.00 Length:5888 Min. : 0.00 Length:5888
## 1st Qu.: 6.00 Class :character 1st Qu.: 6.00 Class :character
## Median :13.00 Mode :character Median :13.00 Mode :character
## Mean :16.07 Mean :16.02
## 3rd Qu.:23.00 3rd Qu.:23.00
## Max. :84.00 Max. :84.00
## NA's :86 NA's :86
## age_cat5 hospital lon lat
## Length:5888 Length:5888 Min. :-13.27 Min. :8.446
## Class :character Class :character 1st Qu.:-13.25 1st Qu.:8.461
## Mode :character Mode :character Median :-13.23 Median :8.469
## Mean :-13.23 Mean :8.470
## 3rd Qu.:-13.22 3rd Qu.:8.480
## Max. :-13.21 Max. :8.492
##
## infector source wt_kg ht_cm
## Length:5888 Length:5888 Min. :-11.00 Min. : 4
## Class :character Class :character 1st Qu.: 41.00 1st Qu.: 91
## Mode :character Mode :character Median : 54.00 Median :129
## Mean : 52.64 Mean :125
## 3rd Qu.: 66.00 3rd Qu.:159
## Max. :111.00 Max. :295
##
## ct_blood fever chills cough
## Min. :16.00 Length:5888 Length:5888 Length:5888
## 1st Qu.:20.00 Class :character Class :character Class :character
## Median :22.00 Mode :character Mode :character Mode :character
## Mean :21.21
## 3rd Qu.:22.00
## Max. :26.00
##
## aches vomit temp time_admission
## Length:5888 Length:5888 Min. :35.20 Length:5888
## Class :character Class :character 1st Qu.:38.20 Class :character
## Mode :character Mode :character Median :38.80 Mode :character
## Mean :38.56
## 3rd Qu.:39.20
## Max. :40.80
## NA's :149
## bmi days_onset_hosp
## Min. :-1200.00 Min. : 0.000
## 1st Qu.: 24.56 1st Qu.: 1.000
## Median : 32.12 Median : 1.000
## Mean : 46.89 Mean : 2.059
## 3rd Qu.: 50.01 3rd Qu.: 3.000
## Max. : 1250.00 Max. :22.000
## NA's :256
variables_resumen <- tibble(
variable = names(linelist),
tipo = sapply(linelist, class)
)
knitr::kable(
variables_resumen,
caption = "Resumen de variables en la base linelist_cleaned: nombre y tipo de dato.",
longtable = TRUE
)
| variable | tipo |
|---|---|
| case_id | character |
| generation | numeric |
| date_infection | POSIXct, POSIXt |
| date_onset | POSIXct, POSIXt |
| date_hospitalisation | POSIXct, POSIXt |
| date_outcome | POSIXct, POSIXt |
| outcome | character |
| gender | character |
| age | numeric |
| age_unit | character |
| age_years | numeric |
| age_cat | character |
| age_cat5 | character |
| hospital | character |
| lon | numeric |
| lat | numeric |
| infector | character |
| source | character |
| wt_kg | numeric |
| ht_cm | numeric |
| ct_blood | numeric |
| fever | character |
| chills | character |
| cough | character |
| aches | character |
| vomit | character |
| temp | numeric |
| time_admission | character |
| bmi | numeric |
| days_onset_hosp | numeric |
Para la descripción epidemiológica básica, es útil identificar variables clave como:
Estas variables pueden tener nombres distintos según el diseño de la base. A modo de ejemplo, asumiremos que existen los siguientes campos (ajustar según el dataset real):
age (edad en años).sex (“male”/“female” u otras codificaciones).date_onset (fecha de inicio de síntomas).date_report o date_hospitalisation.outcome (estado final del paciente).# Ejemplo de selección de variables (ajustar nombres a la base real)
vars_clave <- c("id", "age", "gender", "date_onset", "date_report", "outcome")
vars_clave[!vars_clave %in% names(linelist)]
## [1] "id" "date_report"
# Crear una versión reducida con las variables que sí existen
linelist_reducida <- linelist |>
dplyr::select(any_of(vars_clave))
head(linelist_reducida)
## # A tibble: 6 × 4
## age gender date_onset outcome
## <dbl> <chr> <dttm> <chr>
## 1 2 m 2014-05-13 00:00:00 <NA>
## 2 3 f 2014-05-13 00:00:00 Recover
## 3 56 m 2014-05-16 00:00:00 Recover
## 4 18 f 2014-05-18 00:00:00 <NA>
## 5 3 m 2014-05-21 00:00:00 Recover
## 6 16 f 2014-05-22 00:00:00 Recover
names(linelist_reducida)
## [1] "age" "gender" "date_onset" "outcome"
linelist_reducida |>
summarise(
n_casos = sum(!is.na(age)),
edad_min = min(age, na.rm = TRUE),
edad_max = max(age, na.rm = TRUE),
edad_media = mean(age, na.rm = TRUE),
edad_mediana = median(age, na.rm = TRUE)
) |>
knitr::kable(
digits = 1,
caption = "Resumen descriptivo de la edad en la base linelist."
)
| n_casos | edad_min | edad_max | edad_media | edad_mediana |
|---|---|---|---|---|
| 5802 | 0 | 84 | 16.1 | 13 |
linelist_reducida |>
ggplot(aes(x = age)) +
geom_histogram(binwidth = 5, boundary = 0, closed = "left") +
labs(
title = "Distribución de la edad en casos registrados (linelist)",
x = "Edad (años)",
y = "Frecuencia"
)
linelist_reducida |>
count(gender) |>
mutate(prop = n / sum(n)) |>
knitr::kable(
digits = 3,
caption = "Distribución de casos por sexo (frecuencia y proporción)."
)
| gender | n | prop |
|---|---|---|
| f | 2807 | 0.477 |
| m | 2803 | 0.476 |
| NA | 278 | 0.047 |
linelist_reducida |>
count(gender) |>
mutate(prop = n / sum(n)) |>
ggplot(aes(x = gender, y = prop)) +
geom_col() +
scale_y_continuous(labels = scales::percent) +
labs(
title = "Proporción de casos por sexo (linelist)",
x = "Sexo",
y = "Proporción de casos"
)
linelist_reducida |>
ggplot(aes(x = gender, y = age)) +
geom_boxplot() +
labs(
title = "Distribución de la edad por sexo",
x = "Sexo",
y = "Edad (años)"
)
Para la caracterización temporal del brote o evento, se construye una curva epidémica, agrupando los casos por fecha (o semana) de inicio de síntomas.
library(lubridate)
linelist_fechas <- linelist_reducida |>
mutate(
date_onset = as.Date(date_onset),
semana_onset = floor_date(date_onset, unit = "week", week_start = 1)
)
# Vista rápida para comprobar la transformación
linelist_fechas |>
dplyr::select(date_onset, semana_onset) |>
head()
## # A tibble: 6 × 2
## date_onset semana_onset
## <date> <date>
## 1 2014-05-13 2014-05-12
## 2 2014-05-13 2014-05-12
## 3 2014-05-16 2014-05-12
## 4 2014-05-18 2014-05-12
## 5 2014-05-21 2014-05-19
## 6 2014-05-22 2014-05-19
linelist_fechas |>
count(semana_onset) |>
ggplot(aes(x = semana_onset, y = n)) +
geom_col() +
labs(
title = "Curva epidémica semanal según fecha de inicio de síntomas",
x = "Semana de inicio de síntomas",
y = "Número de casos"
)
linelist_fechas |>
filter(!is.na(gender)) |>
count(semana_onset, gender) |>
ggplot(aes(x = semana_onset, y = n, fill = gender)) +
geom_col(position = "stack") +
labs(
title = "Curva epidémica semanal estratificada por sexo",
x = "Semana de inicio de síntomas",
y = "Número de casos",
fill = "Sexo"
)
Si la base incluye una variable de desenlace (por ejemplo,
outcome con categorías como “Died” / “Recovered”), es
posible estimar proporciones de letalidad y explorar diferencias por
edad o sexo.
if ("outcome" %in% names(linelist_reducida)) {
linelist_reducida |>
count(outcome) |>
mutate(prop = n / sum(n)) |>
knitr::kable(
digits = 3,
caption = "Distribución de desenlaces clínicos (outcome) en la base linelist."
)
}
| outcome | n | prop |
|---|---|---|
| Death | 2582 | 0.439 |
| Recover | 1983 | 0.337 |
| NA | 1323 | 0.225 |
if (all(c("outcome", "sex") %in% names(linelist_reducida))) {
linelist_reducida |>
mutate(muerto = ifelse(outcome %in% c("Died", "Dead", "Fallecido"), 1, 0)) |>
group_by(sex) |>
summarise(
n = n(),
muertes = sum(muerto, na.rm = TRUE),
letalidad = muertes / n
) |>
knitr::kable(
digits = 3,
caption = "Letalidad (proporción de fallecidos) por sexo, según variable outcome."
)
}
| 14 Desenlace clínico y letalidad (si la variable está disponible) =============================================================== |
Si la base incluye una variable de desenlace (por ejemplo,
outcome con categorías como "Death" /
"Recover"), es posible estimar proporciones de letalidad y
explorar diferencias por edad o sexo/género. |
| 14.1 Tabla de desenlaces (outcome) |
if ("outcome" %in% names(linelist_reducida)) {
linelist_reducida |>
count(outcome) |>
mutate(prop = n / sum(n)) |>
knitr::kable(
digits = 3,
caption = "Distribución de desenlaces clínicos (outcome) en la base linelist."
)
}
| outcome | n | prop |
|---|---|---|
| Death | 2582 | 0.439 |
| Recover | 1983 | 0.337 |
| NA | 1323 | 0.225 |
En la base linelist_cleaned, la variable disponible es
gender (y no sex), por lo que conviene usarla
como estrato.
if (all(c("outcome", "gender") %in% names(linelist_reducida))) {
linelist_reducida |>
mutate(muerto = ifelse(outcome %in% c("Death", "Died", "Dead", "Fallecido"), 1, 0)) |>
group_by(gender) |>
summarise(
n = n(),
muertes = sum(muerto, na.rm = TRUE),
letalidad = muertes / n
) |>
knitr::kable(
digits = 3,
caption = "Letalidad (proporción de fallecidos) por género, según la variable outcome."
)
}
| gender | n | muertes | letalidad |
|---|---|---|---|
| f | 2807 | 1227 | 0.437 |
| m | 2803 | 1228 | 0.438 |
| NA | 278 | 127 | 0.457 |
El paquete gtsummary facilita la
construcción de tablas descriptivas y de resultados de modelos, con
formato adecuado para informes y manuscritos.
if (!requireNamespace("gtsummary", quietly = TRUE)) {
install.packages("gtsummary")
}
library(gtsummary)
if (all(c("age", "gender") %in% names(linelist_reducida))) {
linelist_reducida |>
select(age, gender) |>
tbl_summary(
by = gender,
statistic = list(
all_continuous() ~ "{mean} ({sd})",
all_categorical() ~ "{n} ({p}%)"
),
missing = "no"
) |>
add_n() |>
modify_header(label ~ "Variable") |>
modify_caption("**Resumen descriptivo de edad por género (gtsummary)**")
}
| Variable | N | f N = 2,8071 |
m N = 2,8031 |
|---|---|---|---|
| age | 5,610 | 13 (10) | 20 (14) |
| 1 Mean (SD) | |||
Primero se crea una variable binaria de fallecimiento
(muerto), luego se resume su distribución por género:
if (all(c("outcome", "gender") %in% names(linelist_reducida))) {
linelist_tab_out <- linelist_reducida |>
mutate(
genero = gender,
muerto = dplyr::if_else(
outcome %in% c("Death", "Died", "Dead", "Fallecido"),
"Sí",
"No",
missing = "No"
)
)
linelist_tab_out |>
select(genero, muerto) |>
tbl_summary(
by = genero,
statistic = all_categorical() ~ "{n} ({p}%)",
missing = "no"
) |>
modify_header(label ~ "Variable") |>
modify_caption("**Distribución de fallecimiento (muerto = Sí) por género (gtsummary)**")
}
| Variable | f N = 2,8071 |
m N = 2,8031 |
|---|---|---|
| muerto | ||
| No | 1,580 (56%) | 1,575 (56%) |
| Sí | 1,227 (44%) | 1,228 (44%) |
| 1 n (%) | ||
En muchos análisis epidemiológicos resulta útil estimar la letalidad por grupos de edad, para identificar un gradiente de riesgo.
if (all(c("outcome", "age") %in% names(linelist_reducida))) {
linelist_grupos <- linelist_reducida |>
mutate(
muerto = ifelse(outcome %in% c("Death", "Died", "Dead", "Fallecido"), 1, 0),
grupo_edad = cut(
age,
breaks = c(-Inf, 4, 14, 24, 44, 64, Inf),
labels = c("0-4", "5-14", "15-24", "25-44", "45-64", "65+")
)
)
tabla_letalidad_edad <- linelist_grupos |>
group_by(grupo_edad) |>
summarise(
n = n(),
muertes = sum(muerto, na.rm = TRUE),
letalidad = muertes / n
)
knitr::kable(
tabla_letalidad_edad,
digits = 3,
caption = "Letalidad por grupos de edad en la base linelist."
)
}
| grupo_edad | n | muertes | letalidad |
|---|---|---|---|
| 0-4 | 1072 | 464 | 0.433 |
| 5-14 | 2051 | 920 | 0.449 |
| 15-24 | 1387 | 611 | 0.441 |
| 25-44 | 1098 | 477 | 0.434 |
| 45-64 | 175 | 70 | 0.400 |
| 65+ | 19 | 8 | 0.421 |
| NA | 86 | 32 | 0.372 |
if (exists("tabla_letalidad_edad")) {
tabla_letalidad_edad |>
ggplot(aes(x = grupo_edad, y = letalidad)) +
geom_col() +
scale_y_continuous(labels = scales::percent) +
labs(
title = "Letalidad por grupo de edad",
x = "Grupo de edad (años)",
y = "Letalidad (%)"
)
}
if (all(c("outcome", "age", "gender") %in% names(linelist_reducida))) {
linelist_grupos_gt <- linelist_reducida |>
mutate(
grupo_edad = cut(
age,
breaks = c(-Inf, 4, 14, 24, 44, 64, Inf),
labels = c("0-4", "5-14", "15-24", "25-44", "45-64", "65+")
),
muerto = dplyr::if_else(
outcome %in% c("Death", "Died", "Dead", "Fallecido"),
"Sí",
"No",
missing = "No"
)
)
linelist_grupos_gt |>
select(grupo_edad, gender, muerto) |>
tbl_summary(
by = grupo_edad,
statistic = all_categorical() ~ "{n} ({p}%)",
missing = "no"
) |>
modify_caption("**Distribución de fallecidos por grupo de edad y género (gtsummary)**")
}
| Characteristic | 0-4 N = 1,0721 |
5-14 N = 2,0511 |
15-24 N = 1,3871 |
25-44 N = 1,0981 |
45-64 N = 1751 |
65+ N = 191 |
|---|---|---|---|---|---|---|
| gender | ||||||
| f | 626 (60%) | 1,169 (59%) | 667 (49%) | 335 (31%) | 10 (5.9%) | 0 (0%) |
| m | 409 (40%) | 800 (41%) | 682 (51%) | 736 (69%) | 159 (94%) | 17 (100%) |
| muerto | ||||||
| No | 608 (57%) | 1,131 (55%) | 776 (56%) | 621 (57%) | 105 (60%) | 11 (58%) |
| Sí | 464 (43%) | 920 (45%) | 611 (44%) | 477 (43%) | 70 (40%) | 8 (42%) |
| 1 n (%) | ||||||
if (all(c("date_onset", "outcome") %in% names(linelist_reducida))) {
linelist_fechas_out <- linelist_reducida |>
mutate(
date_onset = as.Date(date_onset),
semana_onset = lubridate::floor_date(date_onset, unit = "week", week_start = 1)
)
linelist_fechas_out |>
filter(!is.na(outcome)) |>
count(semana_onset, outcome) |>
ggplot(aes(x = semana_onset, y = n, fill = outcome)) +
geom_col(position = "stack") +
labs(
title = "Curva epidémica semanal estratificada por desenlace (outcome)",
x = "Semana de inicio de síntomas",
y = "Número de casos",
fill = "Desenlace"
)
}
if (all(c("date_onset", "outcome", "gender") %in% names(linelist_reducida))) {
linelist_fechas_go <- linelist_reducida |>
mutate(
date_onset = as.Date(date_onset),
semana_onset = lubridate::floor_date(date_onset, unit = "week", week_start = 1)
)
linelist_fechas_go |>
filter(!is.na(outcome), !is.na(gender)) |>
count(semana_onset, gender, outcome) |>
ggplot(aes(x = semana_onset, y = n, fill = outcome)) +
geom_col(position = "stack") +
facet_wrap(~ gender) +
labs(
title = "Curva epidémica semanal por género y desenlace",
x = "Semana de inicio de síntomas",
y = "Número de casos",
fill = "Desenlace"
)
}
Como ejemplo introductorio de análisis multivariable, se puede ajustar un modelo de regresión logística para explorar la asociación entre edad, género y probabilidad de fallecer. Aquí se muestra tanto el resumen clásico como su presentación con gtsummary.
if (all(c("outcome", "age", "gender") %in% names(linelist_reducida))) {
linelist_modelo <- linelist_reducida |>
mutate(
muerto = ifelse(outcome %in% c("Death", "Died", "Dead", "Fallecido"), 1, 0)
) |>
filter(!is.na(muerto), !is.na(age), !is.na(gender))
if (!requireNamespace("broom", quietly = TRUE)) {
install.packages("broom")
}
library(broom)
modelo_logit <- glm(
muerto ~ age + gender,
data = linelist_modelo,
family = binomial(link = "logit")
)
summary(modelo_logit)
}
##
## Call:
## glm(formula = muerto ~ age + gender, family = binomial(link = "logit"),
## data = linelist_modelo)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -0.228825 0.047277 -4.840 1.3e-06 ***
## age -0.001899 0.002220 -0.856 0.392
## genderm 0.017081 0.055954 0.305 0.760
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 7689.5 on 5609 degrees of freedom
## Residual deviance: 7688.8 on 5607 degrees of freedom
## AIC: 7694.8
##
## Number of Fisher Scoring iterations: 3
if (exists("modelo_logit")) {
tbl_regression(
modelo_logit,
exponentiate = TRUE
) |>
modify_header(label ~ "Variable") |>
modify_caption("**Modelo de regresión logística para mortalidad, ajustado por edad y género (OR e IC 95%)**")
}
| Variable | OR1 | 95% CI1 | p-value |
|---|---|---|---|
| age | 1.00 | 0.99, 1.00 | 0.4 |
| gender | |||
| f | — | — | |
| m | 1.02 | 0.91, 1.14 | 0.8 |
| 1 OR = Odds Ratio, CI = Confidence Interval | |||
fever, o estado nutricional aproximado
con bmi) dentro del modelo logístico.hospital) o por
categoría de edad (age_cat, age_cat5).Estos ejercicios permiten consolidar el vínculo entre exploración descriptiva, visualización epidemiológica y modelos estadísticos dentro de un flujo reproducible en R Markdown.