Contexto del problema

La enfermedad cardíaca, en particular la enfermedad coronaria y el infarto, es una de las principales causas de muerte en el mundo y una de las que más recursos consume en los servicios de salud: consultas especializadas, hospitalización, unidades de cuidado intensivo y procedimientos de hemodinamia. Para una institución de salud, saber en qué grupos se concentra la enfermedad y cuánta demanda puede generar es clave para planear programas de prevención y dimensionar su capacidad.

Audiencia

Directivos y equipos de planeación de instituciones prestadoras de servicios de salud (IPS), aseguradores (EPS) y responsables de programas de prevención cardiovascular.

Objetivo analítico

Aplicar herramientas de probabilidad y estadística (descriptiva e inferencial) en R para: (1) identificar los grupos con mayor probabilidad de enfermedad cardíaca, (2) simular escenarios de demanda de servicios y (3) evaluar si las diferencias observadas entre grupos son estadísticamente significativas y, sobre todo, si son relevantes para la toma de decisiones.

Referente metodológico

Como punto de partida se revisó el análisis estadístico de referencia propuesto en la actividad (Explorando medicaldata en R, RPubs). De él se retomó la secuencia de trabajo: carga y revisión de los datos, verificación de valores faltantes, estadística descriptiva y visualización. Se evitaron prácticas como attach(), que dificultan la reproducibilidad del análisis.

Los datos

Fuente: base Personal Key Indicators of Heart Disease (Kaggle), construida a partir de la encuesta telefónica BRFSS 2020 de los Centros para el Control y la Prevención de Enfermedades (CDC) de EE. UU. Cada fila corresponde a un adulto encuestado.

La base contiene 319.795 personas encuestadas y 18 variables. La variable principal es HeartDisease: si la persona reporta antecedente de enfermedad coronaria o infarto. Las demás son cuatro variables numéricas (IMC, días de mala salud física y mental en el último mes y horas de sueño) y trece categóricas (tabaquismo, consumo excesivo de alcohol, ACV, dificultad para caminar, sexo, grupo de edad, raza, diabetes, actividad física, salud general percibida, asma, enfermedad renal y cáncer de piel).

count(corazon, HeartDisease)
## # A tibble: 2 × 2
##   HeartDisease      n
##   <chr>         <int>
## 1 No           292422
## 2 Yes           27373

Calidad de los datos.

# Antes de analizar se revisa la calidad: valores faltantes y filas idénticas
c(valores_faltantes = sum(is.na(corazon)),
  filas_repetidas   = sum(duplicated(corazon)))
## valores_faltantes   filas_repetidas 
##                 0             18078

La base no tiene valores faltantes. Hay 18.078 filas idénticas (5,7 %); como la base no incluye un identificador de persona y la mayoría de las preguntas son de respuesta sí/no, es esperable que personas distintas respondan exactamente igual, por lo que se conservaron. Las horas de sueño (de 1 a 24) y el IMC (de 12 a 94,9) presentan algunos valores extremos poco plausibles; se conservaron, pero deben tenerse en cuenta al interpretar.

Probabilidad aplicada

Selección aleatoria y espacio muestral

Situación de gestión: un programa de tamizaje cardiovascular recibe 100 pacientes. Cada uno puede tener o no antecedente de enfermedad cardíaca (espacio muestral: {Sí, No}). Con la probabilidad observada en la encuesta, simulamos cuántos casos llegarían.

# La semilla hace reproducible el azar: quien ejecute el informe
# obtiene exactamente los mismos resultados
set.seed(2026)

espacio_muestral <- c("Sí", "No")

# Probabilidad real de la base: proporción de respuestas "Yes"
p_cardiaca <- mean(corazon$HeartDisease == "Yes")

tamizaje <- sample(
  espacio_muestral,
  size = 100,
  replace = TRUE,                         # cada paciente es un sorteo nuevo
  prob = c(p_cardiaca, 1 - p_cardiaca)    # mismo orden que espacio_muestral
)

table(tamizaje)
## tamizaje
## No Sí 
## 91  9

Muestra aleatoria de la base real: para auditar la información sin revisar los 319.795 registros, se seleccionan al azar 1.000 encuestados.

set.seed(2026)

# replace = FALSE (el valor por defecto): nadie puede ser elegido dos veces
filas_elegidas <- sample(nrow(corazon), size = 1000)
muestra <- corazon |> slice(filas_elegidas)

# Si las dos proporciones se parecen, la muestra representa bien a la base
c(base_completa = p_cardiaca,
  muestra_1000  = mean(muestra$HeartDisease == "Yes"))
## base_completa  muestra_1000 
##    0.08559546    0.09200000

Interpretación

Tamizaje simulado. Con la probabilidad observada en la encuesta (8,6 %), se simuló la llegada de 100 pacientes a un programa de tamizaje cardiovascular. En esta simulación, 9 pacientes presentaron antecedente de enfermedad coronaria o infarto y 91 no, un resultado cercano a lo esperado (8,6 por cada 100). El número exacto cambia en cada simulación por efecto del azar; por eso, al planear la capacidad del programa (por ejemplo, cupos de valoración por cardiología), conviene trabajar con un rango y no con un único número.

