A continuación un análisis de BRFSS 2020 Survey Data Behavioral Risk Factor Surveillance System 2020 Survey Data

Introducción

El análisis de datos constituye una herramienta fundamental para comprender fenómenos relacionados con la salud pública, identificar patrones y obtener información relevante para la toma de decisiones. En este trabajo se utiliza la base de datos de la encuesta Behavioral Risk Factor Surveillance System (BRFSS) correspondiente al año 2020, con el propósito de explorar sus características, aplicar conceptos de probabilidad y estadística y desarrollar un modelo analítico que permita estudiar factores asociados al estado de salud de las personas.

Inicialmente, se realiza un análisis exploratorio de datos (EDA) para conocer la estructura de la base de datos, identificar variables de interés y examinar su comportamiento mediante tablas, medidas descriptivas y representaciones gráficas. Posteriormente, se aplican conceptos de probabilidad y métodos de estadística descriptiva e inferencial para analizar variables relacionadas con la salud, las horas de sueño y las condiciones físicas y mentales, mediante técnicas de simulación, cálculo de probabilidades, estimación estadística y pruebas de hipótesis.

Finalmente, se desarrolla un modelo analítico de regresión logística para explorar la relación entre la edad, las horas de sueño y los días con dificultades de salud física y mental, con el fin de estimar la probabilidad de presentar un estado de salud no favorable. El desempeño del modelo se evalúa mediante métricas de clasificación y una curva ROC, complementando el análisis con representaciones gráficas interactivas que facilitan la interpretación de los resultados.

El trabajo se desarrolla en RStudio mediante R Markdown, integrando herramientas de programación, análisis estadístico y visualización de datos. De esta manera, se busca conectar los fundamentos teóricos con el análisis de datos reales, identificar patrones y asociaciones entre variables y comprender el aporte de las técnicas estadísticas y los modelos analíticos al estudio de situaciones de interés en salud pública.

Objetivos

Objetivo general

Analizar la base de datos de la encuesta BRFSS 2020 mediante técnicas de análisis exploratorio, probabilidad, estadística descriptiva e inferencial y modelado analítico, con el fin de identificar patrones y asociaciones entre variables relacionadas con el estado de salud, las horas de sueño y las condiciones físicas y mentales de las personas.

Objetivos específicos

  1. Explorar y caracterizar la base de datos BRFSS 2020 mediante tablas, medidas estadísticas y representaciones gráficas que permitan comprender la distribución y el comportamiento de las variables seleccionadas.

  2. Aplicar conceptos de probabilidad y estadística mediante simulaciones, cálculo de probabilidades, estimaciones y pruebas de hipótesis para analizar situaciones relacionadas con la salud.

  3. Preparar y seleccionar variables relevantes para estudiar la relación entre la edad, las horas de sueño y los días con dificultades de salud física y mental.

  4. Construir un modelo de regresión logística que permita estimar la probabilidad de presentar un estado de salud no favorable a partir de las variables seleccionadas.

  5. Evaluar e interpretar los resultados mediante métricas de desempeño y representaciones gráficas, con el propósito de identificar patrones y comprender las asociaciones entre las variables analizadas.

1. Exploración Analítica de Datos (EDA)

1.1 Reconocimiento y estructura de la base de datos

En esta primera etapa se realizó un reconocimiento general de la base de datos BRFSS 2020, con el propósito de conocer su tamaño, estructura y las variables disponibles para el análisis. Para ello, se revisó el número de registros y variables, una muestra inicial de los datos, la estructura de las variables y sus nombres.

library(tidyverse)
library(plotly)
datos <- readr::read_csv(
  "C:\\Users\\loren\\OneDrive\\Documentos\\FUCS\\Analitica de datos\\Datos de la encuesta BRFSS 2020.csv"
)
dim(datos)
## [1] 401958    279
head(datos, 6)
glimpse(datos %>% select(1:6))
## Rows: 401,958
## Columns: 6
## $ `_STATE` <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1…
## $ FMONTH   <dbl> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2…
## $ IDATE    <dbl> 1042020, 2072020, 1232020, 1092020, 1042020, 1092020, 1092020…
## $ IMONTH   <dbl> 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 2, 2, 2, 2…
## $ IDAY     <dbl> 4, 7, 23, 9, 4, 9, 9, 29, 30, 4, 5, 29, 7, 9, 18, 20, 8, 27, …
## $ IYEAR    <dbl> 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2020, 2…
head(names(datos), 6)
## [1] "_STATE" "FMONTH" "IDATE"  "IMONTH" "IDAY"   "IYEAR"

1.2. Identificación y revisión de las variables

En esta etapa se seleccionaron algunas variables de interés para realizar una revisión inicial de la información disponible en la base de datos. Las variables fueron seleccionadas considerando aspectos sociodemográficos, estado de salud, hábitos y acceso a servicios de salud.

datos %>%
  select(GENHLTH, SEXVAR, PHYSHLTH, MENTHLTH, 
         HLTHPLN1, CHECKUP1, EXERANY2, SLEPTIM1,
         DIABETE4, SMOKE100, `_AGE_G`, `_BMI5CAT`) %>%
  glimpse()
## Rows: 401,958
## Columns: 12
## $ GENHLTH    <dbl> 2, 3, 3, 1, 2, 4, 3, 4, 2, 4, 4, 3, 2, 4, 3, 5, 2, 2, 3, 3,…
## $ SEXVAR     <dbl> 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 1, 2, 2,…
## $ PHYSHLTH   <dbl> 3, 88, 88, 88, 88, 20, 88, 15, 28, 6, 15, 5, 88, 88, 88, 30…
## $ MENTHLTH   <dbl> 30, 88, 88, 88, 88, 30, 88, 10, 88, 88, 88, 88, 88, 88, 88,…
## $ HLTHPLN1   <dbl> 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ CHECKUP1   <dbl> 4, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,…
## $ EXERANY2   <dbl> 1, 1, 1, 2, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 2, 2,…
## $ SLEPTIM1   <dbl> 5, 7, 7, 6, 7, 8, 6, 6, 8, 12, 4, 9, 6, 5, 10, 15, 5, 8, 7,…
## $ DIABETE4   <dbl> 1, 3, 3, 3, 3, 1, 3, 1, 3, 3, 3, 1, 3, 4, 3, 1, 3, 1, 3, 3,…
## $ SMOKE100   <dbl> 1, NA, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, NA, 2, 2, 1, 1, 1, 2, …
## $ `_AGE_G`   <dbl> 5, 6, 6, 6, 6, 6, 6, 6, 3, 6, 6, 6, 6, 6, 6, 5, 5, 6, 6, 5,…
## $ `_BMI5CAT` <dbl> 1, 3, NA, NA, 2, 3, 2, 3, 2, 3, 2, 4, NA, 3, 4, 4, 3, 3, 3,…

Descripción de las variables seleccionadas

La siguiente tabla presenta una descripción general de las principales variables seleccionadas para la exploración inicial de la base de datos.

Variable Descripción
GENHLTH Estado general de salud
SEXVAR Sexo
PHYSHLTH Salud física
MENTHLTH Salud mental
HLTHPLN1 Cobertura de salud
CHECKUP1 Consulta médica de rutina
EXERANY2 Actividad física
SLEPTIM1 Horas de sueño
DIABETE4 Diabetes
SMOKE100 Antecedente de consumo de cigarrillos
_AGE_G Grupo de edad
_BMI5CAT Categoría de índice de masa corporal

1.3. Calidad y valores ausentes de los datos

