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.
Directivos y equipos de planeación de instituciones prestadoras de servicios de salud (IPS), aseguradores (EPS) y responsables de programas de prevención cardiovascular.
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.
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.
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.
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
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.
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
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.
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)
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.
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
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.
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 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>
# 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)
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.
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 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
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.
# 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
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.
# 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
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()).
# 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
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.
# 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
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.
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")
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.
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.