Muestra de auditoría. En la muestra aleatoria de 1.000 encuestados, la proporción con enfermedad cardíaca fue 9,2 %, frente a 8,6 % en la base completa. La diferencia de 0,6 puntos porcentuales es pequeña y se explica por la variación natural del muestreo. Una muestra aleatoria bien seleccionada reproduce de forma aproximada lo que ocurre en toda la población encuestada, lo que permite a la gerencia auditar o estimar indicadores con menos tiempo y costo, sin revisar todos los registros.

Probabilidades empíricas con datos reales

Situación de gestión: para priorizar programas de prevención, la gerencia necesita saber en qué grupos se concentra la enfermedad cardíaca. Se calcula la probabilidad general y la probabilidad condicional según tabaquismo, antecedente de ACV y edad.

# Probabilidad simple: casos / total en toda la base
frecuencias <- table(corazon$HeartDisease)
frecuencias
## 
##     No    Yes 
## 292422  27373
prop.table(frecuencias)
## 
##         No        Yes 
## 0.91440454 0.08559546
# margin = 1 calcula el % dentro de cada FILA, es decir, dentro de cada grupo:
# responde "entre los que fuman, ¿qué % tiene enfermedad cardíaca?"
tabla_tabaco <- table(Fuma = corazon$Smoking, Cardiaca = corazon$HeartDisease)
round(prop.table(tabla_tabaco, margin = 1) * 100, 1)
##      Cardiaca
## Fuma    No  Yes
##   No  94.0  6.0
##   Yes 87.8 12.2
tabla_acv <- table(ACV = corazon$Stroke, Cardiaca = corazon$HeartDisease)
round(prop.table(tabla_acv, margin = 1) * 100, 1)
##      Cardiaca
## ACV     No  Yes
##   No  92.5  7.5
##   Yes 63.6 36.4
tabla_edad <- table(Edad = corazon$AgeCategory, Cardiaca = corazon$HeartDisease)
round(prop.table(tabla_edad, margin = 1) * 100, 1)
##              Cardiaca
## Edad            No  Yes
##   18-24       99.4  0.6
##   25-29       99.2  0.8
##   30-34       98.8  1.2
##   35-39       98.6  1.4
##   40-44       97.7  2.3
##   45-49       96.6  3.4
##   50-54       94.6  5.4
##   55-59       92.6  7.4
##   60-64       90.1  9.9
##   65-69       88.0 12.0
##   70-74       84.4 15.6
##   75-79       81.2 18.8
##   80 or older 77.4 22.6
# Riesgo alto no siempre significa muchos casos: se compara el peso de cada
# grupo en la población encuestada y en el total de casos
mayores_65 <- c("65-69", "70-74", "75-79", "80 or older")
casos <- corazon |> filter(HeartDisease == "Yes")

c(
  poblacion_65_mas = mean(corazon$AgeCategory %in% mayores_65),
  casos_65_mas     = mean(casos$AgeCategory %in% mayores_65),
  casos_con_acv    = mean(casos$Stroke == "Yes")
)
## poblacion_65_mas     casos_65_mas    casos_con_acv 
##        0.3466314        0.6738757        0.1603405

Interpretación

Probabilidad general. De los 319.795 adultos encuestados, 27.373 reportan antecedente de enfermedad coronaria o infarto: una probabilidad empírica de 0,086, es decir, 8,6 de cada 100 personas.

Probabilidades condicionales. La probabilidad cambia de forma importante según el grupo. Entre los fumadores, el 12,2 % tiene enfermedad cardíaca, frente al 6,0 % de los no fumadores: el doble (riesgo relativo ≈ 2,0). Entre quienes tuvieron un ACV, el 36,4 % tiene enfermedad cardíaca, frente al 7,5 % de quienes no: casi 5 veces más. Con la edad, la probabilidad aumenta de forma sostenida y se acelera después de los 50 años: pasa de 0,6 % (18-24 años) a 22,6 % (80 años o más). Como en cada grupo la probabilidad es distinta del 8,6 % general, estos eventos son dependientes: saber si una persona fuma, tuvo un ACV o es mayor cambia la probabilidad de que tenga enfermedad cardíaca.

Implicación para la gestión. Con presupuesto para un solo programa de prevención, conviene dirigirlo a los adultos de 65 años o más: representan el 34,7 % de los encuestados, pero concentran el 67,4 % de los casos. Las personas con antecedente de ACV tienen el riesgo más alto (36,4 %), pero aportan solo el 16 % de los casos; por eso encajan mejor en un seguimiento específico, articulado con la atención del ACV.

Límite. Los datos provienen de una encuesta transversal (todo se preguntó en el mismo momento) y son autorreportados: muestran asociación, no causalidad. Además, estos factores pueden compartir causas no medidas en la base, como la hipertensión arterial.

Distribuciones discretas: escenarios para planear capacidad

Situación de gestión: la gerencia necesita dimensionar tres servicios: (1) un programa de prevención dirigido a mayores de 80 años, (2) las jornadas de tamizaje cardiovascular de 100 pacientes y (3) las camas de urgencias para pacientes con infarto. Se simulan escenarios con las probabilidades observadas en la base.