En esta etapa se realizó una revisión general de la calidad de los datos, con el propósito de identificar la presencia de valores ausentes en la base de datos. Esta revisión permite reconocer qué variables presentan información incompleta y tenerlo en cuenta antes de realizar análisis posteriores.

sum(is.na(datos))
## [1] 51295182
faltantes <- data.frame(
  variable = names(datos),
  faltantes = colSums(is.na(datos))
)

head(
  faltantes[order(-faltantes$faltantes), ],
  6
)

La revisión de los valores ausentes permitió identificar que algunas variables de la base de datos presentan información incompleta. La cantidad de valores ausentes puede variar entre las diferentes variables, por lo que es importante considerar esta característica antes de realizar análisis posteriores.

1.4. Visualización exploratoria de los datos

Gráfico 1. Estado general de salud

salud <- datos %>%
  filter(GENHLTH %in% 1:5) %>%
  count(GENHLTH) %>%
  mutate(
    estado_salud = factor(
      GENHLTH,
      levels = 1:5,
      labels = c(
        "Excelente",
        "Muy buena",
        "Buena",
        "Regular",
        "Mala"
      )
    )
  )

grafico_salud <- ggplot(
  salud,
  aes(x = estado_salud, y = n)
) +
  geom_col(fill = "steelblue") +
  labs(
    title = "Estado general de salud",
    x = "Estado general de salud",
    y = "Número de registros"
  )

ggplotly(grafico_salud)

Interpretación: El gráfico muestra que la categoría “Muy buena” presenta la mayor frecuencia, con aproximadamente 138.000 registros, seguida de “Buena”, con cerca de 119.000, y “Excelente”, con alrededor de 81.000. Por su parte, la categoría “Regular” reúne aproximadamente 46.000 registros, mientras que “Mala” presenta la menor frecuencia, con cerca de 15.000 registros.

Estos resultados muestran que las respuestas se concentran principalmente en las categorías “Muy buena” y “Buena”, mientras que las valoraciones “Regular” y “Mala” tienen una menor representación. Sin embargo, la categoría “Regular” sigue siendo relevante, pues reúne una cantidad considerable de respuestas y permite identificar participantes que perciben su estado de salud de manera menos favorable. En general, entre las cinco categorías, predominan las valoraciones positivas del estado general de salud.

Gráfico 2. Grupos de edad

edad <- datos %>%
  filter(`_AGE_G` %in% 1:6) %>%
  count(`_AGE_G`) %>%
  mutate(
    grupo_edad = factor(
      `_AGE_G`,
      levels = 1:6,
      labels = c(
        "18-24",
        "25-34",
        "35-44",
        "45-54",
        "55-64",
        "65 años o más"
      )
    )
  )

grafico_edad <- ggplot(
  edad,
  aes(x = grupo_edad, y = n)
) +
  geom_col(fill = "darkseagreen3") +
  labs(
    title = "Distribución de los registros por grupo de edad",
    x = "Grupo de edad",
    y = "Número de registros"
  )

ggplotly(grafico_edad)

Interpretación: El gráfico muestra una distribución desigual de los registros entre los seis grupos de edad. El grupo de 65 años o más presenta la mayor frecuencia, con aproximadamente 140.000 registros, una cifra considerablemente superior a la de los demás grupos. Le sigue el grupo de 55 a 64 años, con cerca de 78.000 registros. En los grupos de 45 a 54, 35 a 44 y 25 a 34 años se observa un aumento progresivo, con aproximadamente 61.000, 52.000 y 44.000 registros, respectivamente. Por último, el grupo de 18 a 24 años presenta la menor frecuencia, con alrededor de 25.000 registros.

Esta distribución indica que la base de datos contiene una mayor representación de personas de 55 años o más, especialmente del grupo de 65 años o más, mientras que los adultos jóvenes tienen una menor participación. Por ello, al interpretar los resultados generales de la encuesta, es importante tener presente que la composición por edad de los registros no es uniforme y puede influir en los patrones observados.

Gráfico 3. Horas de sueño

sueno <- datos %>%
  filter(SLEPTIM1 >= 1 & SLEPTIM1 <= 24)

grafico_sueno <- ggplot(
  sueno,
  aes(x = SLEPTIM1)
) +
  geom_histogram(
    binwidth = 1,
    fill = "mediumpurple",
    color = "white"
  ) +
  labs(
    title = "Distribución de las horas de sueño",
    x = "Horas de sueño",
    y = "Número de registros"
  )

ggplotly(grafico_sueno)

Interpretación: El histograma muestra que la mayor concentración de registros se encuentra entre las 6 y 8 horas de sueño, con un máximo alrededor de las 8 horas, que alcanza aproximadamente 122.000 registros. También se observa una frecuencia importante en las 7 horas, con cerca de 120.000 registros, y en las 6 horas, con aproximadamente 83.000. En contraste, las duraciones inferiores a 5 horas y superiores a 9 horas presentan frecuencias considerablemente menores.

La distribución indica que las respuestas se concentran principalmente entre 6 y 8 horas de sueño, mientras que los valores extremos son menos frecuentes. Se observa además una prolongación hacia la derecha del gráfico, debido a que existen algunos registros de más de 10 horas de sueño, aunque su frecuencia es reducida. Este comportamiento permite reconocer que las duraciones habituales se agrupan en un intervalo relativamente estrecho, con predominio de las 7 y 8 horas.

Conclusiones del EDA

La exploración inicial de la base de datos BRFSS 2020 permitió reconocer su estructura, identificando 401.958 registros y 279 variables relacionadas con características sociodemográficas, estado de salud, factores de riesgo, acceso a servicios de salud y utilización de servicios preventivos.

La revisión de las variables permitió identificar diferentes tipos de información y reconocer la existencia de valores ausentes en algunas variables. También se observó que varias variables de la base utilizan códigos numéricos, por lo que su interpretación debe realizarse teniendo en cuenta la documentación oficial de la encuesta.

Las visualizaciones exploratorias permitieron obtener una primera aproximación a la distribución de los registros según estado general de salud, grupos de edad y horas de sueño. Estos gráficos facilitan el reconocimiento de las características generales de la información antes de realizar análisis posteriores.

En conclusión, esta etapa de EDA permitió familiarizarse con la base de datos, identificar sus principales variables y reconocer aspectos que deberán ser considerados en etapas posteriores del análisis.

2. Probabilidad y Estadística Aplicada (Descriptiva e Inferencial)

En este apartado se aplican conceptos de probabilidad y estadística descriptiva e inferencial a la base de datos de la encuesta BRFSS 2020. El objetivo es explorar los datos, analizar distribuciones, calcular medidas estadísticas y realizar pruebas que permitan identificar posibles diferencias y asociaciones entre variables relacionadas con la salud.

2.1. Probabilidad y simulación

2.1.1. Carga de datos y selección de una muestra aleatoria

Una vez cargada la base de datos de la encuesta BRFSS 2020, se verifica su dimensión y se selecciona una muestra aleatoria de 10 registros mediante la función sample(). Esto permite observar una pequeña parte de la información y comprender cómo se pueden seleccionar elementos al azar de una población de datos.

# Verificar las dimensiones de la base de datos
dim(datos)
## [1] 401958    279
# Seleccionar una muestra aleatoria de 10 registros
set.seed(123)
indices_muestra <- sample(seq_len(nrow(datos)), size = 10)
muestra_aleatoria <- datos[indices_muestra, ]

# Mostrar la muestra seleccionada
muestra_aleatoria

