1 Introducción

En esta activiad vamos analizar desde el punto de vista estadístico, la relación entre el hábito de fumar y variables cardiometabólicas clave (frecuencia cardíaca y colesterol), así como contrastar proporciones de eventos clínicos (taquicardia y colesterol alto) en una muestra observacional. Para ello, se implementa un flujo de trabajo reproducible en R apoyado en el ecosistema tidyverse para la limpieza, transformación y visualización, y en pruebas de hipótesis clásicas (t de una y dos muestras con corrección de Welch, pruebas de proporciones) complementadas con tamaños de efecto (Cohen’s d y Hedges’ g) que ayudan a interpretar la magnitud práctica de las diferencias más allá del valor p [1], [3].

La estrategia metodológica se estructura en: (i) depuración y estandarización de nombres y tipos de variables; (ii) exploración descriptiva y visual (histogramas, densidades, diagramas de caja y gráficos de barras), (iii) planteamiento formal de hipótesis (H0/H1), selección del contraste (bilateral/unilateral), nivel de significancia y criterios de decisión, y (iv) reporte tabular de resultados con intervalos de confianza e interpretación. Este enfoque sigue lineamientos de análisis de datos reproducibles en R orientados a ciencias de la vida y salud [1] y se fundamenta en los principios de la inferencia estadística clásica (estimación, contrastes, supuestos y potencia) descritos en la literatura estándar [3].

Además, se adopta una visión práctica de la ciencia de datos integrando limpieza, análisis, visualización y comunicación que facilita la trazabilidad y la toma de decisiones sobre evidencia cuantitativa [4]. De manera complementaria, si el estudio requiriera reducir dimensionalidad o construir indicadores compuestos, técnicas como PCA pueden incorporarse para explorar estructuras latentes en los datos, tal como discute la bibliografía especializada reciente [5]. Finalmente, se reconocen desarrollos modernos en analítica y aprendizaje (incluido deep learning) que, aunque no son el foco de este informe, enmarcan el continuo metodológico entre estadística clásica y modelos de mayor complejidad [2].

2 Preparación

Configuración del entorno estableciendo la reproducibilidad, cargando de forma automática y silenciosa todos los paquetes necesarios, definiendo un tema gráfico global para ggplot2 y fijando una paleta de colores consistente que se utilizará en todas las visualizaciones posteriores.

knitr::opts_chunk$set(warning = FALSE, message = FALSE)
set.seed(123)                                              # Fija semilla para reproducibilidad (set.seed)

pkgs <- c(                                                 # Crea vector con nombres de paquetes (c)
  "tidyverse","janitor","broom","rstatix",
  "effectsize","knitr","kableExtra","rlang","scales"
)                                                          # Lista de paquetes usados en el análisis

need <- setdiff(pkgs, rownames(installed.packages()))      # Identifica paquetes faltantes (setdiff + installed.packages + rownames)
if (length(need)) install.packages(need, dependencies=TRUE) # Instala paquetes faltantes con dependencias (install.packages)

invisible(lapply(pkgs, function(p)                         # Itera sobre paquetes y los carga (lapply)
  suppressPackageStartupMessages(                          # Suprime mensajes de arranque (suppressPackageStartupMessages)
    library(p, character.only = TRUE)                      # Carga paquete por nombre como cadena (library)
  )
))

theme_set(ggplot2::theme_minimal(base_size = 12))          # Fija tema gráfico global minimalista con texto base 12 (theme_set + theme_minimal)

# Paleta consistente para todo el informe                                                         (vectores de colores hex)
pal_groups <- c("no" = "#1f77b4", "yes" = "#d62728")       # Paleta para grupos (no/yes) (vector nombrado)
col_hr_fill  <- "#4C78A8"                                  # Color base para HR (constante hex)
col_ch_fill  <- "#F58518"                                  # Color base para colesterol (constante hex)
col_mean     <- "#2ca02c"                                  # Color para línea de media (constante hex)
col_ref1     <- "#9467bd"                                  # Color para línea de referencia 1 (constante hex)
col_ref2     <- "#e377c2"                                  # Color para línea de referencia 2 (constante hex)

3 Carga y limpieza de datos

En este bloque cargamos el archivo de datos, procedemos a estandarizar y reparar los nombres de columnas, detectar las variables claves (fumador, frecuencia cardiaca, colesterol y presión arterial), normalizar y transformar estas variables (incluyendo la separación de presión sistólica/diastólica y la creación de indicadores binarios de colesterol alto y taquicardia), eliminar filas duplicadas y generar un reporte tabular del número de valores perdidos por variable, dejando un data frame limpio (df) y un diagnóstico de calidad de datos listo para el análisis estadístico posterior.

data_path <- "smoking_health_data_final.csv"               # Define ruta del CSV (asignación de cadena)

raw <- readr::read_csv(data_path, show_col_types = FALSE)  # Lee CSV inferiendo tipos sin imprimirlos (readr::read_csv)

raw <- janitor::clean_names(raw)                           # Estandariza nombres a snake_case, válidos y únicos (janitor::clean_names)

if (any(names(raw) == "")) {                               # Verifica si hay nombres vacíos (any + names)
  idx <- which(names(raw) == "")                           # Obtiene posiciones de nombres vacíos (which)
  names(raw)[idx] <- paste0("unnamed_", idx)               # Asigna nombres provisionales únicos (paste0)
}                                                         # Cierra condición

utils::str(head(raw, 1))                                   # Muestra estructura compacta de la primera fila (utils::str + head)
## tibble [1 × 7] (S3: tbl_df/tbl/data.frame)
##  $ age           : num 54
##  $ sex           : chr "male"
##  $ current_smoker: chr "yes"
##  $ heart_rate    : num 95
##  $ blood_pressure: chr "110/72"
##  $ cigs_per_day  : num NA
##  $ chol          : num 219
pick_col <- function(d, candidates) {                      # Define función auxiliar para elegir primera columna existente (function)
  found <- intersect(candidates, names(d))                 # Interseca candidatos con nombres del data.frame (intersect + names)
  if (length(found) == 0)                                  # Evalúa si no hay coincidencias (length == 0)
    stop("No se encontró ninguna columna entre: ",         # Lanza error con mensaje descriptivo (stop)
         paste(candidates, collapse = ", "))               # Concatena candidatos en cadena (paste)
  found[1]                                                 # Devuelve la primera coincidencia (indexación)
}                                                        # Cierra función

col_smoker <- ifelse("current_smoker" %in% names(raw),     # Usa nombre canónico si existe (ifelse + %in%)
                     "current_smoker",
                     pick_col(raw, c("smoker","is_smoker","fumador")))  # O el primero que exista (pick_col)
col_hr   <- pick_col(raw, c("heart_rate","hr","frecuencia_cardiaca"))   # Detecta nombre real de HR (pick_col)
col_chol <- pick_col(raw, c("chol","cholesterol","colesterol"))         # Detecta nombre real de colesterol (pick_col)
col_bp   <- if ("blood_pressure" %in% names(raw)) "blood_pressure" else NA_character_  # Detecta presión arterial si existe (%in%)

df <- raw |>                                               # Inicia tubería con datos crudos (|>)
  dplyr::mutate(                                           # Modifica/crea variables (dplyr::mutate)
    !!col_smoker := dplyr::case_when(                      # Normaliza fumador a yes/no (case_when con tidy-eval !!)
      tolower(.data[[col_smoker]]) %in%                    # Convierte a minúsculas y compara conjunto (tolower + %in%)
        c("yes","y","1","si","sí","true") ~ "yes",         # Mapea valores afirmativos a "yes" (vector literal)
      tolower(.data[[col_smoker]]) %in%                    # Compara valores negativos (tolower + %in%)
        c("no","n","0","false") ~ "no",                    # Mapea a "no" (asignación de cadena)
      TRUE ~ as.character(.data[[col_smoker]])             # Mantiene valor original como carácter (as.character)
    ),                                                     # Cierra case_when
    !!col_smoker := factor(.data[[col_smoker]],            # Convierte a factor ordenado no/yes (factor)
                           levels = c("no","yes"))         # Define orden de niveles (levels)
  )                                                        # Cierra mutate