set.seed(2026)

# Probabilidad en mayores de 80 años, tomada de la tabla por edad del punto 3
p_mayores_80 <- 0.226

# Un número al azar entre 0 y 1 cae por debajo de 0,226 el 22,6 % de las veces:
# así se simula si cada paciente tiene o no enfermedad cardíaca
aleatorios <- runif(100)
casos_mayores_80 <- sum(aleatorios < p_mayores_80)

c(programa_general    = sum(tamizaje == "Sí"),
  programa_mayores_80 = casos_mayores_80)
##    programa_general programa_mayores_80 
##                   9                  24
set.seed(2026)

# rbinom() repite el tamizaje de 100 pacientes en 1.000 jornadas
# y cuenta los casos de cada jornada
casos_por_jornada <- rbinom(n = 1000, size = 100, prob = p_cardiaca)

summary(casos_por_jornada)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   7.000   8.000   8.504  10.000  18.000
# Rango que cubre el 90 % central de las jornadas: base para planear cupos
quantile(casos_por_jornada, probs = c(0.05, 0.95))
##  5% 95% 
##   4  13
# ¿En qué % de jornadas no alcanzarían 9 cupos (el promedio)?
mean(casos_por_jornada > 9)
## [1] 0.353
grafico_jornadas <- tibble(casos = casos_por_jornada) |>
  ggplot(aes(x = casos)) +
  geom_bar(fill = "#3B528B") +
  labs(
    title = "Casos por jornada en 1.000 jornadas simuladas de tamizaje",
    x = "Pacientes con enfermedad cardíaca (de 100 atendidos)",
    y = "Número de jornadas"
  ) +
  theme_minimal()

# ggplotly() vuelve interactivo el gráfico: al pasar el cursor se ven los valores
ggplotly(grafico_jornadas)
# SUPUESTO del ejemplo (no viene de la base): una unidad de urgencias
# recibe en promedio 3 pacientes con infarto por día
lambda_infartos <- 3

prob_ingresos <- tibble(
  ingresos = 0:10,
  probabilidad = dpois(ingresos, lambda = lambda_infartos)
)
prob_ingresos
## # A tibble: 11 × 2
##    ingresos probabilidad
##       <int>        <dbl>
##  1        0     0.0498  
##  2        1     0.149   
##  3        2     0.224   
##  4        3     0.224   
##  5        4     0.168   
##  6        5     0.101   
##  7        6     0.0504  
##  8        7     0.0216  
##  9        8     0.00810 
## 10        9     0.00270 
## 11       10     0.000810
# Con 5 camas para ingresos nuevos, ¿qué probabilidad hay de que no alcancen?
1 - sum(dpois(0:5, lambda = lambda_infartos))
## [1] 0.08391794
# ¿Y si se agrega una sexta cama?
1 - sum(dpois(0:6, lambda = lambda_infartos))
## [1] 0.03350854
grafico_ingresos <- prob_ingresos |>
  ggplot(aes(x = ingresos, y = probabilidad)) +
  geom_col(fill = "#3B528B") +
  labs(
    title = "Probabilidad de ingresos diarios por infarto (promedio supuesto: 3 por día)",
    x = "Pacientes con infarto que ingresan en un día",
    y = "Probabilidad"
  ) +
  theme_minimal()

ggplotly(grafico_ingresos)

Interpretación

Programa para mayores de 80 años (runif()). Con la probabilidad observada en ese grupo (22,6 %), de 100 pacientes inscritos 24 presentaron enfermedad cardíaca, frente a 9 en el programa general: casi 3 veces más casos con el mismo número de inscritos. Un programa dirigido a este grupo requiere más horas de cardiología, seguimiento y apoyo diagnóstico por cada 100 pacientes.

Jornadas de tamizaje (rbinom()). Al simular 1.000 jornadas de 100 pacientes, el promedio fue de 8,5 casos por jornada, pero el número varió entre 1 y 18. En 9 de cada 10 jornadas hubo entre 4 y 13 casos. Si se reservaran solo 9 cupos de valoración cardiológica (el promedio), no alcanzarían en el 35,3 % de las jornadas (más de 1 de cada 3); reservar 13 cupos cubre cerca del 95 % de ellas. Planear con el promedio subestima la demanda en muchas jornadas.

Camas de urgencias (dpois()). Bajo el supuesto de un promedio de 3 ingresos por infarto al día, lo más probable es recibir 2 o 3 pacientes (22,4 % cada uno). Con 5 camas, la demanda las supera el 8,4 % de los días, unos 31 días al año; con una sexta cama, ese riesgo baja a 3,4 % (unos 12 días al año). Esto permite discutir con datos si el costo de una cama adicional se justifica frente a los días de saturación que evita.

Límite. Son simulaciones y dependen de sus supuestos. Las probabilidades provienen de una encuesta autorreportada de EE. UU., y el promedio de 3 infartos diarios es un valor supuesto, no observado. Además, la distribución de Poisson supone que los ingresos llegan de forma independiente y a un ritmo constante, algo que en urgencias puede no cumplirse (por ejemplo, por picos estacionales). Antes de decidir, estos escenarios deben recalcularse con los datos reales de la institución.