Interpretación: La base de datos contiene 401.958 registros y 279 variables. A partir de ella se seleccionan aleatoriamente 10 registros para observar algunas de sus características. La función set.seed(123) permite reproducir la selección al ejecutar nuevamente el código con los mismos datos.

2.1.2. Probabilidad de las categorías de salud general

La variable GENHLTH representa la percepción que tienen las personas encuestadas sobre su estado de salud general. Sus categorías válidas son: 1 = Excelente, 2 = Muy buena, 3 = Buena, 4 = Regular y 5 = Mala.

A partir de esta variable, se construye una tabla de frecuencias y se calcula la probabilidad empírica de cada categoría, dividiendo el número de respuestas de cada grupo entre el total de respuestas válidas.

# Contar las respuestas válidas
frecuencias_salud <- datos %>%
  filter(GENHLTH %in% 1:5) %>%
  count(GENHLTH, name = "Frecuencia") %>%
  mutate(
    Categoria = factor(
      GENHLTH,
      levels = 1:5,
      labels = c("Excelente", "Muy buena", "Buena", "Regular", "Mala")
    ),
    Probabilidad = Frecuencia / sum(Frecuencia),
    Porcentaje = Probabilidad * 100
  )

frecuencias_salud

Interpretación: Los resultados muestran que la categoría más frecuente es «Muy buena», con aproximadamente el 34,45 % de las respuestas válidas, seguida de «Buena», con el 29,80 %, y «Excelente», con el 20,36 %. Por su parte, las categorías «Regular» y «Mala» representan aproximadamente el 11,53 % y el 3,85 %, respectivamente. Esto indica que, entre las respuestas válidas analizadas, predominan las percepciones favorables del estado de salud. Sin embargo, estos porcentajes describen los datos analizados y no deben interpretarse automáticamente como estimaciones representativas de toda la población, ya que no incorporan los ajustes del diseño muestral de la encuesta.

2.1.3. Simulación de un experimento binomial

Se simula un experimento hipotético en el que cada ensayo tiene dos resultados posibles: éxito o no éxito. Se establece una probabilidad de éxito de 0,5 y se generan 100 ensayos independientes mediante la distribución binomial. Esta simulación tiene fines didácticos y no representa una estimación real de la encuesta BRFSS 2020.

set.seed(123)

simulacion_binomial <- rbinom(
  n = 100,
  size = 1,
  prob = 0.5
)

table(simulacion_binomial)
## simulacion_binomial
##  0  1 
## 53 47
# Probabilidad de obtener exactamente 5 éxitos en 10 ensayos
dbinom(x = 5, size = 10, prob = 0.5)
## [1] 0.2460938

Interpretación: La simulación obtuvo 53 resultados de tipo 0 y 47 de tipo 1. Aunque la probabilidad teórica de cada resultado es del 50 %, las frecuencias observadas pueden variar por efecto del azar. Por otra parte, dbinom() indica que la probabilidad teórica de obtener exactamente cinco éxitos en diez ensayos independientes es aproximadamente 0,2461, equivalente al 24,61 %.

2.1.4. Simulación de eventos mediante la distribución de Poisson

La distribución de Poisson permite modelar el número de eventos que ocurren en un intervalo determinado, suponiendo una tasa promedio constante. En este ejemplo se simula un número hipotético de eventos por periodo, con un promedio de tres eventos.

set.seed(123)

# Simular el número de eventos en 100 periodos
eventos <- rpois(n = 100, lambda = 3)

# Frecuencia de los eventos simulados
table(eventos)
## eventos
##  0  1  2  3  4  5  6  7  8 
##  5 14 24 20 22  9  4  1  1
# Probabilidad de observar exactamente 3 eventos
dpois(x = 3, lambda = 3)
## [1] 0.2240418

Interpretación: La simulación representa el número de eventos que podrían ocurrir en distintos periodos, suponiendo un promedio de tres eventos por periodo. La función table() permite observar cuántas veces se presentó cada número de eventos, mientras que dpois() calcula la probabilidad teórica de observar exactamente tres eventos. Este es un ejemplo simulado con fines académicos y no un resultado observado directamente en la encuesta BRFSS 2020.

2.2. Distribuciones y estadística descriptiva

2.2.1. Simulación de distribuciones de probabilidad

Se generan datos simulados mediante las distribuciones uniforme, normal y exponencial, con el propósito de comprender sus características y comparar el comportamiento de diferentes fenómenos aleatorios. Estos datos son ejemplos matemáticos y no corresponden a respuestas reales de los participantes de la encuesta BRFSS 2020.

set.seed(123)

# Simular las tres distribuciones
uniforme <- runif(1000, min = 0, max = 1)
normal <- rnorm(1000, mean = 0, sd = 1)
exponencial <- rexp(1000, rate = 1)

# Resumen estadístico de cada distribución
summary(uniforme)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## 0.0004654 0.2536404 0.4899502 0.4972778 0.7468051 0.9994045
summary(normal)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -2.80977 -0.68839  0.02881  0.01193  0.65507  3.39037
summary(exponencial)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.003319 0.274730 0.675022 1.003397 1.378160 9.081390
# Visualizar las distribuciones
par(mfrow = c(1, 3))

hist(uniforme,
     main = "Distribución uniforme",
     xlab = "Valores",
     col = "lightblue")

hist(normal,
     main = "Distribución normal",
     xlab = "Valores",
     col = "lightgreen")

hist(exponencial,
     main = "Distribución exponencial",
     xlab = "Valores",
     col = "lightpink")

par(mfrow = c(1, 1))

Interpretación: En la distribución uniforme, las barras presentan alturas similares a lo largo del intervalo de 0 a 1, lo que indica que los valores simulados se distribuyen de manera relativamente equilibrada.

En la distribución normal, los valores se concentran principalmente alrededor de 0 y las frecuencias disminuyen hacia los extremos. Esto refleja una distribución aproximadamente simétrica, en la que los valores cercanos a la media son más frecuentes.

En la distribución exponencial, se observa una mayor concentración de valores cercanos a 0, mientras que las frecuencias disminuyen a medida que los valores aumentan. Esto muestra una distribución asimétrica, en la que los valores grandes son menos frecuentes.

En conjunto, las gráficas permiten reconocer las diferencias entre las tres distribuciones: la uniforme reparte los valores de forma equilibrada, la normal los concentra alrededor de la media y la exponencial presenta una mayor frecuencia de valores pequeños. Estos resultados corresponden a una simulación realizada en R.

# Distribución normal estándar
dnorm(0, mean = 0, sd = 1)
## [1] 0.3989423
pnorm(1, mean = 0, sd = 1)
## [1] 0.8413447
# Distribución exponencial
dexp(1, rate = 1)
## [1] 0.3678794
pexp(1, rate = 1)
## [1] 0.6321206

Interpretación: Los resultados muestran cómo se calculan la densidad y la probabilidad acumulada de las distribuciones normal y exponencial. En la distribución normal, la probabilidad acumulada hasta el valor 1 es aproximadamente 84,13 %. En la distribución exponencial, la probabilidad acumulada hasta el valor 1 es aproximadamente 63,21 %. Por su parte, dnorm() y dexp() calculan la densidad en un punto específico, por lo que sus resultados no deben interpretarse directamente como probabilidades. Estas funciones complementan las simulaciones y permiten comprender el comportamiento matemático de ambas distribuciones.

2.2.2. Medidas descriptivas de las horas de sueño

