En cada ejercicio (excepto el que usa base de datos), se presenta primero la solución matemática paso a paso, como se haría en un examen o pizarra, y después la implementación en R, para reforzar el razonamiento epidemiológico antes del uso del software.
En este taller aprenderemos a calcular e interpretar las tres medidas fundamentales de la epidemiología:
El enfoque del taller es doble:
Antes de empezar, cargamos las “herramientas” necesarias. Asegúrese
de tener conexión a internet si es la primera vez que ejecuta estos
comandos para descargar las librerías tidyverse,
lubridate y epiR.
# --- PASO 0: INSTALACIÓN Y CARGA DE PAQUETES ---
# 1. Si no tiene los paquetes, R los instalará automáticamente con este código:
if(!require(tidyverse)) install.packages("tidyverse")
## Cargando paquete requerido: tidyverse
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.2
## ✔ ggplot2 4.0.0 ✔ tibble 3.3.0
## ✔ lubridate 1.9.4 ✔ tidyr 1.3.1
## ✔ purrr 1.1.0
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
if(!require(lubridate)) install.packages("lubridate")
if(!require(epiR)) install.packages("epiR")
## Cargando paquete requerido: epiR
## Warning: package 'epiR' was built under R version 4.5.2
## Cargando paquete requerido: survival
## Package epiR 2.0.89 is loaded
## Type help(epi.about) for summary information
## Type browseVignettes(package = 'epiR') to learn how to use epiR for applied epidemiological analyses
if(!require(epitools)) install.packages("epitools", repos = "[http://cran.us.r-project.org](http://cran.us.r-project.org)")
## Cargando paquete requerido: epitools
## Warning: package 'epitools' was built under R version 4.5.2
##
## Adjuntando el paquete: 'epitools'
##
## The following object is masked from 'package:survival':
##
## ratetable
library(epitools)
# 2. Cargar las librerías en la memoria
library(tidyverse) # Para manipulación de datos
library(lubridate) # Para manejar fechas
library(epiR) # Calculadora epidemiológica profesional
library(epitools)
print("✅ Entorno listo. Comencemos.")
## [1] "✅ Entorno listo. Comencemos."
Concepto: Es la proporción de la población que tiene el evento en un momento (Punto) o periodo específico. No mide riesgo, mide la carga de enfermedad en la comunidad.
La prevalencia puntual se define como:
\[ P = \frac{C}{N} \]
Donde:
Imaginemos que realizamos una encuesta rápida a 20 pacientes en la sala de espera, de los cuales 5 resultaron ser diabéticos.
0 = Sano1 = Enfermo (Caso prevalente)Calcularemos la prevalencia puntual y su intervalo de confianza al 95%.
Antes de programar, calculamos los indicadores básicos para entender la carga de la enfermedad.
La fórmula de la prevalencia es: \[P = \frac{Casos \ existentes (C)}{Población \ total (N)}\]
Sustituyendo los datos (\(C=5\), \(N=20\)): \[P = \frac{5}{20} = 0.25 \implies 25\%\]
Utilizamos el método de Wald para la aproximación manual:
Error Estándar (\(EE\)): \[EE = \sqrt{\frac{P \times (1 - P)}{n}} = \sqrt{\frac{0.25 \times 0.75}{20}} \approx 0.0968\]
Límites del Intervalo (\(IC_{95\%}\)): \[IC = P \pm (1.96 \times EE)\] \[IC = 0.25 \pm (1.96 \times 0.0968) = 0.25 \pm 0.1897\] Resultado Manual: \(IC_{95\%} [0.0603 - 0.4397]\) o (6.0% - 44.0%).
Una vez comprendida la base matemática, procedemos a la automatización. En epidemiología, no solo buscamos el dato puntual, sino la precisión del mismo expresada en sus intervalos de confianza. Note que R utiliza métodos exactos (como Wilson o Clopper-Pearson) que son más precisos que el cálculo manual en muestras pequeñas.
# --- 1. PREPARACIÓN ---
library(tidyverse)
library(epiR)
# --- 2. CARGA DE DATOS ---
datos_diabetes <- tibble(
id = 1:20,
estado = c(0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0)
)
# --- 3. PROCESAMIENTO ---
# Calculamos numerador (casos) y denominador (total)
num_casos <- sum(datos_diabetes$estado)
total_pacientes <- nrow(datos_diabetes)
# --- 4. CÁLCULO DE PREVALENCIA E IC 95% ---
# Para epi.conf, necesitamos una matriz con [casos, total]
matriz_datos <- matrix(c(num_casos, total_pacientes), nrow = 1, ncol = 2)
# Calculamos usando el método de proporción simple
# Por defecto, epi.conf utiliza el método de Wilson, ideal para muestras pequeñas
resultados <- epi.conf(matriz_datos, ctype = "prop.single")
# --- 5. RESULTADOS ---
print(paste("Casos encontrados:", num_casos))
## [1] "Casos encontrados: 5"
print(paste("Prevalencia:", resultados$est * 100, "%"))
## [1] "Prevalencia: 20 %"
print(paste("IC 95% (Inferior):", round(resultados$lower * 100, 2), "%"))
## [1] "IC 95% (Inferior): 8.86 %"
print(paste("IC 95% (Superior):", round(resultados$upper * 100, 2), "%"))
## [1] "IC 95% (Superior): 39.13 %"
Concepto de Riesgo: La probabilidad de que ocurra un evento en un periodo determinado. El denominador es el total de personas en riesgo al inicio. Este cálculo asume que la población es fija (nadie entra, nadie sale y todos son seguidos por el mismo tiempo). Concepto de Odds: Es la razón entre la probabilidad de que el evento ocurra y la probabilidad de que no ocurra. El denominador son las personas que no tuvieron el evento.
Escenario: Evaluamos los datos de una cohorte cerrada de 11,034 médicos que recibieron aspirina para prevenir infarto de miocardio (IAM). Tras el seguimiento, 139 desarrollaron un IAM, mientras que 10,895 permanecieron sanos.
Datos del estudio: * Casos nuevos de IAM (\(a\)): 139 * Sujetos sanos al final del estudio (\(b\)): 10,895 * Población total en riesgo (\(n\)): 11,034 * Valor crítico \(Z_{0.95}\): 1.96
La IA es una proporción. Su intervalo de confianza se calcula mediante la aproximación normal.
\[IA = \frac{a}{n} = \frac{139}{11,034} = 0.01259 \implies 1.26\%\]
\[EE(IA) = \sqrt{\frac{IA \times (1 - IA)}{n}} = \sqrt{\frac{0.01259 \times 0.98741}{11,034}} \approx 0.00106\]
\[IC = IA \pm (1.96 \times EE)\] \[IC = 0.01259 \pm (1.96 \times 0.00106) = 0.01259 \pm 0.00207\] Resultado: \(IC_{95\%} [1.05\% - 1.46\%]\)
El Odds no es una proporción. Para su IC, debemos trabajar en escala logarítmica (\(\ln\)) para garantizar que los límites sean siempre positivos.
\[Odds = \frac{a}{b} = \frac{139}{10,895} = 0.01275\]
\[EE(\ln Odds) = \sqrt{\frac{1}{a} + \frac{1}{b}} = \sqrt{\frac{1}{139} + \frac{1}{10,895}} \approx 0.0853\]
Utilizamos la función exponencial (\(e^x\)): * Límite Inferior: \(e^{-4.529} = 0.0107\) * Límite Superior: \(e^{-4.195} = 0.0150\)
Resultado: \(IC_{95\%} [0.0107 - 0.0150]\)
| Medida | Denominador | Interpretación |
|---|---|---|
| Riesgo (IA) | Todos (Sanos + Enfermos) | Probabilidad individual de enfermar. |
| Odds | Solo los Sanos | Ventaja o posibilidad de enfermar vs. no enfermar. |
Nota: Observe que el límite superior del riesgo (1.46%) es menor que el del odds (1.50%). Esto ocurre porque el riesgo siempre está “contenido” por el total de la población, mientras que el odds puede crecer teóricamente hasta el infinito.
Utilizaremos R para calcular ambos parámetros y sus intervalos de confianza, lo que nos permitirá ver qué tan similares son cuando el evento es “raro” (baja prevalencia/incidencia).
# 1. DATOS DE ENTRADA
casos_iam <- 139
total_medicos <- 11034
sanos_iam <- total_medicos - casos_iam
# 2. CÁLCULOS CON epiR
# Riesgo (Proporción): usamos casos y TOTAL
mat_riesgo <- matrix(c(casos_iam, total_medicos), nrow = 1, ncol = 2)
res_riesgo <- epi.conf(mat_riesgo, ctype = "prop.single")
# Odds (Razón): usamos casos y SANOS
mat_odds <- matrix(c(casos_iam, sanos_iam), nrow = 1, ncol = 2)
res_odds <- epi.conf(mat_odds, ctype = "odds")
# 3. IMPRESIÓN DE RESULTADOS (Nombres corregidos)
cat("--- COMPARATIVA MATEMÁTICA ---\n")
## --- COMPARATIVA MATEMÁTICA ---
# Imprimimos Riesgo
cat("Incidencia Acumulada (Riesgo):", round(res_riesgo$est * 100, 3), "%\n")
## Incidencia Acumulada (Riesgo): 1.244 %
cat("IC 95% Riesgo: [", round(res_riesgo$lower * 100, 3), "% - ",
round(res_riesgo$upper * 100, 3), "% ]\n\n")
## IC 95% Riesgo: [ 1.055 % - 1.467 % ]
# Imprimimos Odds (Cambiado 'odds_val' por 'res_odds$est')
cat("Odds (Posibilidad):", round(res_odds$est, 5), "\n")
## Odds (Posibilidad): 0.01276
cat("IC 95% Odds: [", round(res_odds$lower, 5), "-",
round(res_odds$upper, 5), "]\n\n")
## IC 95% Odds: [ 0.01072 - 0.0149 ]
cat("Nota: Observe que cuando el evento es raro (<10%), el Riesgo y el Odds son casi idénticos.")
## Nota: Observe que cuando el evento es raro (<10%), el Riesgo y el Odds son casi idénticos.
# --- REFLEXIÓN PEDAGÓGICA ---
# Note que cuando el evento es raro (frecuencia < 10%), el Riesgo y el Odds
# son numéricamente muy similares. Sin embargo, conceptualmente son distintos:
# El riesgo divide entre 'todos', el odds divide entre 'los que no enfermaron'.
Concepto: La velocidad con la que aparecen nuevos casos. Aquí el denominador ya no es “personas”, sino Tiempo-Persona. Es la medida vital cuando hay pérdidas de seguimiento (datos censurados) o tiempos de ingreso variables.
Trabajaremos con una cohorte simulada de 50 pacientes post-infarto.
Cálculo de Tasa Calcularemos los años-persona exactos restando la fecha de ingreso de la fecha de último control.
# --- EJERCICIO 3: CÁLCULO DE TASA DE INCIDENCIA ---
# 1. Cargar los datos
# Nota: Asegúrate de que el archivo csv esté en la carpeta usada como directorio de trabajo (Working Directory).
datos_cardio <- read_csv("cohorte_cardiologia_rosales.csv")
## Rows: 50 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## dbl (2): id, evento_infarto
## date (2): fecha_ingreso, fecha_ultimo_control
##
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# 2. Paso Crítico: Construir la variable Tiempo (Años-Persona)
# Usamos lubridate para restar fechas y convertir a años
datos_cardio <- datos_cardio %>%
mutate(
dias_riesgo = as.numeric(fecha_ultimo_control - fecha_ingreso),
anios_riesgo = dias_riesgo / 365.25
)
# Veamos los primeros 5 pacientes para entender qué hicimos
print(head(datos_cardio))
## # A tibble: 6 × 6
## id fecha_ingreso fecha_ultimo_control evento_infarto dias_riesgo
## <dbl> <date> <date> <dbl> <dbl>
## 1 1 2021-07-31 2021-12-20 0 142
## 2 2 2021-07-02 2022-11-06 1 492
## 3 3 2021-07-10 2023-07-26 0 746
## 4 4 2021-11-30 2024-01-04 0 765
## 5 5 2020-09-11 2022-02-05 0 512
## 6 6 2021-09-12 2023-07-08 0 664
## # ℹ 1 more variable: anios_riesgo <dbl>
# 3. Sumar el Numerador (Eventos) y el Denominador (Tiempo)
total_eventos <- sum(datos_cardio$evento_infarto)
total_tiempo_anios <- sum(datos_cardio$anios_riesgo)
total_pacientes <- nrow(datos_cardio)
print(paste("Total Eventos (Numerador):", total_eventos))
## [1] "Total Eventos (Numerador): 14"
print(paste("Total Años-Persona (Denominador):", round(total_tiempo_anios, 2)))
## [1] "Total Años-Persona (Denominador): 65.26"
# 4. Cálculo de la Tasa de Incidencia (TI)
# Fórmula: Eventos / Tiempo-Persona Total
tasa_incidencia <- total_eventos / total_tiempo_anios
print(paste("Tasa de Incidencia:", round(tasa_incidencia, 4), "eventos por año-persona"))
## [1] "Tasa de Incidencia: 0.2145 eventos por año-persona"
print(paste("Interpretación: Ocurren", round(tasa_incidencia * 100, 1),
"infartos por cada 100 años-persona de seguimiento."))
## [1] "Interpretación: Ocurren 21.5 infartos por cada 100 años-persona de seguimiento."
# 5. COMPARACIÓN: ¿Qué pasaría si usamos la fórmula incorrecta (Riesgo)?
# (El error ingenuo de dividir entre el N inicial)
riesgo_ingenuo <- total_eventos / total_pacientes
print("--- MOMENTO DE REFLEXIÓN ---")
## [1] "--- MOMENTO DE REFLEXIÓN ---"
print(paste("Cálculo Incorrecto (Riesgo Ingenuo):", riesgo_ingenuo * 100, "%"))
## [1] "Cálculo Incorrecto (Riesgo Ingenuo): 28 %"
print(paste("Cálculo Correcto (Tasa x 100 años-persona):", round(tasa_incidencia * 100, 1)))
## [1] "Cálculo Correcto (Tasa x 100 años-persona): 21.5"
# Note la diferencia. En cohortes dinámicas, el riesgo ingenuo suele fallar.
IMPORTANTE: No es que esperemos 100 años para ver los 25 infartos. Es que si juntamos el tiempo de todos los pacientes, al acumular 100 años-persona, esperaríamos ver esa cantidad de eventos.
Concepto: Imagine un lavabo (o una bañera).
Nota: La imagen superior ilustra cómo la duración de la enfermedad actúa como un factor multiplicador de la carga de enfermedad en el hospital.
La Fórmula de Oro: En una población estable y para enfermedades con prevalencia baja, la relación matemática es:
\[
Prevalencia \approx Incidencia \times Duración
\]
—
En este ejercicio aplicaremos la Fórmula de Oro de la epidemiología para entender por qué la carga hospitalaria no siempre depende de qué tan “contagiosa” es una enfermedad, sino de cuánto tiempo permanecen los pacientes enfermos.
En una población estable, la relación entre Prevalencia (\(P\)), Incidencia (\(I\)) y Duración (\(D\)) es:
\[P \approx I \times D\]
La gripe se propaga rápidamente, pero el cuerpo la elimina en pocos días.
Cálculo de Prevalencia: \[P_{gripe} = 0.20 \times \left(\frac{1}{52}\right) = 0.0038\] Resultado: \(0.38\%\) de prevalencia puntual.
La diabetes tiene pocos casos nuevos comparada con la gripe, pero es una condición de por vida.
Cálculo de Prevalencia: \[P_{diabetes} = 0.01 \times 25 = 0.25\] Resultado: \(25\%\) de prevalencia puntual.
A pesar de que la Gripe es 20 veces más frecuente en términos de casos nuevos (incidencia), la Diabetes genera una carga hospitalaria 65 veces mayor en un momento dado.
Moraleja para el epidemiólogo: 1. Si quieres medir el éxito de un tratamiento que prolonga la vida (aumenta \(D\)), verás que la Prevalencia sube. ¡Eso es una buena noticia clínica aunque el número parezca mayor! 2. Si quieres medir el éxito de una vacuna (baja \(I\)), verás que tanto la incidencia como la prevalencia bajan.
# --- EJERCICIO 4: SIMULACIÓN P = I x D ---
# 1. Definimos las variables
# Incidencia anual (casos por persona-año)
incidencia_gripe <- 0.20 # 20% de riesgo anual (Muy alto)
incidencia_diabetes <- 0.01 # 1% de riesgo anual (Bajo)
# Duración de la enfermedad (en años)
# Gripe: dura 1 semana (1/52 de un año)
duracion_gripe <- 1 / 52
# Diabetes: dura 25 años promedio
duracion_diabetes <- 25
# 2. Calculamos la Prevalencia estimada (P = I * D)
prevalencia_gripe <- incidencia_gripe * duracion_gripe
prevalencia_diabetes <- incidencia_diabetes * duracion_diabetes
# 3. Resultados
print(paste("Prevalencia Gripe:", round(prevalencia_gripe * 100, 2), "%"))
## [1] "Prevalencia Gripe: 0.38 %"
print(paste("Prevalencia Diabetes:", round(prevalencia_diabetes * 100, 2), "%"))
## [1] "Prevalencia Diabetes: 25 %"
# --- REFLEXIÓN CLÍNICA ---
# Observe los resultados:
# La Gripe tiene una incidencia 20 VECES MAYOR que la Diabetes.
# Sin embargo, la Diabetes tiene una carga (Prevalencia)
# ~65 VECES MAYOR que la Gripe.
# 4. Escenario: ¿Qué pasa si mejoramos el tratamiento de la Diabetes?
# El nuevo medicamento NO cura, pero evita que el paciente muera rápido.
# Esto aumenta la Duración (D).
nueva_duracion <- 35 # Ahora viven 35 años con la enfermedad (antes 25)
nueva_prevalencia <- incidencia_diabetes * nueva_duracion
print(paste("Prevalencia tras 'mejorar' el tratamiento:",
round(nueva_prevalencia * 100, 2), "%"))
## [1] "Prevalencia tras 'mejorar' el tratamiento: 35 %"
# Conclusión automática
if(nueva_prevalencia > prevalencia_diabetes) {
print("CONCLUSIÓN PARADÓJICA: ¡El éxito médico aumentó la prevalencia!")
print("Recordatorio: No confunda aumento de prevalencia con fracaso del programa de prevención.")
}
## [1] "CONCLUSIÓN PARADÓJICA: ¡El éxito médico aumentó la prevalencia!"
## [1] "Recordatorio: No confunda aumento de prevalencia con fracaso del programa de prevención."
| Medida | Pregunta que responde | Denominador (Fondo de la fracción) | Unidad de Medida | Característica Clave |
|---|---|---|---|---|
| Prevalencia | ¿Qué tan frecuente es la enfermedad hoy? | Población total (\(N\)) | Porcentaje o proporción | Es una fotografía del momento actual. |
| Incidencia Acumulada | ¿Qué riesgo tiene una persona de enfermar? | Población sana al inicio | Proporción (0 a 1) | Mide la probabilidad en una cohorte cerrada. |
| Tasa de Incidencia | ¿Qué tan rápido aparecen los casos? | Tiempo-Persona total (\(\sum tiempo\)) | Casos por unidad de tiempo | Mide la velocidad de aparición. |
| Odds | ¿Cuál es la posibilidad a favor vs. en contra? | Sujetos que NO tuvieron el evento | Razón (Ratio) | Es la base para el Odds Ratio y la Regresión Logística. |
| P \(\approx\) I \(\times\) D | ¿Cómo se relacionan? | N/A | Variable | Útil para estimar la carga hospitalaria. |
Guarde su script como Practica_Semana3_NombreApellido.R