if (!is.na(col_bp) && col_bp %in% names(df)) {            # Si existe columna de presión arterial (is.na + %in%)
  if ("separate_wider_delim" %in% getNamespaceExports("tidyr")) {  # Usa API nueva si está disponible (getNamespaceExports)
    df <- tidyr::separate_wider_delim(                     # Separa texto "120/80" en dos columnas (separate_wider_delim)
      df,                                                  # Data.frame de entrada (df)
      cols = !!rlang::sym(col_bp),                         # Selecciona columna por nombre con símbolo (rlang::sym)
      delim = "/",                                         # Define delimitador de separación (delim)
      names = c("sbp","dbp"),                              # Nombres de salida para sistólica/diastólica (names)
      too_few = "align_start"                              # Alinea si faltan valores (too_few)
    )                                                      # Cierra llamada
    df <- dplyr::mutate(df,                                # Convierte a numéricos las nuevas columnas (mutate)
      dplyr::across(c(sbp, dbp), readr::parse_number))     # Aplica parseo numérico sobre columnas (across + parse_number)
  } else {                                               # Rama alternativa si API nueva no existe (else)
    df <- tidyr::separate(                                 # Separa texto usando API clásica (tidyr::separate)
      df, !!rlang::sym(col_bp),                            # Data y columna a separar (rlang::sym)
      into = c("sbp","dbp"),                               # Nombres de columnas resultantes (into)
      sep = "/",                                           # Delimitador de separación (sep)
      convert = TRUE,                                      # Convierte tipos cuando es posible (convert)
      remove = TRUE,                                       # Remueve la columna original (remove)
      extra = "merge",                                     # Une excedentes si sobran separadores (extra)
      fill  = "right"                                      # Rellena a la derecha si faltan valores (fill)
    )                                                      # Cierra llamada
  }                                                       # Cierra condición anidada
}                                                         # Cierra condición principal

df <- df |>                                                # Continúa tubería sobre df (|>)
  dplyr::mutate(                                           # Crea indicadores binarios (mutate)
    chol_high   = ifelse(!is.na(.data[[col_chol]]) &       # 1 si colesterol > 240 y no es NA (ifelse + !is.na)
                          .data[[col_chol]] > 240, 1L, 0L),
    tachycardia = ifelse(!is.na(.data[[col_hr]]) &         # 1 si HR > 100 y no es NA (ifelse + !is.na)
                          .data[[col_hr]] > 100, 1L, 0L)
  ) |>
  dplyr::distinct()                                        # Elimina filas duplicadas exactas (distinct)

na_report <- sapply(df, function(x) sum(is.na(x)))         # Cuenta NAs por columna (sapply + is.na + sum)

na_tbl <- tibble::tibble(                                  # Crea tabla con nombres y conteo de NAs (tibble::tibble)
  variable = names(na_report),                             # Columna de nombres de variables (names)
  na = as.integer(na_report)                               # Columna de conteo como entero (as.integer)
) |>
  dplyr::arrange(dplyr::desc(na))                          # Ordena de mayor a menor número de NAs (arrange + desc)

knitr::kable(na_tbl,                                       # Renderiza tabla en el reporte (knitr::kable)
  caption = "Conteo de valores perdidos por variable",     # Título de la tabla (caption)
  digits = 0                                               # Formato de decimales (digits)
) |>
  kableExtra::kable_styling(full_width = FALSE)            # Aplica estilo sin ancho completo (kable_styling)
Conteo de valores perdidos por variable
variable na
cigs_per_day 14
chol 7
age 0
sex 0
current_smoker 0
heart_rate 0
sbp 0
dbp 0
chol_high 0
tachycardia 0

3.1 Análisis de resultados

Calidad general de los datos

La mayoría de variables claves para el análisis (edad, sexo, condición de fumador, frecuencia cardiaca, presión sistólica/diastólica y los indicadores binarios chol_high y tachycardia) no presentan valores perdidos, lo cual es muy favorable para los contrastes de medias y proporciones.

Variables con problemas de completitud

cigs_per_day (intensidad del hábito de fumar) es la variable con mayor cantidad de datos faltantes (14 casos).

Esto implica que cualquier análisis que use “cigarrillos por día” tendrá una muestra efectiva menor y podría estar sesgado si los NAs no son aleatorios (por ejemplo, si quienes no responden son justamente los que más fuman).

chol tiene 7 valores perdidos.

Aunque el indicador derivado chol_high no tiene NAs (porque se codificó 0 cuando el valor es NA o ≤240), conviene tener presente que el análisis que use el colesterol como variable continua perderá esos 7 registros.

4 EDA — Exploración

En este bloque se realizar la exploración descriptiva inicial de la muestra, obteniendo un resumen numérico global (tamaño muestral, medias, desviaciones estándar y proporciones de colesterol alto y taquicardia) y generando visualizaciones univariadas y por grupos (histogramas, densidades y boxplots de frecuencia cardíaca y colesterol según condición de fumador, además de un gráfico de barras con las proporciones de colesterol alto y taquicardia frente a valores de referencia), para caracterizar la distribución y magnitud de las variables clave antes de aplicar las pruebas de hipótesis.

desc_tbl <- df |>                                           # Inicia resumen descriptivo (|>)
  dplyr::summarise(                                        # Calcula estadísticas agregadas (summarise)
    n         = dplyr::n(),                                # Tamaño muestral (n)
    mean_hr   = mean(.data[[col_hr]],   na.rm = TRUE),     # Media de HR ignorando NA (mean)
    sd_hr     = sd(  .data[[col_hr]],   na.rm = TRUE),     # Desviación estándar de HR (sd)
    mean_ch   = mean(.data[[col_chol]], na.rm = TRUE),     # Media de colesterol (mean)
    sd_ch     = sd(  .data[[col_chol]], na.rm = TRUE),     # Desviación estándar de colesterol (sd)
    prop_chh  = mean(chol_high,   na.rm = TRUE),           # Proporción colesterol alto (mean sobre 0/1)
    prop_tach = mean(tachycardia, na.rm = TRUE)            # Proporción taquicardia (mean sobre 0/1)
  )                                                        # Cierra summarise

knitr::kable(desc_tbl, digits = 3,                         # Muestra tabla con 3 decimales (kable)
             caption = "Resumen descriptivo") |>
  kableExtra::kable_styling(full_width = FALSE)            # Estiliza tabla sin ancho completo (kable_styling)