La variable SLEPTIM1 registra el número de horas de sueño que reportan las personas encuestadas. Para describir su comportamiento, se calculan medidas de tendencia central y dispersión, como la media, la mediana, la desviación estándar, la varianza y el rango de los valores observados. También se utiliza la función summary() para obtener un resumen general de la distribución.

# Convertir la variable a formato numérico y excluir valores faltantes
horas_sueno <- datos %>%
  mutate(SLEPTIM1 = as.numeric(SLEPTIM1)) %>%
  filter(!is.na(SLEPTIM1), SLEPTIM1 >= 1, SLEPTIM1 <= 24) %>%
  pull(SLEPTIM1)

# Calcular medidas descriptivas
media_sueno <- mean(horas_sueno)
mediana_sueno <- median(horas_sueno)
desviacion_sueno <- sd(horas_sueno)
varianza_sueno <- var(horas_sueno)
rango_sueno <- range(horas_sueno)

cat("Número de observaciones válidas:", length(horas_sueno), "\n")
## Número de observaciones válidas: 397264
cat("Media:", round(media_sueno, 2), "horas\n")
## Media: 7.1 horas
cat("Mediana:", round(mediana_sueno, 2), "horas\n")
## Mediana: 7 horas
cat("Desviación estándar:", round(desviacion_sueno, 2), "\n")
## Desviación estándar: 1.47
cat("Varianza:", round(varianza_sueno, 2), "\n")
## Varianza: 2.16
cat("Valor mínimo:", rango_sueno[1], "\n")
## Valor mínimo: 1
cat("Valor máximo:", rango_sueno[2], "\n")
## Valor máximo: 24
summary(horas_sueno)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   1.000   6.000   7.000   7.097   8.000  24.000

Interpretación: Se analizaron 397.264 observaciones válidas. La media fue de aproximadamente 7,1 horas de sueño, mientras que la mediana fue de 7 horas, lo que indica que el valor central de las respuestas se encuentra alrededor de esa cantidad. La desviación estándar fue de 1,47 horas y la varianza de 2,16 horas cuadradas, lo que permite describir la variabilidad de los datos respecto a su media. Los valores oscilaron entre 1 y 24 horas, con el 50 % central de las observaciones ubicado aproximadamente entre 6 y 8 horas. Estos resultados describen las respuestas válidas incluidas en el análisis, después de excluir datos faltantes y valores fuera del rango establecido.

2.2.3. Gráfico de la distribución de horas de sueño

datos_sueno <- datos %>%
  mutate(SLEPTIM1 = as.numeric(SLEPTIM1)) %>%
  filter(!is.na(SLEPTIM1), SLEPTIM1 >= 1, SLEPTIM1 <= 24) %>%
  count(SLEPTIM1, name = "Frecuencia") %>%
  mutate(Porcentaje = Frecuencia / sum(Frecuencia) * 100)

ggplot(datos_sueno, aes(x = SLEPTIM1, y = Porcentaje)) +
  geom_col(fill = "darkseagreen3", color = "white") +
  labs(
    title = "Porcentaje de personas según horas de sueño",
    x = "Horas de sueño",
    y = "Porcentaje (%)"
  ) +
  scale_x_continuous(breaks = 1:24) +
  theme_minimal()

Interpretación: El gráfico muestra que las horas de sueño más frecuentes son 7 y 8 horas, con aproximadamente el 30 % y el 31 % de las respuestas, respectivamente. Les siguen las 6 horas, con cerca del 21 %, mientras que las 5 y 9 horas presentan porcentajes considerablemente menores. A partir de las 10 horas, la frecuencia disminuye notablemente, y las duraciones superiores a 12 horas son prácticamente inexistentes en los datos representados. En general, se observa que la mayoría de las personas reporta entre 6 y 8 horas de sueño, con una mayor concentración en 7 y 8 horas.

2.2.4 Comparación de horas de sueño por salud general

# Preparar los datos para comparar las horas de sueño
datos_grafico <- datos %>%
  mutate(SLEPTIM1 = as.numeric(SLEPTIM1)) %>%
  filter(
    GENHLTH %in% 1:5,
    !is.na(SLEPTIM1),
    SLEPTIM1 >= 1,
    SLEPTIM1 <= 24
  ) %>%
  mutate(
    Categoria = factor(
      GENHLTH,
      levels = 1:5,
      labels = c("Excelente", "Muy buena", "Buena", "Regular", "Mala")
    )
  )

# Crear la gráfica comparativa
ggpubr::ggboxplot(
  datos_grafico,
  x = "Categoria",
  y = "SLEPTIM1",
  color = "Categoria",
  palette = "jco",
  xlab = "Estado de salud general",
  ylab = "Horas de sueño",
  title = "Comparación de horas de sueño según salud general"
)

Interpretación: El gráfico muestra que la mediana de las horas de sueño se encuentra alrededor de 7 horas en las cinco categorías de salud general. Sin embargo, las personas que califican su salud como “Excelente” presentan una menor dispersión en sus horas de sueño, mientras que quienes la califican como “Mala” muestran una mayor variabilidad. También se observan valores atípicos en todos los grupos, correspondientes a personas que reportan cantidades de sueño considerablemente inferiores o superiores a las habituales. En general, las diferencias entre las medianas son pequeñas, aunque la dispersión varía entre categorías. Estos resultados permiten explorar posibles diferencias en los patrones de sueño según la percepción del estado de salud, pero no demuestran una relación causal entre ambas variables.

2.3. Estadística inferencial

La estadística inferencial permite utilizar una muestra para evaluar hipótesis sobre posibles diferencias o asociaciones. En esta sección se realizan pruebas estadísticas con las variables de la encuesta. Debido a que BRFSS utiliza un diseño de encuesta complejo, estas pruebas convencionales se presentan con fines académicos y no incorporan las ponderaciones ni el diseño muestral.

2.3.1. Comparación de horas de sueño entre dos grupos

Se compara la media de horas de sueño de quienes calificaron su salud como excelente y de quienes la calificaron como mala. En GENHLTH, estas categorías corresponden a los códigos 1 y 5, respectivamente.

datos_ttest <- datos %>%
  mutate(SLEPTIM1 = as.numeric(SLEPTIM1)) %>%
  filter(
    GENHLTH %in% c(1, 5),
    !is.na(SLEPTIM1),
    SLEPTIM1 >= 1,
    SLEPTIM1 <= 24
  ) %>%
  mutate(
    Grupo_salud = factor(
      GENHLTH,
      levels = c(1, 5),
      labels = c("Excelente", "Mala")
    )
  )

# Resumen descriptivo por grupo
datos_ttest %>%
  group_by(Grupo_salud) %>%
  summarise(
    Numero_personas = n(),
    Media_sueno = mean(SLEPTIM1),
    Mediana_sueno = median(SLEPTIM1),
    .groups = "drop"
  )
# Prueba t para comparar las medias
prueba_t <- t.test(SLEPTIM1 ~ Grupo_salud, data = datos_ttest)
prueba_t
## 
##  Welch Two Sample t-test
## 
## data:  SLEPTIM1 by Grupo_salud
## t = 18.205, df = 16274, p-value < 2.2e-16
## alternative hypothesis: true difference in means between group Excelente and group Mala is not equal to 0
## 95 percent confidence interval:
##  0.3380529 0.4196312
## sample estimates:
## mean in group Excelente      mean in group Mala 
##                7.211445                6.832603