Distribuciones continuas: IMC, tiempos de llegada y duración de consultas

Situación de gestión: la gerencia necesita (1) estimar cuántas personas tienen obesidad para dimensionar programas de control de peso, (2) saber con qué frecuencia llegan pacientes con infarto seguidos para organizar la sala de hemodinamia y (3) calcular cuántas horas de profesional exige una jornada de tamizaje.

# Parámetros reales del IMC en la base
media_imc <- mean(corazon$BMI)
de_imc    <- sd(corazon$BMI)

set.seed(2026)
# Si el IMC siguiera una distribución normal con esos parámetros...
imc_simulado <- rnorm(1000, mean = media_imc, sd = de_imc)

# Comparar el IMC simulado con el real: ¿se parecen los extremos?
summary(imc_simulado)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   8.952  24.019  28.419  28.413  32.283  50.041
summary(corazon$BMI)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   12.02   24.03   27.34   28.33   31.42   94.85
# Obesidad (IMC >= 30): pnorm() da P(IMC < 30), por eso se resta de 1
c(segun_normal = 1 - pnorm(30, mean = media_imc, sd = de_imc),
  real         = mean(corazon$BMI >= 30))
## segun_normal         real 
##    0.3960966    0.3215873
# Obesidad severa (IMC >= 40)
c(segun_normal = 1 - pnorm(40, mean = media_imc, sd = de_imc),
  real         = mean(corazon$BMI >= 40))
## segun_normal         real 
##   0.03312301   0.05123595
# Curva normal teórica calculada con dnorm(), para superponerla a los datos reales
curva_normal <- tibble(
  imc = seq(10, 70, by = 0.5),
  densidad = dnorm(imc, mean = media_imc, sd = de_imc)
)

grafico_imc <- ggplot() +
  geom_histogram(
    data = muestra,
    aes(x = BMI, y = after_stat(density)),
    binwidth = 2, boundary = 0, fill = "#3B528B", alpha = 0.6
  ) +
  geom_line(data = curva_normal, aes(x = imc, y = densidad),
            color = "#D95F02", linewidth = 1) +
  labs(
    title = "IMC real (muestra de 1.000) frente a la curva normal teórica",
    x = "Índice de masa corporal (kg/m²)",
    y = "Densidad"
  ) +
  theme_minimal()

ggplotly(grafico_imc)
# Mismo SUPUESTO del bloque anterior: 3 infartos por día = 3/24 por hora.
# Si las llegadas siguen una Poisson, el tiempo entre una y otra es exponencial
tasa_hora <- 3 / 24

# Tiempo promedio entre llegadas, en horas
1 / tasa_hora
## [1] 8
set.seed(2026)
tiempos_entre_llegadas <- rexp(1000, rate = tasa_hora)
summary(tiempos_entre_llegadas)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##  0.004507  2.277612  5.849029  8.325302 11.943313 55.446716
# Si la sala de hemodinamia necesita 4 horas para quedar lista otra vez,
# ¿qué probabilidad hay de que llegue otro infarto antes?
pexp(4, rate = tasa_hora)
## [1] 0.3934693
curva_exponencial <- tibble(
  horas = seq(0, 40, by = 0.5),
  densidad = dexp(horas, rate = tasa_hora)
)

grafico_llegadas <- ggplot() +
  geom_histogram(
    data = tibble(horas = tiempos_entre_llegadas),
    aes(x = horas, y = after_stat(density)),
    binwidth = 2, boundary = 0, fill = "#3B528B", alpha = 0.6
  ) +
  geom_line(data = curva_exponencial, aes(x = horas, y = densidad),
            color = "#D95F02", linewidth = 1) +
  labs(
    title = "Tiempo entre llegadas de pacientes con infarto (simulado; supuesto: 3 por día)",
    x = "Horas entre una llegada y la siguiente",
    y = "Densidad"
  ) +
  theme_minimal()

ggplotly(grafico_llegadas)
set.seed(2026)
# SUPUESTO: cada consulta de tamizaje dura entre 15 y 25 minutos y cualquier
# valor del rango es igual de probable (distribución uniforme continua)
duracion_consultas <- runif(100, min = 15, max = 25)
summary(duracion_consultas)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   15.01   17.32   19.60   19.84   22.22   24.78
# Horas de profesional que exige una jornada de 100 pacientes
sum(duracion_consultas) / 60
## [1] 33.06733

Interpretación

IMC y distribución normal (rnorm(), dnorm(), pnorm()). Con la media (28,3) y la desviación estándar (6,4) reales del IMC, la distribución normal predice que el 39,6 % de los encuestados tendría obesidad (IMC ≥ 30), cuando en realidad es el 32,2 %; en cambio, para la obesidad severa (IMC ≥ 40) predice 3,3 % frente a 5,1 % real. La normal sobrestima la obesidad general y subestima la severa porque el IMC real es asimétrico: la mayoría se concentra cerca del promedio y unas pocas personas tienen valores muy altos, una “cola” que la curva normal no reproduce (ver gráfico). Si un hospital planeara su programa de cirugía bariátrica con la curva normal, subestimaría la demanda en cerca de un tercio. Para la gestión, las proporciones deben calcularse con los datos reales y no con un modelo que no se ajusta a ellos.