Resumen descriptivo
n mean_hr sd_hr mean_ch sd_ch prop_chh prop_tach
3900 75.689 12.015 236.596 44.375 0.428 0.024
g_hr_hist <- ggplot2::ggplot(df,                           # Crea objeto ggplot para HR (ggplot)
  ggplot2::aes(x = .data[[col_hr]])) +
  ggplot2::geom_histogram(bins = 30, color = "white", fill = col_hr_fill) + # Hist a color (geom_histogram + fill)
  ggplot2::geom_vline(xintercept = mean(df[[col_hr]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media en color (geom_vline)
  ggplot2::geom_vline(xintercept = 75, linetype = "dotted", color = col_ref1) + # Línea ref 75 en color (geom_vline)
  ggplot2::labs(title = "Histograma de frecuencia cardiaca", x = "lpm", y = "Frecuencia")   # Títulos (labs)

g_hr_dens <- ggplot2::ggplot(df,                           # Gráfico de densidad para HR (ggplot)
  ggplot2::aes(x = .data[[col_hr]])) +
  ggplot2::geom_density(alpha = 0.5, fill = col_hr_fill) + # Densidad rellena a color (geom_density + fill)
  ggplot2::geom_vline(xintercept = mean(df[[col_hr]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media (geom_vline)
  ggplot2::geom_vline(xintercept = 75, linetype = "dotted", color = col_ref1) + # Línea ref 75 (geom_vline)
  ggplot2::labs(title = "Densidad de frecuencia cardiaca", x = "lpm", y = "Densidad")       # Etiquetas (labs)

g_hr_box <- ggplot2::ggplot(df,                            # Boxplot de HR por fumador (ggplot)
  ggplot2::aes(x = .data[[col_smoker]], y = .data[[col_hr]], fill = .data[[col_smoker]])) +
  ggplot2::geom_boxplot(alpha = 0.8) +                     # Cajas rellenas por grupo (geom_boxplot + fill)
  ggplot2::scale_fill_manual(values = pal_groups) +        # Paleta para grupos (scale_fill_manual)
  ggplot2::geom_hline(yintercept = mean(df[[col_hr]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media global (geom_hline)
  ggplot2::labs(title = "Frecuencia cardiaca por fumador", x = "Fumador", y = "lpm", fill = "Fumador") # Etiquetas (labs)

g_ch_hist <- ggplot2::ggplot(df,                           # Histograma de colesterol (ggplot)
  ggplot2::aes(x = .data[[col_chol]])) +
  ggplot2::geom_histogram(bins = 30, color = "white", fill = col_ch_fill) + # Hist a color (geom_histogram + fill)
  ggplot2::geom_vline(xintercept = mean(df[[col_chol]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media (geom_vline)
  ggplot2::geom_vline(xintercept = 200, linetype = "dotted", color = col_ref1) + # Umbral 200 (geom_vline)
  ggplot2::geom_vline(xintercept = 240, linetype = "dotted", color = col_ref2) + # Umbral 240 (geom_vline)
  ggplot2::labs(title = "Histograma de colesterol (mg/dL)", x = "mg/dL", y = "Frecuencia")  # Etiquetas (labs)

g_ch_dens <- ggplot2::ggplot(df,                           # Densidad de colesterol (ggplot)
  ggplot2::aes(x = .data[[col_chol]])) +
  ggplot2::geom_density(alpha = 0.5, fill = col_ch_fill) + # Densidad rellena a color (geom_density + fill)
  ggplot2::geom_vline(xintercept = mean(df[[col_chol]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media (geom_vline)
  ggplot2::geom_vline(xintercept = 200, linetype = "dotted", color = col_ref1) + # Umbral 200 (geom_vline)
  ggplot2::geom_vline(xintercept = 240, linetype = "dotted", color = col_ref2) + # Umbral 240 (geom_vline)
  ggplot2::labs(title = "Densidad de colesterol", x = "mg/dL", y = "Densidad")              # Etiquetas (labs)

g_ch_box <- ggplot2::ggplot(df,                            # Boxplot de colesterol por fumador (ggplot)
  ggplot2::aes(x = .data[[col_smoker]], y = .data[[col_chol]], fill = .data[[col_smoker]])) +
  ggplot2::geom_boxplot(alpha = 0.8) +                     # Cajas rellenas por grupo (geom_boxplot + fill)
  ggplot2::scale_fill_manual(values = pal_groups) +        # Paleta para grupos (scale_fill_manual)
  ggplot2::geom_hline(yintercept = mean(df[[col_chol]], na.rm = TRUE),
                      linetype = "dashed", color = col_mean) +              # Línea media (geom_hline)
  ggplot2::labs(title = "Colesterol por fumador", x = "Fumador", y = "mg/dL", fill = "Fumador") # Etiquetas (labs)

prop_tbl <- df |>                                          # Prepara tabla de proporciones (|>)
  dplyr::summarise(                                        # Calcula proporciones globales (summarise)
    chol_high = mean(chol_high, na.rm = TRUE),             # Proporción colesterol alto (mean)
    tachycardia = mean(tachycardia, na.rm = TRUE)          # Proporción taquicardia (mean)
  ) |>
  tidyr::pivot_longer(everything(),                        # Pivotea a formato largo (pivot_longer)
                      names_to = "indicador",              # Nombre de variable de indicadores (names_to)
                      values_to = "prop")                  # Nombre de columna de proporciones (values_to)

g_props <- ggplot2::ggplot(prop_tbl,                       # Barras de proporciones (ggplot)
  ggplot2::aes(x = indicador, y = prop, fill = indicador)) +
  ggplot2::geom_col(alpha = 0.9) +                         # Barras rellenas a color (geom_col + alpha)
  ggplot2::scale_fill_manual(values = c("chol_high" = "#17becf", "tachycardia" = "#bcbd22")) + # Colores por indicador (scale_fill_manual)
  ggplot2::geom_hline(yintercept = 0.20, linetype = "dotted", color = col_ref1) + # Línea referencia 20% (geom_hline)
  ggplot2::geom_hline(yintercept = 0.05, linetype = "dashed", color = col_ref2) + # Línea referencia 5% (geom_hline)
  ggplot2::scale_y_continuous(labels = scales::percent_format(accuracy = 1)) +    # Formatea eje Y en % (percent_format)
  ggplot2::labs(title = "Proporciones observadas vs referencias", x = NULL, y = "Proporcion", fill = "Indicador") # Etiquetas (labs)

print(g_hr_hist); print(g_hr_dens); print(g_hr_box)        # Imprime gráficos de HR (print)

print(g_ch_hist); print(g_ch_dens); print(g_ch_box)        # Imprime gráficos de colesterol (print)

print(g_props)                                             # Imprime gráfico de proporciones (print)

4.1 Análisis de resultados.

4.1.1 Resumen descriptivo.

  • Tamaño muestral (n): 3900 sujetos. Es una muestra grande, lo que da buena potencia para los análisis estadísticos posteriores.

    • Frecuencia cardíaca (mean_hr = 75.689, sd_hr = 12.015):La frecuencia cardíaca promedio es de ~75.7 lpm, con una desviación estándar de ~12 lpm.

    • Esto indica que, en general, la muestra presenta una frecuencia cardíaca de reposo algo elevada pero todavía dentro de rangos habituales, con variabilidad moderada entre individuos.

    • Colesterol (mean_ch = 236.596, sd_ch = 44.375):

  • El colesterol medio es de ~236.6 mg/dL, valor cercano al umbral de 240 mg/dL usado para definir colesterol alto.

    • La desviación estándar (~44.4 mg/dL) indica una dispersión importante, con presencia de individuos tanto en rango normal como claramente elevado.
  • Proporción de colesterol alto (prop_chh = 0.428):

    • Aproximadamente el 42.8 % de los sujetos presenta colesterol alto según el punto de corte definido.

    • Esto confirma una prevalencia elevada de hipercolesterolemia en la población estudiada.

  • Proporción de taquicardia (prop_tach = 0.024):

    • Solo alrededor del 2.4 % de los individuos cumple el criterio de taquicardia.

    • En comparación con el colesterol alto, la taquicardia es un evento mucho menos frecuente.

En conclusión, en esta muestra de 3900 personas, la frecuencia cardíaca presenta valores medios moderadamente elevados pero con baja prevalencia de taquicardia, mientras que los niveles de colesterol muestran una media cercana al umbral clínico y una proporción muy alta de sujetos con colesterol elevado, lo que posiciona a la dislipidemia como el principal factor de riesgo observado en las variables analizadas.

4.1.2 Histograma de frecuencia cardíaca

El histograma de la frecuencia cardíaca muestra una distribución aproximadamente unimodal, con la mayor concentración de observaciones entre 65 y 85 latidos por minuto (lpm). La línea vertical discontinua indica la media muestral, cercana a 75 lpm, lo que sugiere que, en promedio, los sujetos presentan una frecuencia cardiaca de reposo moderadamente elevada pero dentro de rangos habituales. Se aprecia una cola hacia la derecha con pocos casos por encima de 100–110 lpm, lo que indica la presencia de algunos valores altos (posibles taquicardias), aunque representan una fracción pequeña de la muestra.

4.1.3 Densidad de frecuencia cardíaca

La curva de densidad suavizada confirma lo observado en el histograma: la distribución es casi simétrica alrededor de la media, con un pico bien definido próximo a 75 lpm. La cola derecha es ligeramente más prolongada, lo que refleja una leve asimetría positiva debida a sujetos con frecuencias cardíacas más altas. En conjunto, estos resultados indican que la media y la desviación estándar son medidas razonables para describir la variabilidad de la frecuencia cardíaca en la muestra.

4.1.4 Frecuencia cardíaca por condición de fumador

El diagrama de cajas compara la frecuencia cardíaca entre no fumadores y fumadores. Visualmente, la mediana del grupo de fumadores es ligeramente superior a la de los no fumadores, lo que sugiere que los fumadores presentan, en promedio, una frecuencia cardíaca algo más elevada. No obstante, existe un amplio solapamiento entre ambos grupos y una variabilidad considerable, con presencia de valores atípicos altos en los dos casos. La línea discontinua verde, que representa la media global, se sitúa entre ambas cajas, indicando que cualquier diferencia entre grupos deberá ser confirmada mediante las pruebas de hipótesis posteriores.

4.2 Análisis del colesterol

4.2.1 Histograma de colesterol total

El histograma del colesterol total (mg/dL) muestra una distribución unimodal con mayor concentración de observaciones entre aproximadamente 190 y 260 mg/dL. La línea discontinua verde marca la media muestral, situada en torno a 230–240 mg/dL, mientras que las líneas verticales punteadas señalan los puntos de corte clínicos de 200 y 240 mg/dL. Se observa que una proporción importante de los sujetos presenta valores por encima de 200 mg/dL y que una fracción no despreciable supera los 240 mg/dL, lo que evidencia una carga relevante de hipercolesterolemia en la población analizada.

4.2.2 Densidad de colesterol

La gráfica de densidad corrobora que la distribución del colesterol es aproximadamente normal con una ligera asimetría hacia la derecha. El máximo de densidad se sitúa algo por debajo de la media, mientras que la cola derecha se prolonga hacia valores elevados. La ubicación de los umbrales de 200 y 240 mg/dL respecto a la masa de la distribución permite visualizar que una parte considerable de la muestra se encuentra en rangos limítrofes o francamente altos de colesterol.

4.2.3 Colesterol por condición de fumador

En el boxplot que compara los niveles de colesterol entre no fumadores y fumadores, las medianas de ambos grupos aparecen muy próximas, y las cajas presentan amplitudes similares. Esto sugiere que, de forma exploratoria, el hábito de fumar no se asocia con diferencias marcadas en el colesterol total. En ambos grupos se observan varios valores atípicos altos, lo que indica una gran variabilidad intra-grupo. Nuevamente, cualquier diferencia sutil entre fumadores y no fumadores deberá evaluarse formalmente mediante los contrastes de hipótesis.

4.3 Proporciones de colesterol alto y taquicardia

4.3.1 Proporciones observadas vs referencias

El gráfico de barras resume las proporciones de sujetos con colesterol alto (chol_high) y con taquicardia (tachycardia), comparándolas con valores de referencia del 20 % y 5 %, respectivamente. Aproximadamente el 40-45 % de la muestra presenta colesterol alto, porcentaje claramente superior a la referencia del 20 %, lo que refuerza la idea de una elevada prevalencia de dislipidemia en esta población. En contraste, la proporción de taquicardia se sitúa alrededor del 2-3 %, por debajo de la referencia del 5 %, indicando que las frecuencias cardíacas francamente elevadas son relativamente poco frecuentes. En conjunto, estos resultados destacan al colesterol elevado como el principal problema cardiovascular en el conjunto de variables analizadas.

5 Pruebas de hipótesis

5.1 Media — HR = 75 (bilateral)

En este bloque vamos contrastar estadísticamente si la media poblacional de la frecuencia cardíaca (μ_HR) difiere de 75 lpm, aplicando una prueba t de una muestra bilateral con α = 0,05, calcular el tamaño de efecto (d de Cohen) frente al valor de referencia 75, presentar los resultados en una tabla formateada y emitir automáticamente la decisión sobre H₀ (“rechazar” o “no rechazar”) junto con el p-valor correspondiente.

Hipótesis
- H0: μ_HR = 75 (lpm)
- H1: μ_HR ≠ 75 (lpm)

Parámetro: μ_HR (media poblacional de frecuencia cardíaca).
Prueba: t de una muestra (bilateral).
Nivel de significancia: α = 0.05.
Criterio: si p < α ⇒ rechazar H0; en otro caso ⇒ no rechazar H0.

alpha <- 0.05                                              # Define nivel de significancia (asignación)
ht_hr <- t.test(df[[col_hr]], mu = 75,                     # Ejecuta t(1 muestra) vs 75 (t.test)
                alternative = "two.sided")
d_hr  <- effectsize::cohens_d(df[[col_hr]], mu = 75,       # Calcula d de Cohen vs 75 (cohens_d)
                              within = FALSE)

broom::tidy(ht_hr) |>                                      # Convierte salida a tibble (broom::tidy)
  dplyr::mutate(cohens_d = d_hr$Cohens_d) |>               # Añade tamaño de efecto d (mutate)
  knitr::kable(digits = 4,                                 # Formatea tabla de resultados (kable)
               caption = "t (1 muestra): HR vs 75 (bilateral)") |>
  kableExtra::kable_styling(full_width = FALSE)            # Estilo de tabla (kable_styling)
t (1 muestra): HR vs 75 (bilateral)
estimate statistic p.value parameter conf.low conf.high method alternative cohens_d
75.689 3.5809 3e-04 3899 75.3118 76.0662 One Sample t-test two.sided 0.0573
dec_hr <- ifelse(ht_hr$p.value < alpha, "Rechazar H0", "No rechazar H0") # Regla de decisión (ifelse)
paste("Decisión (α = 0.05):", dec_hr, "| p-valor =", signif(ht_hr$p.value, 4)) # Mensaje de decisión (paste + signif)
## [1] "Decisión (α = 0.05): Rechazar H0 | p-valor = 0.0003465"

5.1.1 Anáilis de resultados.

El resultado de la prueba t de una muestra para la frecuencia cardíaca se resume así:

  • Media muestral: 75.689 lpm, muy cercana al valor de referencia de 75 lpm.
  • Estadístico t: 3.5809 con gl = 3899, lo que, dado el gran tamaño muestral, produce un p-valor ≈ 0.00035, claramente menor que α = 0.05.
  • IC 95 % para μ_HR: [75.3118; 76.0662]. Este intervalo no incluye el valor 75, por lo que es coherente con el rechazo de H₀.
  • Tamaño de efecto (d de Cohen): 0.0573, lo que corresponde a un efecto trivial (muy por debajo de 0.2).

En consecuencia, se rechaza H₀ y se concluye que, estadísticamente, la media poblacional de frecuencia cardíaca es distinta de 75 lpm. Sin embargo, la diferencia estimada es de apenas ≈ 0.69 lpm por encima de 75, con un tamaño de efecto prácticamente nulo. Esto indica que, aunque la diferencia es significativa desde el punto de vista estadístico (por el gran tamaño de la muestra), no parece tener relevancia clínica o práctica.

5.2 Colesterol (una muestra, unilateral derecha)

En este paso vamos a evaluar si la media poblacional de colesterol es significativamente mayor que 200 mg/dL, aplicando una prueba t de una muestra unilateral derecha con α = 0,05, calcular el tamaño de efecto (d de Cohen) respecto a ese umbral clínico, presentar los resultados en una tabla formateada y generar automáticamente la decisión sobre H₀ (“rechazar” o “no rechazar”) junto con el p-valor correspondiente.

Hipótesis
- H0: μ_col ≤ 200 (mg/dL)
- H1: μ_col > 200 (mg/dL)

Parámetro: μ_col (media poblacional de colesterol).
Prueba: t de una muestra (unilateral derecha).
Nivel de significancia: α = 0.05.
Criterio: si p < α ⇒ rechazar H0.

alpha <- 0.05                                              # Nivel de significancia (asignación)
ht_ch <- t.test(df[[col_chol]], mu = 200,                  # t(1 muestra) vs 200 unilateral derecha (t.test)
                alternative = "greater")
d_ch  <- effectsize::cohens_d(df[[col_chol]], mu = 200,    # d de Cohen vs 200 (cohens_d)
                              within = FALSE)

broom::tidy(ht_ch) |>                        # broom::tidy() convierte el objeto de la prueba t (ht_ch),
                                             # resultado de t.test(), en un tibble ordenado con columnas como
                                             # estimate, statistic, p.value, conf.low, conf.high, etc.

  dplyr::mutate(cohens_d = d_ch$Cohens_d) |> # dplyr::mutate() agrega una nueva columna llamada 'cohens_d'
                                             # al tibble; le asigna el valor del tamaño de efecto d de Cohen
                                             # extraído del objeto d_ch (d_ch$Cohens_d).

  knitr::kable(                              # knitr::kable() transforma ese tibble enriquecido en una tabla
                                             # formateada lista para incluir en el informe (HTML, PDF, Word).
    digits  = 4,                             # digits = 4 indica que los valores numéricos se muestran
                                             # con 4 cifras decimales.
    caption = "t (1 muestra): Colesterol > 200 (unilateral)"
                                             # caption define el título o leyenda que aparecerá encima
                                             # de la tabla en el documento.
  ) |>

  kableExtra::kable_styling(full_width = FALSE)  
t (1 muestra): Colesterol > 200 (unilateral)
estimate statistic p.value parameter conf.low conf.high method alternative cohens_d
236.5959 51.4557 0 3892 235.4258 Inf One Sample t-test greater 0.8247
                                             # kableExtra::kable_styling() aplica estilos adicionales a la tabla.
                                             # full_width = FALSE evita que la tabla ocupe todo el ancho de la página,
                                             # mejorando la presentación y la legibilidad.


dec_ch <- ifelse(ht_ch$p.value < alpha, "Rechazar H0", "No rechazar H0") # Regla de decisión (ifelse)
paste("Decisión (α = 0.05):", dec_ch, "| p-valor =", signif(ht_ch$p.value, 4)) # Mensaje (paste + signif)
## [1] "Decisión (α = 0.05): Rechazar H0 | p-valor = 0"

5.2.1 Anális de resultados.

El resultado de la prueba t de una muestra para colesterol (H₀: μ_col ≤ 200; H₁: μ_col > 200) se interpreta así:

  • Media muestral (estimate): xˉ=236.60 mg/dL, es decir, en promedio los sujetos están 36.6 mg/dL por encima del valor de referencia de 200 mg/dL.

  • Estadístico t (statistic = 51.4557, gl = 3892): Es un valor extremadamente alto; indica que la diferencia entre la media observada y 200 mg/dL son muchas desviaciones estándar por encima del valor nulo.

  • p-valor ≈ 0: R redondea el p-valor a 0 (en realidad es “menor que la precisión numérica de la máquina”). Al ser un test unilateral derecha, esto implica evidencia abrumadora contra H₀. Con α = 0.05, la regla de decisión “Rechazar H0” está completamente justificada.

  • IC 95 % unilateral: [235.43;+∞) mg/dL. Incluso en el límite inferior del intervalo de confianza, la media poblacional se sitúa muy por encima de 200 mg/dL, por lo que no es plausible que la media real sea ≤ 200.

  • Tamaño de efecto (cohens_d = 0.8247): Un d ≈ 0.82 se considera un efecto grande, es decir, la diferencia entre la media observada y el umbral de 200 mg/dL no solo es estadísticamente significativa sino también relevante en términos prácticos o clínicos.

En conclusión y con base en esta prueba t unilateral, se rechaza H₀ y se concluye que la media poblacional de colesterol en la muestra es significativamente mayor que 200 mg/dL, con una diferencia amplia y clínicamente importante, lo que confirma una situación de riesgo elevado por hipercolesterolemia en la población analizada.

5.3 Colesterol alto > 240 (proporción, unilateral)

En este bloque se evalua si la proporción poblacional de personas con colesterol alto (> 240 mg/dL) es significativamente mayor al 20 %, mediante una prueba de proporciones (prop.test) unilateral derecha con corrección de continuidad y α = 0,05, calculando la proporción observada, contrastándola con el valor teórico del 20 %, presentando los resultados en una tabla formateada y generando automáticamente la decisión sobre H₀ (“rechazar” o “no rechazar”) junto con el p-valor correspondiente.

Hipótesis
- H0: p = 0.20
- H1: p > 0.20

Parámetro: p (proporción poblacional con colesterol > 240 mg/dL).
Prueba: prop.test (corrección de continuidad).
Nivel de significancia: α = 0.05.
Criterio: si p < α ⇒ rechazar H0.

alpha <- 0.05                                              # Nivel de significancia (asignación)
x_ch <- sum(df$chol_high == 1, na.rm = TRUE)               # Éxitos: col alto (sum)
n_ch <- sum(!is.na(df$chol_high))                           # Total: no NA (sum)

pt_ch <- prop.test(x = x_ch, n = n_ch, p = 0.20,           # Prueba de proporción unilateral (prop.test)
                   alternative = "greater", correct = TRUE)

broom::tidy(pt_ch) |>                        # broom::tidy() convierte el objeto de la prueba de proporciones
                                             # (pt_ch, resultado de prop.test) en un tibble ordenado con
                                             # columnas como estimate, statistic, p.value, conf.low, conf.high, etc.

  knitr::kable(                              # knitr::kable() toma ese tibble y lo transforma en una tabla
                                             # formateada para el informe (HTML, PDF o Word).
    digits  = 4,                             # digits = 4 indica que los valores numéricos se mostrarán
                                             # con 4 cifras decimales.
    caption = "Proporcion col alto vs 20 por ciento (unilateral)"
                                             # caption define el título o leyenda de la tabla que aparecerá
                                             # en el documento.
  ) |>

  kableExtra::kable_styling(full_width = FALSE)  
Proporcion col alto vs 20 por ciento (unilateral)
estimate statistic p.value parameter conf.low conf.high method alternative
0.4279 1265.116 0 1 0.4148 1 1-sample proportions test with continuity correction greater
                                             # kableExtra::kable_styling() aplica estilos adicionales a la tabla.
                                             # full_width = FALSE evita que la tabla ocupe todo el ancho de la página,
                                             # mejorando la presentación y la legibilidad.


dec_p_ch <- ifelse(pt_ch$p.value < alpha, "Rechazar H0", "No rechazar H0")  # Decisión (ifelse)
paste("Decisión (α = 0.05):", dec_p_ch, "| p-valor =", signif(pt_ch$p.value, 4)) # Mensaje (paste)
## [1] "Decisión (α = 0.05): Rechazar H0 | p-valor = 2.153e-277"

5.3.1 Análisi de resultados.

  • Hipótesis

    • H₀: p=0.20p = 0.20p=0.20 (el 20 % de la población tiene colesterol > 240 mg/dL).
    • H₁: p>0.20p > 0.20p>0.20.
  • Del resumen descriptivo previo sabemos que la proporción observada de colesterol alto (chol_high) es aproximadamente 0.428, es decir, cerca del 42.8 % de la muestra presenta colesterol > 240 mg/dL.

  • El mensaje del test indica:

    Decisión (α = 0.05): Rechazar H0 | p-valor = 2.153e-277

Esto significa que el p-valor es extremadamente pequeño (2.153×10−2772.153 ^{-277}2.153×10−277), prácticamente cero, muy por debajo del nivel de significancia α=0.05= 0.05α=0.05.

  • Conclusiones

    • Con un p-valor tan reducido, existe evidencia abrumadora para rechazar H₀.

    • Se concluye que la proporción poblacional de personas con colesterol > 240 mg/dL es significativamente mayor al 20 %.

    • La proporción observada (~42.8 %) casi duplica el valor de referencia del 20 %, con una diferencia de alrededor de 23 puntos porcentuales.

    • No solo la diferencia es estadísticamente significativa, sino que además es grande y clínicamente relevante, indicando una alta prevalencia de hipercolesterolemia en la población analizada.

5.4 Taquicardia > 100 lpm (proporción, bilateral)

En este paso vamos aevaluar si la proporción poblacional de personas con taquicardia (HR > 100 lpm) es distinta del 5 %, mediante una prueba de proporciones bilateral (prop.test) con α = 0,05, calculando la proporción observada a partir de la variable tachycardia, comparándola con el valor teórico de 0,05, presentando los resultados en una tabla formateada y generando automáticamente la decisión sobre H₀ (“rechazar” o “no rechazar”) junto con el p-valor correspondiente.

Hipótesis
- H0: p = 0.05
- H1: p ≠ 0.05

Parámetro: p (proporción poblacional con HR > 100 lpm).
Prueba: prop.test (bilateral).
Nivel de significancia: α = 0.05.
Criterio: si p < α ⇒ rechazar H0.

alpha <- 0.05                                              # Nivel de significancia (asignación)
x_ta <- sum(df$tachycardia == 1, na.rm = TRUE)             # Éxitos: taquicardia (sum)
n_ta <- sum(!is.na(df$tachycardia))                         # Total: no NA (sum)

pt_ta <- prop.test(x = x_ta, n = n_ta, p = 0.05,           # Prueba de proporción bilateral (prop.test)
                   alternative = "two.sided", correct = TRUE)

broom::tidy(pt_ta) |>                        # broom::tidy() convierte el objeto de la prueba de proporciones
                                             # (pt_ta, resultado de prop.test) en un tibble ordenado con
                                             # columnas como estimate, statistic, p.value, conf.low, conf.high, etc.

  knitr::kable(                              # knitr::kable() toma ese tibble y lo transforma en una tabla
                                             # formateada para el informe (HTML, PDF, Word).
    digits  = 4,                             # digits = 4 indica que los valores numéricos se mostrarán
                                             # con 4 decimales.
    caption = "Proporción taquicardia = 5% (bilateral)"
                                             # caption define el título o leyenda de la tabla que aparecerá
                                             # en el documento.
  ) |>

  kableExtra::kable_styling(full_width = FALSE)  
Proporción taquicardia = 5% (bilateral)
estimate statistic p.value parameter conf.low conf.high method alternative
0.0238 55.6127 0 1 0.0194 0.0293 1-sample proportions test with continuity correction two.sided
                                             # kableExtra::kable_styling() aplica estilos adicionales a la tabla.
                                             # full_width = FALSE hace que la tabla no ocupe todo el ancho
                                             # de la página, mejorando la presentación y la legibilidad.


dec_p_ta <- ifelse(pt_ta$p.value < alpha, "Rechazar H0", "No rechazar H0")  # Decisión (ifelse)
paste("Decisión (α = 0.05):", dec_p_ta, "| p-valor =", signif(pt_ta$p.value, 4)) # Mensaje (paste)
## [1] "Decisión (α = 0.05): Rechazar H0 | p-valor = 8.825e-14"

5.4.1 Análisis de resultados.

  • Proporción observada (estimate): p^​=0.0238≈2.38% Es decir, en la muestra solo ~2.4 % de las personas presenta taquicardia.

  • Intervalo de confianza 95 %: [0.0194; 0.0293], es decir, entre 1.94 % y 2.93 %. Todo el intervalo queda por debajo del 5 %, lo que ya indica que el 5 % no es un valor plausible para la proporción poblacional.

  • Estadístico de prueba: statistic = 55.6127 (chi-cuadrado con 1 g.l.), muy grande en valor absoluto, lo que refleja una discrepancia importante entre la proporción observada (2.4 %) y la teórica (5 %).

  • p-valor ≈ 8.825 × 10⁻¹⁴ Es muchísimo menor que α = 0.05, por lo que la regla automática “Rechazar H₀” es correcta.

Conclusiones

Con un p-valor tan pequeño y un IC que no contiene el 5 %, hay evidencia estadísticamente muy fuerte para rechazar H₀. Se concluye que la proporción de personas con taquicardia en la población no es del 5 %, sino significativamente distinta.

Dado que la proporción observada (~2.4 %) es claramente menor que el 5 %, la evidencia apunta a que la taquicardia es menos frecuente de lo que planteaba la hipótesis nula.

En términos prácticos:

  • Solo alrededor de 2–3 personas de cada 100 presentan HR > 100 lpm.
  • El valor de referencia del 5 % sobreestima la prevalencia real de taquicardia en esta muestra.
  • Comparado con otros factores de riesgo (como el colesterol alto, que rondaba el 40+ %), la taquicardia aparece como un evento relativamente poco frecuente en la población analizada.

5.5 Colesterol (dos muestras, bilateral)

En este paso vamos comparar los niveles de colesterol entre fumadores y no fumadores para determinar si existe una diferencia estadísticamente significativa entre sus medias poblacionales. Para ello, prepara los datos, aplica una prueba t de dos muestras de Welch (bilateral) con α = 0,05, calcula el tamaño de efecto Hedges’ g con su intervalo de confianza, presenta los resultados en una tabla formateada y genera automáticamente la decisión sobre la hipótesis nula (rechazar o no rechazar H₀) junto con el p-valor correspondiente.

Hipótesis
- H0: μ_col,fum = μ_col,no
- H1: μ_col,fum ≠ μ_col,no

Parámetro: diferencia de medias poblacionales de colesterol por condición de fumador.
Prueba: t de dos muestras (Welch, varianzas no asumidas iguales).
Nivel de significancia: α = 0.05.
Tamaño de efecto: Hedges’ g (grupos independientes).
Criterio: si p < α ⇒ rechazar H0.

alpha <- 0.05                                              # Nivel de significancia (asignación)
aux_ch <- df |>                                            # Prepara datos para t-test (|>)
  dplyr::select(grupo = .data[[col_smoker]],               # Selecciona grupo fumador (select + renombrado)
                chol  = .data[[col_chol]]) |>
  dplyr::filter(!is.na(grupo) & !is.na(chol))              # Elimina NAs de análisis (filter)

ht_ch_sm <- t.test(chol ~ grupo, data = aux_ch,            # t de 2 muestras Welch (t.test fórmula)
                   alternative = "two.sided")
g_ch_sm  <- effectsize::hedges_g(chol ~ grupo, data = aux_ch) # Tamaño de efecto Hedges g (hedges_g)

broom::tidy(ht_ch_sm) |>                     # broom::tidy() convierte el objeto de la prueba t (ht_ch_sm)
                                             # en un data frame/tibble ordenado con columnas como:
                                             # estimate, statistic, p.value, conf.low, conf.high, etc.

  dplyr::bind_cols(                          # dplyr::bind_cols() une por columnas (en paralelo)
                                             # el tibble anterior con otro tibble (el de Hedges' g),
                                             # creando una sola tabla con resultados de la prueba y del tamaño de efecto.

    g_ch_sm |>                               # g_ch_sm es el objeto con el resultado de Hedges' g
                                             # calculado por effectsize::hedges_g() para el colesterol.

      dplyr::select(                         # dplyr::select() selecciona y renombra solo las columnas
                                             # relevantes del objeto g_ch_sm que queremos añadir.
        hedges_g = Hedges_g,                 # Renombra la columna Hedges_g a hedges_g (nombre más amigable).
        ci_low   = CI_low,                   # Renombra CI_low a ci_low (límite inferior del IC del tamaño de efecto).
        ci_high  = CI_high                   # Renombra CI_high a ci_high (límite superior del IC del tamaño de efecto).
      )
  ) |>

  knitr::kable(                              # knitr::kable() convierte el tibble combinado en una tabla
                                             # formateada para su inclusión en el informe (HTML, PDF, Word).
    digits  = 4,                             # digits = 4 indica que los valores numéricos se mostrarán
                                             # con 4 cifras decimales.
    caption = "t (2 muestras): Colesterol por fumador (bilateral)"
                                             # caption define el título o leyenda de la tabla en el documento.
  ) |>

  kableExtra::kable_styling(full_width = FALSE)  
t (2 muestras): Colesterol por fumador (bilateral)
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high method alternative hedges_g ci_low ci_high
4.1391 238.6458 234.5067 2.9119 0.0036 3884.78 1.3523 6.9258 Welch Two Sample t-test two.sided 0.0933 0.0305 0.1562
                                             # kableExtra::kable_styling() aplica estilos adicionales a la tabla.
                                             # full_width = FALSE evita que la tabla ocupe todo el ancho de la página,
                                             # mejorando la legibilidad y la presentación.


dec_2s_ch <- ifelse(ht_ch_sm$p.value < alpha, "Rechazar H0", "No rechazar H0") # Decisión (ifelse)
paste("Decisión (α = 0.05):", dec_2s_ch, "| p-valor =", signif(ht_ch_sm$p.value, 4)) # Mensaje (paste)
## [1] "Decisión (α = 0.05): Rechazar H0 | p-valor = 0.003612"

5.5.1 Análisis de Resultados

  • Medias por grupo

    • estimate1 = 238.65 → colesterol medio en no fumadores.
    • estimate2 = 234.51 → colesterol medio en fumadores.
    • estimate = 4.1391 → diferencia de medias = 238.65 − 234.51 ≈ 4.14 mg/dL. Es decir, en promedio los no fumadores tienen un colesterol ligeramente más alto que los fumadores.
  • Estadístico t y p-valor

    • statistic = 2.9119, con ≈ 3885 grados de libertad (Welch).
    • p.value = 0.0036, claramente menor que α = 0.05. La regla automática da: Decisión (α = 0.05): Rechazar H₀ | p-valor = 0.003612
  • Intervalo de confianza 95 % para la diferencia

    • conf.low = 1.3523, conf.high = 6.9258. El IC queda completamente por encima de 0, lo que indica que, en la población, el colesterol medio de los no fumadores sería entre 1.35 y 6.93 mg/dL mayor que el de los fumadores.
  • Tamaño de efecto (Hedges’ g)

    • hedges_g = 0.0933 con IC 95 % [0.0305; 0.1562]. Este valor corresponde a un efecto muy pequeño (por debajo del umbral clásico de 0.2 para un efecto “pequeño”).

Conclusiones

Desde el punto de vista estadístico, se rechaza H₀ y se concluye que existe una diferencia significativa en los niveles medios de colesterol entre fumadores y no fumadores. Sin embargo, la diferencia absoluta es muy reducida (≈ 4 mg/dL) y el tamaño de efecto (Hedges’ g ≈ 0.09) es prácticamente trivial.

En términos prácticos o clínicos, estos resultados sugieren que, en esta muestra, el hecho de ser fumador o no no se asocia con una diferencia relevante en el nivel de colesterol total, lo que coincide con los boxplots donde las distribuciones de ambos grupos se superponen ampliamente.

5.6 HR (dos muestras, unilateral derecha)

En este bloque comparamos la frecuencia cardíaca media entre fumadores y no fumadores para verificar si los fumadores presentan una HR significativamente mayor, aplicando una prueba t de dos muestras de Welch unilateral derecha (μ_HR,fum > μ_HR,no) con α = 0,05, calculando el tamaño de efecto Hedges’ g con su intervalo de confianza, presentando los resultados en una tabla formateada y generando automáticamente la decisión sobre H₀ (“rechazar” o “no rechazar”) junto con el p-valor correspondiente.

Hipótesis
- H0: μ_HR,fum ≤ μ_HR,no
- H1: μ_HR,fum > μ_HR,no

Parámetro: diferencia de medias poblacionales de HR por condición de fumador.
Prueba: t de dos muestras (Welch) unilateral derecha.
Nivel de significancia: α = 0.05.
Tamaño de efecto: Hedges’ g.
Criterio: si p < α ⇒ rechazar H0.

alpha <- 0.05                                              # Nivel de significancia (asignación)
aux_hr <- df |>                                            # Prepara datos para t-test (|>)
  dplyr::select(grupo = .data[[col_smoker]],               # Selecciona grupo fumador (select)
                hr    = .data[[col_hr]]) |>
  dplyr::filter(!is.na(grupo) & !is.na(hr))                # Elimina NAs (filter)

ht_hr_sm <- t.test(hr ~ grupo, data = aux_hr,              # t de 2 muestras unilateral derecha (t.test)
                   alternative = "greater")
g_hr_sm  <- effectsize::hedges_g(hr ~ grupo, data = aux_hr) # Tamaño de efecto Hedges g (hedges_g)

broom::tidy(ht_hr_sm) |>                     # broom::tidy() convierte el objeto de la prueba t (ht_hr_sm)
                                             # en un data frame/tibble ordenado con columnas como estimate,
                                             # statistic, p.value, conf.low, conf.high, etc.

  dplyr::bind_cols(                          # dplyr::bind_cols() une por columnas (side-by-side) el tibble
                                             # anterior con otro tibble que contiene el tamaño de efecto.

    g_hr_sm |>                               # g_hr_sm es el objeto con el resultado de Hedges' g
                                             # calculado por effectsize::hedges_g().

      dplyr::select(                         # dplyr::select() elige y renombra solo las columnas relevantes
                                             # del objeto g_hr_sm para añadirlas a la tabla final.
        hedges_g = Hedges_g,                 # Renombra la columna Hedges_g a hedges_g (nombre más amigable).
        ci_low   = CI_low,                   # Renombra CI_low a ci_low (límite inferior del IC de g).
        ci_high  = CI_high                   # Renombra CI_high a ci_high (límite superior del IC de g).
      )
  ) |>

  knitr::kable(                              # knitr::kable() convierte el tibble combinado en una tabla
                                             # formateada para el reporte (HTML/PDF/Word).
    digits  = 4,                             # digits = 4 indica que los valores numéricos se muestran
                                             # con 4 decimales.
    caption = "t (2 muestras, unilateral): HR por fumador"  
                                             # caption define el título o leyenda de la tabla en el informe.
  ) |>

  kableExtra::kable_styling(full_width = FALSE)  
t (2 muestras, unilateral): HR por fumador
estimate estimate1 estimate2 statistic p.value parameter conf.low conf.high method alternative hedges_g ci_low ci_high
-1.3754 75.0076 76.383 -3.5809 0.9998 3896.421 -2.0073 Inf Welch Two Sample t-test greater -0.1146 -0.1774 -0.0518
                                             # kableExtra::kable_styling() aplica estilos adicionales a la tabla.
                                             # full_width = FALSE hace que la tabla no ocupe todo el ancho
                                             # de la página, mejorando la presentación.


dec_2s_hr <- ifelse(ht_hr_sm$p.value < alpha, "Rechazar H0", "No rechazar H0") # Decisión (ifelse)
paste("Decisión (α = 0.05):", dec_2s_hr, "| p-valor =", signif(ht_hr_sm$p.value, 4)) # Mensaje (paste)
## [1] "Decisión (α = 0.05): No rechazar H0 | p-valor = 0.9998"

5.6.1 Análisis de resultados.

  • Medias por grupo

    • estimate1 = 75.0076: frecuencia cardíaca media en el primer grupo (no fumadores).
    • estimate2 = 76.3830: frecuencia cardíaca media en el segundo grupo (fumadores).
    • estimate = -1.3754: diferencia de medias
      \[ \bar{x}_{\text{no}} - \bar{x}_{\text{yes}} \approx 75.01 - 76.38 = -1.38 \text{ lpm}. \]

    Es decir, en la muestra los fumadores tienen una frecuencia cardíaca ligeramente mayor (≈ 1.4 lpm) que los no fumadores.

  • Prueba t unilateral

    • Estadístico t: \(\,t = -3.5809\,\).
    • p-valor (unilateral, alternativa “greater” para \(\bar{x}_{\text{no}} - \bar{x}_{\text{yes}} > 0\)): \(p = 0.9998\).
    • Intervalo de confianza unilateral al 95 % para la diferencia: \[ [-2.0073;\ +\infty). \]

    El límite inferior es negativo, lo que indica que la diferencia compatible con los datos apunta a que el grupo de fumadores puede tener una HR mayor.

  • Decisión.

    Decisión (\(\alpha = 0.05\)): No rechazar \(H_0\) \(|\) p-valor = 0.9998

    Con un p-valor tan alto, no hay evidencia estadística para afirmar que la frecuencia cardíaca media del primer grupo sea mayor que la del segundo (en la parametrización de la prueba).

  • Tamaño de efecto (Hedges’ g)

    • hedges_g = -0.1146 con IC 95 %: \([-0.1774;\ -0.0518]\).

    En valor absoluto, \(|g| \approx 0.11\), lo que corresponde a un efecto muy pequeño (muy por debajo de 0.2).

Conclusiones.

  • Los resultados muestran que la diferencia de frecuencia cardíaca entre fumadores y no fumadores es muy pequeña y de magnitud trivial: ≈ 1.4 lpm de diferencia, con un tamaño de efecto prácticamente nulo.

  • La prueba unilateral, tal como está parametrizada, concluye “No rechazar \(H_0\): no se encuentra evidencia estadística de que la frecuencia cardíaca media del grupo tomado como referencia en la prueba sea mayor.

  • En términos prácticos o clínicos, la condición de fumador/no fumador no se asocia con una diferencia relevante en la frecuencia cardíaca en esta muestra, lo que coincide con la fuerte superposición observada en los boxplots.

6 Conclusiones generales

6.1 Sobre el proceso de análisis

  • Se realizó un flujo completo de análisis estadístico: carga y depuración de datos, normalización de variables (especialmente la condición de fumador), separación de la presión arterial en sistólica/diastólica, creación de indicadores binarios de interés (colesterol alto y taquicardia), control de valores perdidos y eliminación de duplicados.
  • La calidad de los datos resultó adecuada para el análisis: las variables clave (frecuencia cardíaca, colesterol, presión arterial, condición de fumador y los indicadores binarios) no presentan valores perdidos relevantes, por lo que los resultados se basan en un volumen muestral grande y estable.
  • Se realizaron tanto estadísticos descriptivos (tablas, histogramas, densidades, boxplots) como pruebas de hipótesis formales (t de una y dos muestras, pruebas de proporciones), incorporando además tamaños de efecto (d de Cohen y Hedges’ g), lo que permite separar claramente significancia estadística de relevancia práctica.

6.2 Frecuencia cardíaca (HR)

  • La frecuencia cardíaca media fue de aproximadamente 75.7 lpm, con una desviación estándar cercana a 12 lpm. La distribución es prácticamente unimodal, con ligera cola hacia la derecha y pocos casos de taquicardia.
  • La prueba t de una muestra frente a 75 lpm arrojó un p-valor muy pequeño y permitió rechazar la hipótesis nula de igualdad exacta. Sin embargo, la diferencia estimada es de solo ≈ 0.7 lpm y el tamaño de efecto (d de Cohen) es trivial, por lo que no se aprecia una diferencia clínicamente relevante frente a ese valor de referencia.
  • La proporción de sujetos con taquicardia (HR > 100 lpm) fue de solo 2–3 %. La prueba de proporciones mostró que esta proporción es significativamente distinta y menor que el 5 % propuesto, lo que indica que la taquicardia es un evento poco frecuente en la muestra.

6.3 Colesterol

  • El colesterol total presentó una media alrededor de 236–237 mg/dL, con una desviación estándar cercana a 44 mg/dL. La distribución es aproximadamente normal, pero desplazada hacia valores relativamente altos.
  • La prueba t de una muestra frente a 200 mg/dL mostró un p-valor prácticamente nulo y un tamaño de efecto grande, indicando que la media poblacional de colesterol es claramente superior a 200 mg/dL. Esta diferencia es tanto estadísticamente significativa como clínicamente importante.
  • La proporción de personas con colesterol alto (> 240 mg/dL) fue de alrededor de 42–43 %. La prueba de proporciones frente a un 20 % de referencia mostró una diferencia muy significativa y de gran magnitud, lo que evidencia una elevada carga de hipercolesterolemia en la población analizada.

6.4 Comparación entre fumadores y no fumadores

  • Para el colesterol, la comparación entre fumadores y no fumadores mediante t de dos muestras (Welch) mostró diferencias estadísticamente significativas: los no fumadores presentan, en promedio, unos 4 mg/dL más de colesterol que los fumadores. No obstante, el tamaño de efecto (Hedges’ g ≈ 0.09) es muy pequeño, por lo que la diferencia carece de relevancia práctica.
  • Para la frecuencia cardíaca, los fumadores presentan una media ligeramente mayor que los no fumadores (≈ 1.4 lpm de diferencia). Sin embargo, la prueba t unilateral, tal como fue parametrizada, no aporta evidencia para sostener que la HR media del grupo de referencia sea mayor, y el tamaño de efecto también es trivial.
  • En conjunto, aunque algunas diferencias entre fumadores y no fumadores resultan estadísticamente significativas debido al gran tamaño muestral, los tamaños de efecto son muy pequeños, por lo que no se observa una influencia fuerte del estado de fumador en los niveles de colesterol o en la frecuencia cardíaca en esta base de datos.

6.5 Síntesis e implicaciones

  • El principal hallazgo del análisis es la alta prevalencia de colesterol elevado, tanto a nivel de media poblacional (muy por encima de 200 mg/dL) como de proporción de casos con colesterol > 240 mg/dL, lo que sugiere un importante problema de riesgo cardiovascular en la muestra.
  • En contraste, la taquicardia es poco frecuente y la frecuencia cardíaca media se mantiene en un rango cercano a 75 lpm, por lo que, dentro de este conjunto de variables, el colesterol se configura como el factor más preocupante.
  • El caso de la frecuencia cardíaca frente a 75 lpm y las diferencias entre fumadores y no fumadores ilustra la importancia de no confundir significancia estadística con relevancia clínica: con muestras grandes, diferencias muy pequeñas pueden resultar “significativas” sin ser relevantes en la práctica.
  • Finalmente, el ejercicio muestra un uso completo y coherente del ciclo de análisis: preprocesamiento, descripción, contrastes de hipótesis y tamaños de efecto, lo que permite una interpretación equilibrada de los datos desde el punto de vista estadístico y aplicado.

7 Bibliografía

[1] R. A. Irizarry and M. I. Love, Data Analysis for the Life Sciences with R. Boca Raton, FL, USA: CRC Press, Taylor & Francis Group, 2017. Recuperado de: Catálogo SIBBILA 201682.

[2] R. Pino Mejías and R. Beltrán Barba, Modelos de Aprendizaje Profundo. Aplicaciones con R y Python (Trabajo Fin de Grado inédito). Sevilla, España: Universidad de Sevilla, 2022. Disponible: https://hdl.handle.net/11441/142752

[3] G. C. Canavos, Probabilidad y estadística: Aplicaciones y métodos, trad. E. G. Urbina Medal; rev. G. J. Valencia Medal. Ciudad de México, México: McGraw-Hill, 1987. Recuperado de: Catálogo SIBBILA, sibila.14304.

[4] J. García, J. M. Molina López, J. García Herrero, A. Berlanga de Jesús, M. Á. Patricio Guisado, Á. L. Bustamante, y W. R. Padilla, Ciencia de datos: Técnicas analíticas y aprendizaje estadístico, un enfoque práctico. Bogotá, Colombia: Alfaomega Colombiana; Altaria Publicaciones, 2018. Recuperado de: Catálogo SIBBILA, sibbila.138355.

[5] S. A. Pernice, “El problema de la reducción dimensional. Análisis de Componentes Principales (PCA),” Revista Mutis, vol. 14, no. 1, pp. 1–21, 2024. doi: 10.21789/22561498.2057. Recuperado de: Catálogo SIBBILA.