Interpretación: La prueba t de Welch muestra una diferencia estadísticamente significativa entre las medias de horas de sueño de las personas que calificaron su salud como excelente y aquellas que la calificaron como mala, dado que el valor p es menor que 0,05. El grupo con salud excelente reportó una media de 7,21 horas de sueño, frente a 6,83 horas en el grupo con salud mala, lo que representa una diferencia aproximada de 0,38 horas, equivalente a 23 minutos. El intervalo de confianza del 95 % para la diferencia de medias se encuentra entre 0,34 y 0,42 horas. Estos resultados evidencian una diferencia entre los grupos analizados, pero no permiten afirmar que dormir más sea la causa de una mejor percepción de salud.

2.3.2. Comparación de horas de sueño entre cinco grupos de salud

Se utiliza el análisis de varianza (ANOVA) para comparar las medias de horas de sueño entre las cinco categorías de salud general: excelente, muy buena, buena, regular y mala. El objetivo es determinar si existen diferencias estadísticamente significativas entre las medias de los grupos analizados.

datos_anova <- datos %>%
  mutate(SLEPTIM1 = as.numeric(SLEPTIM1)) %>%
  filter(
    GENHLTH %in% 1:5,
    !is.na(SLEPTIM1),
    SLEPTIM1 >= 1,
    SLEPTIM1 <= 24
  ) %>%
  mutate(
    Grupo_salud = factor(
      GENHLTH,
      levels = 1:5,
      labels = c(
        "Excelente", "Muy buena", "Buena",
        "Regular", "Mala"
      )
    )
  )

# Resumen descriptivo de cada grupo
datos_anova %>%
  group_by(Grupo_salud) %>%
  summarise(
    Numero_personas = n(),
    Media_sueno = mean(SLEPTIM1),
    .groups = "drop"
  )
# Análisis de varianza
modelo_anova <- aov(
  SLEPTIM1 ~ Grupo_salud,
  data = datos_anova
)

summary(modelo_anova)
##                 Df Sum Sq Mean Sq F value Pr(>F)    
## Grupo_salud      4   3673   918.3   428.9 <2e-16 ***
## Residuals   396450 848790     2.1                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Interpretación: El análisis de varianza (ANOVA) muestra diferencias estadísticamente significativas entre las medias de horas de sueño de las cinco categorías de salud general, con un estadístico F de 428,9 y un valor p menor que 0,001. La media más alta corresponde a las personas que calificaron su salud como excelente, con 7,21 horas de sueño, mientras que la más baja corresponde a quienes la calificaron como mala, con 6,83 horas. Se observa una disminución progresiva de las medias entre las categorías. Estos resultados indican que al menos una de las medias difiere estadísticamente de las demás, aunque se requiere una prueba posterior, como la de Tukey, para identificar específicamente qué grupos presentan diferencias. Este análisis convencional no incorpora las ponderaciones ni el diseño muestral complejo de la encuesta BRFSS y no permite establecer una relación de causa y efecto.

2.3.3. Asociación entre sexo registrado y actividad física

Se examina la asociación entre las variables SEXVAR y EXERANY2 para evaluar si existe una relación estadística entre el sexo registrado y la respuesta sobre la práctica de actividad física. Para el análisis se consideran los códigos válidos 1 y 2 de ambas variables, de acuerdo con el diccionario de datos de la encuesta BRFSS 2020.

datos_chi <- datos %>%
  filter(
    SEXVAR %in% c(1, 2),
    EXERANY2 %in% c(1, 2)
  )

# Crear una tabla de contingencia
tabla_sexo_ejercicio <- table(
  Sexo = datos_chi$SEXVAR,
  Actividad_fisica = datos_chi$EXERANY2
)

tabla_sexo_ejercicio
##     Actividad_fisica
## Sexo      1      2
##    1 144357  39236
##    2 161526  56157
# Prueba chi-cuadrado de independencia
prueba_chi <- chisq.test(tabla_sexo_ejercicio)
prueba_chi
## 
##  Pearson's Chi-squared test with Yates' continuity correction
## 
## data:  tabla_sexo_ejercicio
## X-squared = 1076.6, df = 1, p-value < 2.2e-16

Interpretación: La prueba chi-cuadrado de Pearson, con corrección de continuidad de Yates, obtuvo un estadístico de 1076,6 y un valor p menor que 0,001. Este resultado indica una asociación estadísticamente significativa entre el sexo registrado y la respuesta sobre actividad física, dado que el valor p es inferior a 0,05. En la tabla de frecuencias se observa que las respuestas se distribuyen de manera diferente entre los dos grupos. Sin embargo, para comparar adecuadamente la práctica de actividad física entre ellos, es necesario analizar también sus proporciones y no solamente sus frecuencias absolutas. Esta asociación no demuestra causalidad y los resultados corresponden al análisis convencional de los registros seleccionados, sin incorporar las ponderaciones ni el diseño muestral complejo de BRFSS.

2.3.4. Comparación de la proporción que reportó actividad física

Se calcula la proporción de personas que respondieron afirmativamente a la variable EXERANY2 en cada categoría de SEXVAR. Posteriormente, se utiliza la prueba de comparación de proporciones para determinar si existe una diferencia estadísticamente significativa entre ambos grupos.

datos_prop <- datos %>%
  filter(
    SEXVAR %in% c(1, 2),
    EXERANY2 %in% c(1, 2)
  )

# Resumen de respuestas afirmativas y total por grupo
resumen_prop <- datos_prop %>%
  group_by(SEXVAR) %>%
  summarise(
    Total = n(),
    Respuestas_positivas = sum(EXERANY2 == 1),
    Proporcion = Respuestas_positivas / Total,
    .groups = "drop"
  )

resumen_prop
# Prueba de comparación de proporciones
prueba_prop <- prop.test(
  x = resumen_prop$Respuestas_positivas,
  n = resumen_prop$Total
)

prueba_prop
## 
##  2-sample test for equality of proportions with continuity correction
## 
## data:  resumen_prop$Respuestas_positivas out of resumen_prop$Total
## X-squared = 1076.6, df = 1, p-value < 2.2e-16
## alternative hypothesis: two.sided
## 95 percent confidence interval:
##  0.04163352 0.04689488
## sample estimates:
##    prop 1    prop 2 
## 0.7862881 0.7420240

Interpretación: Los resultados muestran que la proporción de respuestas afirmativas sobre actividad física fue de aproximadamente 78,63 % en el grupo identificado con el código 1 de SEXVAR y de 74,20 % en el grupo identificado con el código 2. La diferencia entre las proporciones es de aproximadamente 4,43 puntos porcentuales. La prueba de proporciones obtuvo un valor p menor que 0,001, lo que indica que existe una diferencia estadísticamente significativa entre los grupos analizados. El intervalo de confianza del 95 % para la diferencia se encuentra aproximadamente entre 4,16 y 4,69 puntos porcentuales. Antes de atribuir estos resultados a hombres o mujeres, se deben verificar las etiquetas de SEXVAR en el diccionario de BRFSS. Además, estos resultados no demuestran causalidad ni incorporan las ponderaciones y el diseño muestral complejo de la encuesta.

Conclusiones Probabilidad y Estadística Aplicada

El análisis de probabilidad permitió calcular las frecuencias y proporciones de las categorías de salud general, así como comprender el uso de simulaciones para representar experimentos aleatorios. Las simulaciones de distribuciones mostraron cómo pueden comportarse distintos modelos probabilísticos.

La estadística descriptiva permitió resumir las horas de sueño mediante medidas como la media, la mediana, la desviación estándar y el rango. Los gráficos facilitaron la identificación de patrones en las horas de sueño y la comparación descriptiva de las respuestas según la percepción de salud general.