Llegadas a hemodinamia (rexp(), dexp(), pexp()). Bajo el supuesto de 3 infartos por día, el tiempo promedio entre llegadas es de 8 horas. Sin embargo, en el 39,3 % de los casos el siguiente paciente llega antes de 4 horas, el tiempo supuesto para que la sala quede lista otra vez. Es decir, en cerca de 4 de cada 10 llegadas la sala podría estar ocupada o en preparación, lo que justifica contar con un equipo de respaldo o un protocolo de remisión.

Duración de consultas (runif()). Si cada consulta de tamizaje dura entre 15 y 25 minutos, una jornada de 100 pacientes exige 33.1 horas de profesional. Con turnos de 8 horas, se necesitan 5 profesionales; el resultado se redondea hacia arriba, porque una fracción de turno también tiene que cubrirse.

Límite. Las cifras del IMC provienen de la base, pero el promedio de 3 infartos diarios, las 4 horas de preparación, la duración de las consultas y los turnos de 8 horas son supuestos del ejercicio. Además, se comprobó que el supuesto de normalidad no describe bien el IMC real, por lo que no conviene usarlo para planear servicios.

Estadística descriptiva

Situación de gestión: antes de comparar grupos, la gerencia necesita conocer los valores típicos y la variabilidad de los indicadores clave de salud de la población encuestada.

Indicadores clave

# Indicadores del IMC con funciones base de R
c(media      = mean(corazon$BMI),
  mediana    = median(corazon$BMI),
  desviacion = sd(corazon$BMI),
  varianza   = var(corazon$BMI))
##      media    mediana desviacion   varianza 
##   28.32540   27.34000    6.35610   40.40001
range(corazon$BMI)
## [1] 12.02 94.85
# Resumen completo de las cuatro variables numéricas
corazon |>
  select(BMI, PhysicalHealth, MentalHealth, SleepTime) |>
  summary()
##       BMI        PhysicalHealth    MentalHealth      SleepTime     
##  Min.   :12.02   Min.   : 0.000   Min.   : 0.000   Min.   : 1.000  
##  1st Qu.:24.03   1st Qu.: 0.000   1st Qu.: 0.000   1st Qu.: 6.000  
##  Median :27.34   Median : 0.000   Median : 0.000   Median : 7.000  
##  Mean   :28.33   Mean   : 3.372   Mean   : 3.898   Mean   : 7.097  
##  3rd Qu.:31.42   3rd Qu.: 2.000   3rd Qu.: 3.000   3rd Qu.: 8.000  
##  Max.   :94.85   Max.   :30.000   Max.   :30.000   Max.   :24.000
# Los mismos indicadores, separados por antecedente de enfermedad cardíaca
corazon |>
  group_by(HeartDisease) |>
  summarise(
    imc_media           = mean(BMI),
    imc_mediana         = median(BMI),
    sueno_media         = mean(SleepTime),
    dias_mala_salud_fis = mean(PhysicalHealth),
    dias_mala_salud_men = mean(MentalHealth)
  )
## # A tibble: 2 × 6
##   HeartDisease imc_media imc_mediana sueno_media dias_mala_salud_fis
##   <chr>            <dbl>       <dbl>       <dbl>               <dbl>
## 1 No                28.2        27.3        7.09                2.96
## 2 Yes               29.4        28.3        7.14                7.81
## # ℹ 1 more variable: dias_mala_salud_men <dbl>

Exploración gráfica

# Porcentaje con enfermedad cardíaca en cada grupo de edad
prevalencia_edad <- corazon |>
  group_by(AgeCategory) |>
  summarise(porcentaje = mean(HeartDisease == "Yes") * 100)