Finalmente, las pruebas inferenciales permitieron evaluar posibles diferencias entre grupos y asociaciones entre variables. Los resultados deben interpretarse a partir de los valores obtenidos al ejecutar el código y sin afirmar relaciones causales. Como la encuesta BRFSS tiene un diseño muestral complejo, los análisis realizados son ejercicios académicos exploratorios y no sustituyen un análisis que incorpore los pesos y las características del diseño de la encuesta.

3. Modelo Analítico

3.1. Contexto y objetivos

El reto de gestión consiste en identificar características asociadas con una percepción desfavorable del estado de salud de las personas encuestadas en el sistema BRFSS 2020. Esta información puede contribuir a orientar programas de prevención, promoción de la salud y planificación de recursos sanitarios.

El objetivo es construir un modelo analítico que estime la probabilidad de que una persona reporte un estado de salud regular o malo, utilizando como variables explicativas la edad, las horas de sueño y los días con dificultades de salud física y mental.

Se utilizará una regresión logística binaria, técnica que permite estimar la probabilidad de pertenecer a una de dos categorías. El desempeño se evaluará mediante el área bajo la curva ROC (AUC) y la exactitud de clasificación.

Como metas iniciales del ejercicio se propone alcanzar un AUC igual o superior a 0,70 y una exactitud de al menos 70 % en los datos de prueba. Estos valores son criterios académicos de referencia y deberán contrastarse con los resultados obtenidos.

3.2. Selección y preparación de variables

library(tidyverse)
library(rsample)
library(broom)
library(yardstick)
library(plotly)
library(skimr)
library(knitr)

# Seleccionar y limpiar las variables
datos_modelo <- datos %>%
  dplyr::select(
    `_RFHLTH`,
    `_AGE80`,
    SLEPTIM1,
    PHYSHLTH,
    MENTHLTH
  ) %>%
  dplyr::mutate(
    salud = dplyr::case_when(
      `_RFHLTH` == 1 ~ "Favorable",
      `_RFHLTH` == 2 ~ "No favorable",
      TRUE ~ NA_character_
    ),
    edad = as.numeric(`_AGE80`),
    sueno = dplyr::case_when(
      SLEPTIM1 %in% 1:24 ~ as.numeric(SLEPTIM1),
      TRUE ~ NA_real_
    ),
    salud_fisica = dplyr::case_when(
      PHYSHLTH == 88 ~ 0,
      PHYSHLTH %in% 1:30 ~ as.numeric(PHYSHLTH),
      TRUE ~ NA_real_
    ),
    salud_mental = dplyr::case_when(
      MENTHLTH == 88 ~ 0,
      MENTHLTH %in% 1:30 ~ as.numeric(MENTHLTH),
      TRUE ~ NA_real_
    ),
    salud = factor(
      salud,
      levels = c("Favorable", "No favorable")
    )
  ) %>%
  dplyr::select(
    salud,
    edad,
    sueno,
    salud_fisica,
    salud_mental
  ) %>%
  tidyr::drop_na()

# Revisar la estructura de los datos
dplyr::glimpse(datos_modelo)
## Rows: 383,384
## Columns: 5
## $ salud        <fct> Favorable, Favorable, Favorable, Favorable, Favorable, No…
## $ edad         <dbl> 56, 65, 65, 80, 80, 66, 75, 69, 41, 78, 73, 80, 78, 80, 6…
## $ sueno        <dbl> 5, 7, 7, 6, 7, 8, 6, 6, 8, 12, 4, 9, 6, 5, 10, 15, 5, 8, …
## $ salud_fisica <dbl> 3, 0, 0, 0, 0, 20, 0, 15, 28, 6, 15, 5, 0, 0, 0, 30, 0, 0…
## $ salud_mental <dbl> 30, 0, 0, 0, 0, 30, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
# Revisar la cantidad de registros por categoría
table(datos_modelo$salud)
## 
##    Favorable No favorable 
##       327514        55870
# Resumen descriptivo de las variables
skimr::skim(datos_modelo)
Data summary
Name datos_modelo
Number of rows 383384
Number of columns 5
_______________________
Column type frequency:
factor 1
numeric 4
________________________
Group variables None

Variable type: factor

skim_variable n_missing complete_rate ordered n_unique top_counts
salud 0 1 FALSE 2 Fav: 327514, No : 55870

Variable type: numeric

skim_variable n_missing complete_rate mean sd p0 p25 p50 p75 p100 hist
edad 0 1 54.21 17.62 18 40 57 69 80 ▃▅▆▇▇
sueno 0 1 7.10 1.45 1 6 7 8 24 ▁▇▁▁▁
salud_fisica 0 1 3.38 7.98 0 0 0 2 30 ▇▁▁▁▁
salud_mental 0 1 3.88 7.99 0 0 0 3 30 ▇▁▁▁▁

Interpretación:

La base analítica contiene 383.384 registros y cinco variables, sin valores faltantes después de la depuración. El 85,4 % de los registros corresponde a salud favorable y el 14,6 % a salud no favorable. La edad promedio es de 54,2 años y el sueño promedio es de 7,1 horas por día. Además, se registran en promedio 3,38 días con dificultades de salud física y 3,88 días con dificultades de salud mental en el periodo de referencia de la encuesta.

Desde una perspectiva gerencial, estos resultados permiten identificar oportunidades para fortalecer las estrategias de promoción de la salud, higiene del sueño y prevención de dificultades físicas y mentales. Sin embargo, las cifras son descriptivas y no demuestran relaciones causales ni representan necesariamente estimaciones poblacionales ponderadas.

set.seed(123)

datos_modelo <- datos_modelo %>%
  slice_sample(
    n = min(20000, nrow(datos_modelo))
  )

# Verificar el número de registros seleccionados
nrow(datos_modelo)
## [1] 20000

3.3. División de los datos y ajuste del modelo

set.seed(123)

# Muestra aleatoria para el ejercicio
datos_muestra <- dplyr::slice_sample(
  datos_modelo,
  n = min(20000, nrow(datos_modelo))
)

# División 75 % entrenamiento y 25 % prueba
particion <- rsample::initial_split(
  datos_muestra,
  prop = 0.75,
  strata = salud
)

entrenamiento <- rsample::training(particion)
prueba <- rsample::testing(particion)

# Cantidad de registros en cada conjunto
nrow(entrenamiento)
## [1] 14999
nrow(prueba)
## [1] 5001
# Regresión logística binaria
modelo <- glm(
  salud ~ edad + sueno + salud_fisica + salud_mental,
  data = entrenamiento,
  family = binomial()
)

summary(modelo)
## 
## Call:
## glm(formula = salud ~ edad + sueno + salud_fisica + salud_mental, 
##     family = binomial(), data = entrenamiento)
## 
## Coefficients:
##               Estimate Std. Error z value Pr(>|z|)    
## (Intercept)  -3.761361   0.160085 -23.496  < 2e-16 ***
## edad          0.026486   0.001745  15.174  < 2e-16 ***
## sueno        -0.053848   0.017554  -3.068  0.00216 ** 
## salud_fisica  0.115830   0.002709  42.757  < 2e-16 ***
## salud_mental  0.044195   0.002985  14.805  < 2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for binomial family taken to be 1)
## 
##     Null deviance: 12621.8  on 14998  degrees of freedom
## Residual deviance:  9310.2  on 14994  degrees of freedom
## AIC: 9320.2
## 
## Number of Fisher Scoring iterations: 5
# Tabla de coeficientes y odds ratios
tabla_coeficientes <- broom::tidy(modelo, conf.int = TRUE) %>%
  dplyr::mutate(
    Odds_Ratio = exp(estimate),
    IC_inferior = exp(conf.low),
    IC_superior = exp(conf.high)
  ) %>%
  dplyr::select(
    term, estimate, p.value,
    Odds_Ratio, IC_inferior, IC_superior
  )

knitr::kable(
  tabla_coeficientes,
  digits = 3,
  col.names = c(
    "Variable", "Coeficiente", "Valor p",
    "Odds ratio", "IC inferior", "IC superior"
  ),
  caption = "Coeficientes del modelo de regresión logística"
)
Coeficientes del modelo de regresión logística
Variable Coeficiente Valor p Odds ratio IC inferior IC superior
(Intercept) -3.761 0.000 0.023 0.017 0.032
edad 0.026 0.000 1.027 1.023 1.030
sueno -0.054 0.002 0.948 0.915 0.981
salud_fisica 0.116 0.000 1.123 1.117 1.129
salud_mental 0.044 0.000 1.045 1.039 1.051

Interpretación: El modelo de regresión logística muestra que las cuatro variables analizadas presentan asociaciones estadísticamente significativas con la probabilidad de tener un estado de salud no favorable (p < 0,05). La edad presenta una asociación positiva: por cada año adicional, las posibilidades de salud no favorable aumentan aproximadamente un 2,7 %, manteniendo constantes las demás variables. En cambio, las horas de sueño presentan una asociación negativa, pues cada hora adicional se relaciona con una disminución aproximada del 5,2 % en dichas posibilidades. Por su parte, cada día adicional con dificultades de salud física se asocia con un aumento del 12,3 %, mientras que cada día con dificultades de salud mental se relaciona con un incremento del 4,5 %. En conjunto, los resultados destacan una mayor asociación con la salud física, seguida de la edad y la salud mental. Desde una perspectiva gerencial, estos hallazgos pueden orientar estrategias preventivas y de promoción de la salud. Sin embargo, los resultados representan asociaciones estadísticas y no demuestran causalidad.

3.4. Evaluación del desempeño

# Probabilidades estimadas
prob_entrenamiento <- predict(
  modelo,
  newdata = entrenamiento,
  type = "response"
)

prob_prueba <- predict(
  modelo,
  newdata = prueba,
  type = "response"
)

# Clasificación usando un umbral de 0.5
clase_entrenamiento <- ifelse(
  prob_entrenamiento >= 0.5,
  "No favorable", "Favorable"
)

clase_prueba <- ifelse(
  prob_prueba >= 0.5,
  "No favorable", "Favorable"
)

# Calcular AUC y exactitud
metricas <- tibble::tibble(
  Conjunto = c("Entrenamiento", "Prueba"),
  AUC = c(
    yardstick::roc_auc_vec(
      entrenamiento$salud,
      prob_entrenamiento,
      event_level = "second"
    ),
    yardstick::roc_auc_vec(
      prueba$salud,
      prob_prueba,
      event_level = "second"
    )
  ),
  Exactitud = c(
    mean(clase_entrenamiento == entrenamiento$salud),
    mean(clase_prueba == prueba$salud)
  )
)

knitr::kable(
  metricas,
  digits = 3,
  caption = "Desempeño del modelo en entrenamiento y prueba"
)
Desempeño del modelo en entrenamiento y prueba
Conjunto AUC Exactitud
Entrenamiento 0.824 0.887
Prueba 0.819 0.889

Interpretación: El modelo logra clasificar correctamente el 88,9 % de los casos evaluados y presenta un AUC de 0,819, lo que indica una buena capacidad para diferenciar entre las categorías de salud analizadas. Además, los resultados son similares en entrenamiento y prueba, lo que sugiere un desempeño estable en datos nuevos. En términos gerenciales, el modelo puede servir como apoyo para identificar perfiles que requieren mayor atención preventiva; antes de utilizarlo para tomar decisiones, es necesario verificar sus errores de clasificación y confirmar que identifica correctamente los casos de salud no favorable.

# Matriz de confusión del conjunto de prueba

resultados <- tibble::tibble(
  observado = prueba$salud,
  predicho = factor(
    ifelse(prob_prueba >= 0.5,
           "No favorable",
           "Favorable"),
    levels = c("Favorable", "No favorable")
  )
)

# Tabla de clasificación
matriz_confusion <- table(
  Observado = resultados$observado,
  Predicho = resultados$predicho
)

knitr::kable(
  matriz_confusion,
  caption = "Matriz de confusión del modelo en el conjunto de prueba"
)
Matriz de confusión del modelo en el conjunto de prueba
Favorable No favorable
Favorable 4152 104
No favorable 450 295
# Calcular sensibilidad y especificidad
verdaderos_positivos <- matriz_confusion["No favorable", "No favorable"]
falsos_negativos <- matriz_confusion["No favorable", "Favorable"]
verdaderos_negativos <- matriz_confusion["Favorable", "Favorable"]
falsos_positivos <- matriz_confusion["Favorable", "No favorable"]

sensibilidad <- verdaderos_positivos /
  (verdaderos_positivos + falsos_negativos)

especificidad <- verdaderos_negativos /
  (verdaderos_negativos + falsos_positivos)

metricas_clasificacion <- tibble::tibble(
  Metrica = c("Sensibilidad", "Especificidad"),
  Valor = c(sensibilidad, especificidad)
)

knitr::kable(
  metricas_clasificacion,
  digits = 3,
  caption = "Sensibilidad y especificidad del modelo"
)
Sensibilidad y especificidad del modelo
Metrica Valor
Sensibilidad 0.396
Especificidad 0.976

Interpretación: La matriz de confusión muestra que el modelo clasifica correctamente 4.152 personas con salud favorable y 295 con salud no favorable. La sensibilidad del 39,6 % indica una capacidad limitada para identificar los casos de salud no favorable, mientras que la especificidad del 97,6 % refleja un alto desempeño al reconocer los casos de salud favorable. Además, 450 personas con salud no favorable fueron clasificadas erróneamente como favorables. Estos resultados evidencian que el modelo presenta un desempeño desigual entre las categorías y que requiere mejoras para detectar con mayor precisión los casos de salud no favorable.

prob_prueba <- predict(modelo, newdata = prueba, type = "response")

datos_roc <- tibble(
  salud = prueba$salud,
  probabilidad = prob_prueba
)

curva_roc <- yardstick::roc_curve(
  datos_roc,
  truth = salud,
  probabilidad,
  event_level = "second"
)

ggplot(curva_roc, aes(x = 1 - specificity, y = sensitivity)) +
  geom_path(linewidth = 1) +
  geom_abline(
    intercept = 0,
    slope = 1,
    linetype = "dashed"
  ) +
  labs(
    title = "Curva ROC del modelo",
    x = "Tasa de falsos positivos",
    y = "Tasa de verdaderos positivos"
  ) +
  theme_minimal()

Interpretación: La curva ROC permite evaluar qué tan bien el modelo distingue entre personas con salud favorable y no favorable. Al ubicarse por encima de la línea diagonal de referencia, indica que el modelo tiene una buena capacidad de discriminación, identificando correctamente los casos y evaluando el equilibrio entre la detección de casos y los falsos positivos. Cuanto más se aproxima la curva a la esquina superior izquierda, mejor es su desempeño.

3.5. Diagnóstico preliminar de residuos

El diagnóstico de residuos permite analizar las diferencias entre los valores observados de la variable salud y las probabilidades estimadas por el modelo de regresión logística. Para ello, se utilizan los residuos de Pearson, que ayudan a identificar observaciones que presentan diferencias importantes entre el resultado observado y el estimado.