grafico_edad <- ggplot(prevalencia_edad, aes(x = AgeCategory, y = porcentaje)) +
  geom_col(fill = "#3B528B") +
  labs(
    title = "Porcentaje de encuestados con enfermedad cardíaca por grupo de edad",
    x = "Grupo de edad (años)",
    y = "% con enfermedad cardíaca"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplotly(grafico_edad)

Interpretación

Indicadores del IMC. El IMC promedio es 28,3 kg/m², con una mediana de 27,3, una desviación estándar de 6,4 (varianza de 40,4) y un rango de 12,0 a 94,9. Que la media supere a la mediana indica una distribución asimétrica hacia valores altos, coherente con lo encontrado al compararla con la distribución normal. El promedio de la población encuestada ya se ubica en el rango de sobrepeso (25 a 29,9).

Días de mala salud y sueño. En los días de mala salud física y mental la mediana es 0: la mayoría no reporta ningún día, pero un grupo reporta muchos (hasta 30), lo que eleva los promedios (3,4 y 3,9 días). En variables así, la mediana describe mejor a la persona típica que el promedio. El sueño promedio es de 7,1 horas, con valores extremos (1 y 24 horas) poco plausibles.

Comparación por grupo. Quienes tienen enfermedad cardíaca reportan en promedio 7,8 días de mala salud física al mes, frente a 3,0 de quienes no la tienen (2,6 veces más), y un IMC promedio más alto (29,4 frente a 28,2). En salud mental (4,6 frente a 3,8 días) y en sueño (7,1 horas en ambos grupos) las diferencias son pequeñas. El gráfico interactivo confirma que el porcentaje con enfermedad cardíaca crece con la edad, de 0,6 % a 22,6 %.

Implicación para la gestión. Los días de mala salud física son un indicador sencillo de la carga de la enfermedad: las personas con enfermedad cardíaca reportan casi 5 días más al mes, lo que puede traducirse en ausentismo laboral y mayor uso de servicios. Límite: todos los indicadores son autorreportados.

Estadística inferencial

Las pruebas de hipótesis siguen la misma lógica del caso del memorando ejecutivo trabajado en clase: se plantea una hipótesis nula (H0: “no hay diferencia” o “no hay asociación”) y una alternativa (H1), y se rechaza H0 si el valor p es menor que el nivel de significación α = 0,05.

Advertencia: con 319.795 registros, casi cualquier diferencia, por pequeña que sea, resulta estadísticamente significativa. Por eso, además del valor p, se reportan intervalos de confianza y tamaños del efecto (medidas de qué tan grande es la diferencia), que son los que indican si un resultado es relevante para la gestión.

Prueba t: IMC según enfermedad cardíaca

# Prueba t de Welch: compara dos medias sin suponer varianzas iguales
# H0: el IMC promedio es igual en ambos grupos; H1: es distinto
# (el intervalo que muestra R es para No - Yes, por eso sale negativo)
prueba_t_imc <- t.test(BMI ~ HeartDisease, data = corazon)
prueba_t_imc
## 
##  Welch Two Sample t-test
## 
## data:  BMI by HeartDisease
## t = -28.402, df = 32295, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group No and group Yes is not equal to 0
## 95 percent confidence interval:
##  -1.258156 -1.095712
## sample estimates:
##  mean in group No mean in group Yes 
##          28.22466          29.40159
# Tamaño del efecto (d de Cohen aproximada): la diferencia medida en
# desviaciones estándar; menos de 0,2 se considera un efecto pequeño
diff(prueba_t_imc$estimate) / sd(corazon$BMI)
## mean in group Yes 
##          0.185166

Interpretación

El IMC promedio es 29,40 en quienes reportan enfermedad cardíaca y 28,22 en quienes no: una diferencia de 1,18 kg/m² (IC 95 %: 1,10 a 1,26). La prueba t de Welch (t = −28,4; p < 0,001) rechaza H0: la diferencia es estadísticamente significativa. Sin embargo, el tamaño del efecto es pequeño (d ≈ 0,19): la diferencia equivale a menos de una quinta parte de la variación habitual del IMC entre personas. Para la gestión: el exceso de peso se asocia con la enfermedad cardíaca, pero el IMC por sí solo no distingue bien quién la tiene; debe usarse junto con otros factores (edad, tabaquismo, antecedente de ACV) para focalizar programas. Límite: la significancia se explica en parte por el enorme tamaño de la base, y el diseño transversal no permite afirmar causalidad.

Prueba t para una muestra: horas de sueño

# Mismo razonamiento del memorando (prueba unilateral de cola inferior):
# H0: horas de sueño promedio >= 7 (mínimo recomendado para adultos)
# H1: horas de sueño promedio < 7
t.test(corazon$SleepTime, mu = 7, alternative = "less")
## 
##  One Sample t-test
## 
## data:  corazon$SleepTime
## t = 38.228, df = 319794, p-value = 1
## alternative hypothesis: true mean is less than 7
## 95 percent confidence interval:
##      -Inf 7.101252
## sample estimates:
## mean of x 
##  7.097075
# Indicador complementario: proporción que duerme menos de 7 horas
mean(corazon$SleepTime < 7)
## [1] 0.3032755

Interpretación

Siguiendo el razonamiento del memorando, se evaluó si el promedio de sueño está por debajo del mínimo recomendado de 7 horas para adultos. El promedio fue de 7,10 horas (t = 38,2; p = 1; límite superior del IC 95 %: 7,10 horas), por lo que no se rechaza H0: no hay evidencia de que el promedio sea inferior a 7 horas. Sin embargo, el 30,3 % de los encuestados duerme menos de 7 horas. Para la gestión: como en el caso del memorando, la decisión no debe basarse en una lectura simple del promedio; aquí el promedio “cumple”, pero oculta que 3 de cada 10 personas duermen menos de lo recomendado. Para un programa de higiene del sueño, el indicador útil es el porcentaje con sueño insuficiente. Límite: las horas de sueño son autorreportadas.

ANOVA: IMC según salud general percibida

# Ordenar la salud general de peor a mejor; sin esto R la ordena alfabéticamente
corazon <- corazon |>
  mutate(GenHealth = factor(GenHealth,
                            levels = c("Poor", "Fair", "Good", "Very good", "Excellent")))

# IMC promedio y número de personas en cada grupo
corazon |>
  group_by(GenHealth) |>
  summarise(imc_media = mean(BMI), personas = n())
## # A tibble: 5 × 3
##   GenHealth imc_media personas
##   <fct>         <dbl>    <int>
## 1 Poor           30.4    11289
## 2 Fair           30.6    34677
## 3 Good           29.5    93129
## 4 Very good      27.8   113858
## 5 Excellent      26.1    66842
# H0: el IMC promedio es igual en los 5 grupos; H1: al menos uno difiere
anova_imc <- aov(BMI ~ GenHealth, data = corazon)
summary(anova_imc)
##                 Df   Sum Sq Mean Sq F value Pr(>F)    
## GenHealth        4   730416  182604    4791 <2e-16 ***
## Residuals   319790 12189265      38                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Tamaño del efecto (eta cuadrado): proporción de la variación del IMC
# que se explica por el grupo de salud general
suma_cuadrados <- summary(anova_imc)[[1]][["Sum Sq"]]
suma_cuadrados[1] / sum(suma_cuadrados)
## [1] 0.05653513
# El ANOVA dice que hay diferencias, pero no entre cuáles grupos:
# Tukey compara todos los pares y ajusta por comparaciones múltiples
TukeyHSD(anova_imc)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = BMI ~ GenHealth, data = corazon)
## 
## $GenHealth
##                           diff         lwr        upr     p adj
## Fair-Poor            0.2445511  0.06206293  0.4270392 0.0023848
## Good-Poor           -0.9061785 -1.07401337 -0.7383436 0.0000000
## Very good-Poor      -2.6405308 -2.80670576 -2.4743559 0.0000000
## Excellent-Poor      -4.3023031 -4.47366881 -4.1309373 0.0000000
## Good-Fair           -1.1507295 -1.25667374 -1.0447854 0.0000000
## Very good-Fair      -2.8850819 -2.98837627 -2.7817875 0.0000000
## Excellent-Fair      -4.5468541 -4.65830744 -4.4354008 0.0000000
## Very good-Good      -1.7343524 -1.80875906 -1.6599457 0.0000000
## Excellent-Good      -3.3961246 -3.48149712 -3.3107520 0.0000000
## Excellent-Very good -1.6617722 -1.74383333 -1.5797111 0.0000000