# Calcular probabilidades estimadas y residuos de Pearson
diagnostico <- broom::augment(
  modelo,
  type.predict = "response",
  type.residuals = "pearson"
)

# Graficar los residuos frente a las probabilidades estimadas
ggplot(diagnostico, aes(x = .fitted, y = .resid)) +
  geom_point(alpha = 0.3) +
  geom_hline(
    yintercept = 0,
    linetype = "dashed"
  ) +
  labs(
    title = "Diagnóstico de residuos del modelo logístico",
    x = "Probabilidad estimada",
    y = "Residuo de Pearson"
  ) +
  theme_minimal()

Interpretación: El eje X representa la probabilidad estimada por el modelo de que una persona tenga un estado de salud no favorable, donde los valores cercanos a 0 indican una probabilidad baja y los cercanos a 1 una probabilidad alta. El eje Y representa los residuos de Pearson, que reflejan la diferencia entre los resultados observados y los esperados por el modelo. La gráfica muestra dos patrones curvos correspondientes a las categorías de salud favorable y no favorable. Los puntos más alejados de cero indican mayores discrepancias entre los resultados observados y las estimaciones, lo que permite identificar casos que podrían requerir una revisión adicional.

3.6. Gráficas interactivas en 3D

En esta sección se presentan dos gráficas interactivas para explorar la relación entre las variables seleccionadas y la probabilidad estimada por el modelo logístico.

# Preparar los datos de prueba y calcular las probabilidades
datos_3d <- prueba %>%
  dplyr::mutate(
    probabilidad = predict(
      modelo,
      newdata = prueba,
      type = "response"
    ),
    categoria = salud
  )

# Seleccionar una muestra para que las gráficas sean más claras
set.seed(123)

datos_3d <- datos_3d %>%
  dplyr::slice_sample(
    n = min(1000, nrow(datos_3d))
  )

# Primera gráfica 3D:
# Edad, horas de sueño y probabilidad estimada
plotly::plot_ly(
  data = datos_3d,
  x = ~edad,
  y = ~sueno,
  z = ~probabilidad,
  color = ~categoria,
  type = "scatter3d",
  mode = "markers",
  marker = list(size = 3),
  text = ~paste(
    "Edad:", edad,
    "<br>Horas de sueño:", sueno,
    "<br>Probabilidad estimada:",
    round(probabilidad, 3),
    "<br>Estado de salud:", categoria
  ),
  hoverinfo = "text"
) %>%
  plotly::layout(
    title = "Edad, sueño y probabilidad estimada",
    scene = list(
      xaxis = list(title = "Edad"),
      yaxis = list(title = "Horas de sueño"),
      zaxis = list(title = "Probabilidad estimada")
    )
  )

Interpretación: La gráfica muestra la distribución de las personas según su edad, horas de sueño y probabilidad estimada de presentar salud no favorable. Los puntos verdes representan a las personas con salud favorable, que se concentran principalmente en niveles bajos de probabilidad estimada. Los puntos azules representan a las personas con salud no favorable, que aparecen distribuidos en distintos niveles y con mayor presencia en probabilidades elevadas. Cada punto corresponde a una persona, y su posición permite observar cómo varían la edad y las horas de sueño entre ambos grupos. En general, se aprecia una tendencia a que las personas con salud favorable tengan probabilidades estimadas más bajas, mientras que las personas con salud no favorable tienden a presentar probabilidades más altas.

# Segunda gráfica 3D:
# Salud física, salud mental y probabilidad estimada
plotly::plot_ly(
  data = datos_3d,
  x = ~salud_fisica,
  y = ~salud_mental,
  z = ~probabilidad,
  color = ~categoria,
  type = "scatter3d",
  mode = "markers",
  marker = list(size = 3),
  text = ~paste(
    "Días con dificultades físicas:", salud_fisica,
    "<br>Días con dificultades mentales:", salud_mental,
    "<br>Probabilidad estimada:",
    round(probabilidad, 3),
    "<br>Estado de salud:", categoria
  ),
  hoverinfo = "text"
) %>%
  plotly::layout(
    title = "Salud física, salud mental y probabilidad",
    scene = list(
      xaxis = list(title = "Días con dificultades físicas"),
      yaxis = list(title = "Días con dificultades mentales"),
      zaxis = list(title = "Probabilidad estimada")
    )
  )

Interpretación: La gráfica representa la relación entre los días con dificultades de salud física, los días con dificultades de salud mental y la probabilidad estimada de presentar salud no favorable. Los puntos verdes corresponden a personas con salud favorable y se concentran principalmente alrededor de valores bajos de dificultades, aproximadamente entre 0 y 10 días, con probabilidades estimadas generalmente menores. Por su parte, los puntos azules representan a personas con salud no favorable y presentan una distribución más dispersa, con casos que alcanzan aproximadamente entre 10 y 20 días de dificultades físicas o mentales y probabilidades estimadas más elevadas. Aunque ambos grupos se superponen, se observa una tendencia a que una mayor cantidad de días con dificultades coincida con una mayor probabilidad estimada de salud no favorable.

Conclusiones y propuestas de mejora

Conclusiones y propuestas de mejora de modelo analítico

El modelo de regresión logística permitió analizar la asociación entre la edad, las horas de sueño y los indicadores de salud física y mental con la percepción general del estado de salud. Las cuatro variables presentaron asociaciones estadísticamente significativas con la probabilidad de tener un estado de salud no favorable. La edad, las dificultades de salud física y las dificultades de salud mental mostraron asociaciones positivas, mientras que las horas de sueño presentaron una asociación negativa, manteniendo constantes las demás variables.

En cuanto al desempeño predictivo, el modelo presentó una alta capacidad para identificar los casos de salud favorable, con una especificidad del 97,6 %, pero una capacidad más limitada para reconocer los casos de salud no favorable, con una sensibilidad del 39,6 %. Esto evidencia un desempeño desigual entre las dos categorías y señala la necesidad de mejorar la identificación de las personas con salud no favorable. Por tanto, la exactitud y el AUC deben interpretarse junto con la matriz de confusión, la sensibilidad y la especificidad.

El diagnóstico de residuos complementó la evaluación al mostrar diferencias entre los resultados observados y las probabilidades estimadas para algunas personas. En conjunto, los resultados indican que el modelo es útil como herramienta exploratoria, aunque su capacidad predictiva debe mejorarse antes de considerar aplicaciones prácticas.

Como propuestas de mejora se plantean:

  1. Incorporar variables adicionales de la encuesta, como enfermedades crónicas, actividad física y acceso a los servicios de salud, para evaluar si mejoran la capacidad predictiva.
  2. Comparar la regresión logística con otros algoritmos de clasificación, como el bosque aleatorio, utilizando los mismos conjuntos de entrenamiento y prueba.
  3. Evaluar diferentes umbrales de clasificación para mejorar la sensibilidad sin reducir excesivamente la especificidad.
  4. Considerar el diseño muestral y los factores de ponderación del BRFSS antes de realizar estimaciones poblacionales o formular recomendaciones para la asignación de recursos sanitarios.

En conclusión, este ejercicio demuestra la utilidad de los modelos analíticos para explorar factores asociados con la percepción del estado de salud y evaluar su capacidad de clasificación. Sin embargo, los resultados no demuestran relaciones causales ni constituyen una herramienta clínica validada. Se requieren análisis adicionales y una validación más amplia antes de utilizar el modelo como apoyo para decisiones sanitarias reales.

Referencias bibliográficas