Interpretación

El IMC promedio cambia según la salud general percibida: 30,4 (mala), 30,6 (regular), 29,5 (buena), 27,8 (muy buena) y 26,1 (excelente). El ANOVA (F = 4.790,7; p < 0,001) rechaza H0: al menos un grupo tiene un IMC promedio distinto. La prueba de Tukey muestra que todos los pares de grupos difieren de forma significativa; la diferencia más grande se da entre salud regular y excelente (4,5 kg/m² menos en excelente) y la más pequeña, entre mala y regular (0,25 kg/m², sin relevancia práctica). El tamaño del efecto (η² = 0,057) indica que la salud general percibida explica cerca del 6 % de la variación del IMC. Para la gestión: las personas que califican su salud como mala o regular concentran los mayores niveles de IMC; la pregunta de salud percibida, fácil y barata de aplicar, puede servir como filtro inicial para remitir a programas de control de peso. Límite: el ANOVA supone varianzas similares entre grupos, algo que no se verificó formalmente; una alternativa que no requiere ese supuesto es el ANOVA de Welch (oneway.test()).

Chi-cuadrado: tabaquismo y enfermedad cardíaca

# H0: fumar y tener enfermedad cardíaca son independientes; H1: están asociados
# tabla_tabaco tiene los conteos; se creó en el bloque de probabilidades
prueba_chi <- chisq.test(tabla_tabaco)
prueba_chi
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  tabla_tabaco
## X-squared = 3713, df = 1, p-value < 2.2e-16
# Conteos que se esperarían si las dos variables fueran independientes
round(prueba_chi$expected)
##      Cardiaca
## Fuma      No   Yes
##   No  171805 16082
##   Yes 120617 11291
# Fuerza de la asociación (V de Cramér): 0 = ninguna, 1 = total
sqrt(prueba_chi$statistic / sum(tabla_tabaco))
## X-squared 
## 0.1077528

Interpretación

La prueba chi-cuadrado (X² = 3.713; gl = 1; p < 0,001) rechaza la hipótesis de independencia: fumar y tener enfermedad cardíaca están asociados. Si fueran independientes, se esperarían unos 11.291 fumadores con enfermedad cardíaca; se observaron 16.037. La V de Cramér (0,11) indica una asociación débil en términos estadísticos, aunque, como se vio en las probabilidades condicionales, los fumadores tienen el doble de probabilidad (12,2 % frente a 6,0 %). Para la gestión: los programas de cesación del tabaco son pertinentes, pero no suficientes: el 41 % de los casos se presenta en personas que no fuman. Límite: la variable registra si la persona fumó al menos 100 cigarrillos en su vida, no si fuma actualmente, y la asociación no prueba causalidad.

Prueba de proporciones: mujeres y hombres

# H0: la proporción con enfermedad cardíaca es igual en mujeres y hombres
tabla_sexo <- table(Sexo = corazon$Sex, Cardiaca = corazon$HeartDisease)
tabla_sexo
##         Cardiaca
## Sexo         No    Yes
##   Female 156571  11234
##   Male   135851  16139
prop.test(x = tabla_sexo[, "Yes"], n = rowSums(tabla_sexo))
## 
##  2-sample test for equality of proportions with continuity correction
## 
## data:  tabla_sexo[, "Yes"] out of rowSums(tabla_sexo)
## X-squared = 1568.3, df = 1, p-value < 2.2e-16
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  -0.04120085 -0.03727487
## sample estimates:
##     prop 1     prop 2 
## 0.06694675 0.10618462

Interpretación

El 6,7 % de las mujeres (11.234 de 167.805) y el 10,6 % de los hombres (16.139 de 151.990) reportan enfermedad cardíaca. La prueba de proporciones (X² = 1.568,3; p < 0,001) indica que la diferencia es estadísticamente significativa: con 95 % de confianza, la proporción en hombres supera a la de mujeres entre 3,7 y 4,1 puntos porcentuales, es decir, los hombres tienen cerca de 1,6 veces la probabilidad de las mujeres. Para la gestión: las estrategias de tamizaje y prevención pueden dar prioridad a los hombres, sin descuidar a las mujeres, que aportan el 41 % de los casos. Límite: la comparación no está ajustada por edad ni por otros factores; parte de la diferencia podría deberse a que los grupos difieren, por ejemplo, en edad o tabaquismo.

Gráficos comparativos con ggpubr

ggboxplot(
  corazon, x = "HeartDisease", y = "BMI",
  fill = "HeartDisease", palette = c("#3B528B", "#D95F02"),
  title = "IMC según antecedente de enfermedad cardíaca",
  xlab = "Antecedente de enfermedad cardíaca",
  ylab = "Índice de masa corporal (kg/m²)",
  legend = "none"
) +
  stat_compare_means(method = "t.test")

ggboxplot(
  corazon, x = "GenHealth", y = "BMI",
  fill = "#9ECAE1",
  title = "IMC según salud general percibida",
  xlab = "Salud general percibida",
  ylab = "Índice de masa corporal (kg/m²)"
) +
  stat_compare_means(method = "anova")

Interpretación

En el primer gráfico, las cajas de IMC de ambos grupos se superponen casi por completo, aunque la mediana es algo mayor en quienes tienen enfermedad cardíaca (28,3 frente a 27,3). La prueba t incluida en el gráfico es significativa, pero la superposición confirma visualmente que la diferencia es pequeña: significancia estadística no es lo mismo que relevancia práctica. El segundo gráfico muestra un descenso escalonado del IMC desde salud regular y mala hasta salud excelente, coherente con el ANOVA. Para la gestión: este tipo de gráfico permite comunicar a una junta directiva, en una sola imagen, la dirección de una diferencia y su magnitud real.

Conclusiones y recomendaciones para la gestión

Hallazgos principales

  1. Carga y concentración. El 8,6 % de los encuestados reporta enfermedad coronaria o infarto. Los adultos de 65 años o más son el 34,7 % de la población, pero concentran el 67,4 % de los casos. La probabilidad es mayor en quienes tuvieron un ACV (36,4 %), en fumadores (12,2 % frente a 6,0 %) y en hombres (10,6 % frente a 6,7 %).
  2. Planear con rangos, no con promedios. Si las jornadas de tamizaje se planean con el promedio de casos, los cupos no alcanzan en el 35,3 % de ellas; 13 cupos por cada 100 pacientes cubren cerca del 95 % de las jornadas. Con 5 camas de urgencias, la demanda supera la capacidad unos 31 días al año; con 6, unos 12.
  3. Los modelos teóricos deben verificarse. La distribución normal no describe bien el IMC: subestima la obesidad severa en cerca de un tercio, lo que llevaría a subdimensionar programas como la cirugía bariátrica.
  4. Significancia no es relevancia. Todas las pruebas fueron significativas por el tamaño de la base, pero los tamaños del efecto fueron pequeños: d = 0,19 para el IMC, V = 0,11 para el tabaquismo y η² = 0,057 para la salud percibida.
  5. Los promedios pueden ocultar problemas. El sueño promedio cumple el mínimo recomendado, pero 3 de cada 10 personas duermen menos de 7 horas.

Recomendaciones

  • Dirigir el programa de prevención cardiovascular prioritario a los adultos de 65 años o más.
  • Crear una ruta de seguimiento conjunto cardio-cerebrovascular para pacientes con antecedente de ACV.
  • Incluir la cesación del tabaco como un componente de la prevención, no como la única estrategia.
  • Dimensionar cupos y camas con percentiles (por ejemplo, 13 cupos por jornada de 100 pacientes) y evaluar el costo-beneficio de una sexta cama de urgencias.
  • Antes de decidir, reemplazar los supuestos de las simulaciones con datos reales de la institución.

Limitaciones

Los datos provienen de una encuesta transversal y autorreportada, realizada en EE. UU. en 2020 y sin pesos de muestreo: los resultados describen a los encuestados, no necesariamente a toda la población, y muestran asociaciones, no relaciones de causa y efecto. El promedio de infartos diarios, los tiempos de preparación, la duración de las consultas y los turnos son supuestos del ejercicio.

Referencias