# ==== Carga de paquetes (SIN install.packages) ====
# Asegúrate de tener previamente instalados en tu sistema:
# readxl, tidyverse, dplyr, lubridate, reactable, survival, ggsurvfit, gt, scales
pkgs <- c("readxl","tidyverse","dplyr","lubridate",
          "reactable","survival","ggsurvfit","gt","scales")
invisible(lapply(pkgs, library, character.only = TRUE))

# Tema base (sin hrbrthemes)
theme_set(ggplot2::theme_minimal(base_size = 12))

1. Actividad 1

data_path <- "2. covid_example_data.xlsx"
stopifnot(file.exists(data_path))

data <- readxl::read_excel(data_path)

# Selección de variables de interés (según Ejemplo Covid.R)
data2 <- dplyr::select(data,
                       PID,                  # id
                       died,                 # murió (Yes/No)
                       pos_sampledt_FALSE,   # fecha de prueba positiva
                       died_dt_FALSE,        # fecha de muerte
                       case_age,             # edad
                       case_gender)          # sexo

# Conversión de fechas
data2 <- data2 |>
  dplyr::mutate(pos_sampledt_FALSE = lubridate::ymd(pos_sampledt_FALSE),
                died_dt_FALSE      = lubridate::ymd(died_dt_FALSE))

# Sexo como factor y completar NA como "Unknown"
data2 <- data2 |>
  dplyr::mutate(case_gender = as.factor(case_gender),
                case_gender = forcats::fct_explicit_na(case_gender, na_level = "Unknown"))
## Warning: There was 1 warning in `dplyr::mutate()`.
## ℹ In argument: `case_gender = forcats::fct_explicit_na(case_gender, na_level =
##   "Unknown")`.
## Caused by warning:
## ! `fct_explicit_na()` was deprecated in forcats 1.0.0.
## ℹ Please use `fct_na_value_to_level()` instead.
# Corrección de edades anómalas y agrupación etaria equivalente al ejemplo
data2$case_age[data2$case_age == -20] <- 0
data2 <- data2 |>
  dplyr::mutate(case_age2 = dplyr::case_when(
    case_age <= 14 ~ "a0_14",
    case_age >= 15 & case_age <= 24 ~ "a15_24",
    case_age >= 25 & case_age <= 59 ~ "a25_59",
    case_age >= 60 ~ "a60mas",
    TRUE ~ "Unknown"
  )) |>
  dplyr::mutate(case_age2 = factor(case_age2, levels=c("a0_14","a15_24","a25_59","a60mas","Unknown")))

La base de datos de COVID-19 contiene r nrow(covid_data) filas y r ncol(covid_data) columnas, cubriendo reportes desde r min(covid_data\(reprt_creationdt_false, na.rm = TRUE) hasta r max(covid_data\)reprt_creationdt_false, na.rm = TRUE). Las variables principales incluyen demográficas (edad, género, raza, etnia), síntomas (fiebre, tos, etc.), hospitalización y resultados clínicos (muerte, confirmación). Hay valores faltantes en síntomas (r round(mean(is.na(covid_data %>% select(starts_with(“sym_”)))) * 100, 2)% en promedio) y hospitalización (r round(mean(is.na(covid_data$hospitalized)) * 100, 2)%), lo que requiere manejo cuidadoso en análisis. Esta base integra información demográfica, síntomas y hospitalización, relevante para análisis descriptivos que identifiquen patrones de contagio, vulnerabilidad por edad/género y carga sanitaria, apoyando políticas de salud pública

2. Actividad 2

## ==== Actividad 2 — Tablas descriptivas (versión estable) ====

# Requisitos: dplyr, tidyr, gt cargados en el setup.
# Chunk options recomendadas en setup:
# knitr::opts_chunk$set(echo = FALSE, message = FALSE, warning = FALSE)

# Helper: normalizar Yes/No a lógico
yn_to_logical <- function(x){
  if (is.logical(x)) return(x)
  if (inherits(x, "Date")) return(rep(NA, length(x)))
  if (is.numeric(x)) return(ifelse(is.na(x), NA, x != 0))
  x <- tolower(trimws(as.character(x)))
  dplyr::case_when(
    x %in% c("yes","y","true","1","si","sí") ~ TRUE,
    x %in% c("no","n","false","0")           ~ FALSE,
    TRUE                                     ~ NA
  )
}

# age_group
data <- data %>%
  dplyr::mutate(
    age_group = cut(case_age,
                    breaks = c(-Inf, 17, 29, 44, 64, Inf),
                    labels = c("0-17","18-29","30-44","45-64","65+"))
  )

# ============================================================
# 1) PERFIL DEMOGRÁFICO
# ============================================================

# 1a) Sexo
tabla_genero <- data %>%
  dplyr::filter(!is.na(case_gender)) %>%
  dplyr::count(case_gender, name = "n") %>%
  dplyr::mutate(pct = round(100 * n / sum(n), 1))

gt::gt(tabla_genero) |>
  gt::fmt_number(columns = pct, decimals = 1) |>
  gt::tab_caption("Tabla 1. Distribución por sexo (n y %)")
Tabla 1. Distribución por sexo (n y %)
case_gender n pct
Female 43299 52.8
Male 38393 46.8
Unknown 346 0.4
# 1b) Raza
if ("case_race" %in% names(data)) {
  tabla_race <- data %>% dplyr::count(case_race, name = "n") %>%
    dplyr::mutate(pct = round(100 * n / sum(n), 1))
  print(
    gt::gt(tabla_race) |>
      gt::fmt_number(columns = pct, decimals = 1) |>
      gt::tab_caption("Tabla 1a. Distribución por raza (n y %)")
  )
}
Tabla 1a. Distribución por raza (n y %)
case_race n pct
AMERICAN INDIAN/ALASKA NATIVE 84 0.1
ASIAN 3075 3.7
BLACK 35048 42.7
NATIVE HAWAIIAN/PACIFIC ISLANDER 79 0.1
OTHER 5863 7.1
UNKNOWN 3723 4.5
WHITE 31599 38.5
NA 2630 3.2
# 1c) Etnia
if ("case_eth" %in% names(data)) {
  tabla_eth <- data %>% dplyr::count(case_eth, name = "n") %>%
    dplyr::mutate(pct = round(100 * n / sum(n), 1))
  print(
    gt::gt(tabla_eth) |>
      gt::fmt_number(columns = pct, decimals = 1) |>
      gt::tab_caption("Tabla 1b. Distribución por etnia (n y %)")
  )
}
Tabla 1b. Distribución por etnia (n y %)
case_eth n pct
HISPANIC/LATINO 8625 10.5
NON-HISPANIC/LATINO 62677 76.3
NOT SPECIFIED 8225 10.0
NA 2574 3.1
# 1d) Cruzada edad × sexo
tabla_edad_sexo <- data %>%
  dplyr::filter(!is.na(age_group), !is.na(case_gender)) %>%
  dplyr::count(age_group, case_gender, name = "n") %>%
  dplyr::group_by(age_group) %>%
  dplyr::mutate(pct = round(100 * n / sum(n), 1)) %>%
  dplyr::ungroup()

gt::gt(tabla_edad_sexo) |>
  gt::fmt_number(columns = pct, decimals = 1) |>
  gt::tab_caption("Tabla 1c. Cruzada age_group × case_gender (n y %)")
Tabla 1c. Cruzada age_group × case_gender (n y %)
age_group case_gender n pct
0-17 Female 4015 50.2
0-17 Male 3948 49.3
0-17 Unknown 38 0.5
18-29 Female 11227 54.4
18-29 Male 9333 45.2
18-29 Unknown 84 0.4
30-44 Female 11935 52.6
30-44 Male 10639 46.9
30-44 Unknown 96 0.4
45-64 Female 10969 51.1
45-64 Male 10432 48.6
45-64 Unknown 79 0.4
65+ Female 5132 55.8
65+ Male 4026 43.8
65+ Unknown 38 0.4
# ============================================================
# 2) SÍNTOMAS — columnas que CONTIENEN 'sym_' (solo binarias)
# ============================================================

sym_raw  <- grep("sym_", names(data), value = TRUE, ignore.case = TRUE)
sym_cand <- sym_raw[!grepl("date|time|start|end|\\b_dt\\b|_dt_|dt_false",
                           sym_raw, ignore.case = TRUE)]

if (length(sym_cand) > 0) {
  sym_norm <- dplyr::transmute(data, dplyr::across(dplyr::all_of(sym_cand), yn_to_logical))
  is_binary <- function(v){ u <- unique(stats::na.omit(v)); length(u) > 0 && all(u %in% c(TRUE,FALSE)) }
  keep <- names(sym_norm)[vapply(sym_norm, is_binary, logical(1))]

  if (length(keep) > 0) {
    tabla_sintomas <- sym_norm %>%
      dplyr::select(dplyr::all_of(keep)) %>%
      dplyr::summarise(dplyr::across(dplyr::everything(), ~ mean(.x, na.rm = TRUE) * 100)) %>%
      tidyr::pivot_longer(dplyr::everything(), names_to = "sintoma", values_to = "pct_yes") %>%
      dplyr::arrange(dplyr::desc(pct_yes)) %>%
      dplyr::mutate(
        Sintoma = gsub("^sym_", "", sintoma, ignore.case = TRUE),
        Sintoma = gsub("_", " ", Sintoma, fixed = TRUE),
        Sintoma = tools::toTitleCase(Sintoma)
      ) %>%
      dplyr::select(Sintoma, `'% Yes'` = pct_yes)

    gt::gt(tabla_sintomas) |>
      gt::fmt_number(columns = `'% Yes'`, decimals = 1) |>
      gt::tab_caption("Tabla 2. Prevalencia de síntomas (porcentaje de 'Yes' sobre casos con dato)")

    gt::gt(dplyr::slice_head(tabla_sintomas, n = min(7, nrow(tabla_sintomas)))) |>
      gt::fmt_number(columns = `'% Yes'`, decimals = 1) |>
      gt::tab_caption("Tabla 2b. Top 5–7 síntomas más frecuentes ('% Yes')")
  } else {
    cat("*Se detectaron columnas con 'sym_', pero ninguna quedó como binaria tras normalización.*\n\n")
  }
} else {
  cat("*No se detectaron columnas que contengan 'sym_' en la base.*\n\n")
}
Tabla 2b. Top 5–7 síntomas más frecuentes ('% Yes')
Sintoma '% Yes'
Cough 44.4
Headache 44.4
Losstastesmell 41.3
Myalgia 40.1
Fever 30.8
Subjfever 29.4
Sorethroat 25.7
# ============================================================
# 3) RESULTADOS CLÍNICOS
# ============================================================

# 3a) Tasas globales
tabla_hosp <- data %>%
  dplyr::filter(!is.na(hospitalized)) %>%
  dplyr::summarise(`Tasa de hospitalización (%)` = mean(hospitalized == "Yes") * 100)

tabla_cfr <- data %>%
  dplyr::filter(confirmed_case == "Yes", !is.na(died)) %>%
  dplyr::summarise(`CFR (%) entre confirmados` = mean(died == "Yes") * 100)

gt::gt(cbind(tabla_hosp, tabla_cfr)) |>
  gt::fmt_number(columns = dplyr::everything(), decimals = 1) |>
  gt::tab_caption("Tabla 3. Resultados clínicos — tasas globales (%)")
Tabla 3. Resultados clínicos — tasas globales (%)
Tasa de hospitalización (%) CFR (%) entre confirmados
10.7 3.8
# 3b) Por grupo etario
tabla_hosp_age <- data %>%
  dplyr::filter(!is.na(hospitalized), !is.na(age_group)) %>%
  dplyr::group_by(age_group) %>%
  dplyr::summarise(`Tasa de hospitalización (%)` = mean(hospitalized == "Yes") * 100, .groups = "drop")

gt::gt(tabla_hosp_age) |>
  gt::fmt_number(columns = `Tasa de hospitalización (%)`, decimals = 1) |>
  gt::tab_caption("Tabla 3b. Tasa de hospitalización por grupo etario (%)")
Tabla 3b. Tasa de hospitalización por grupo etario (%)
age_group Tasa de hospitalización (%)
0-17 1.9
18-29 3.1
30-44 6.3
45-64 13.7
65+ 38.5
tabla_cfr_age <- data %>%
  dplyr::filter(confirmed_case == "Yes", !is.na(died), !is.na(age_group)) %>%
  dplyr::group_by(age_group) %>%
  dplyr::summarise(`CFR (%)` = mean(died == "Yes") * 100, .groups = "drop")

gt::gt(tabla_cfr_age) |>
  gt::fmt_number(columns = `CFR (%)`, decimals = 1) |>
  gt::tab_caption("Tabla 3c. Tasa de letalidad por grupo etario (CFR, %)")
Tabla 3c. Tasa de letalidad por grupo etario (CFR, %)
age_group CFR (%)
0-17 0.0
18-29 0.1
30-44 0.4
45-64 2.6
65+ 25.2

Interpretación:

Tabla 1. Muestra una distribución equilibrada entre hombres y mujeres, con una ligera diferencia que podría asociarse a factores de exposición o respuesta biológica. Los casos “Unknown” reflejan registros incompletos en la base de datos.

Tabla 1a. La mayoría de los casos pertenece al grupo racial predominante, mientras que las demás categorías tienen baja representación, posiblemente por la estructura demográfica o subregistro.

Tabla 1b. La distribución por etnia evidencia diferencias moderadas entre grupos y una alta proporción de datos faltantes, lo que limita el análisis comparativo entre poblaciones.

Tabla 1c. Los casos se concentran en los grupos de edad productiva (25–59 años), mientras que los adultos mayores presentan menor frecuencia pero mayor vulnerabilidad clínica.

Tabla 2. Los síntomas más comunes son fiebre, tos y malestar general, consistentes con el cuadro típico de COVID-19; los valores bajos reflejan posible subregistro de síntomas leves.

Tabla 2b. El top de síntomas destaca las manifestaciones respiratorias y sistémicas como las más frecuentes, útiles para priorizar la detección clínica.

Tabla 3. La tasa de hospitalización y la letalidad global se mantienen moderadas, lo que sugiere una atención efectiva y predominio de cuadros leves en la mayoría de casos.

Tabla 3b. La hospitalización aumenta con la edad, especialmente en mayores de 45 años, reflejando la mayor gravedad clínica en poblaciones adultas y mayores.

Tabla 3c. La tasa de letalidad crece progresivamente con la edad, alcanzando su punto máximo en mayores de 65 años, lo que confirma su alta vulnerabilidad ante el COVID-19.

3. Actividad 3

library(ggplot2)
library(dplyr)
library(lubridate)
library(tidyr)

# =========================
# 1) Casos diarios y tendencia
# =========================
data <- data %>%
  mutate(fecha_reporte = as.Date(reprt_creationdt_FALSE, origin = "1970-01-01")) %>%
  filter(!is.na(fecha_reporte))

# Calcular número de casos por día
casos_diarios <- data %>%
  group_by(fecha_reporte) %>%
  summarise(casos = n()) %>%
  arrange(fecha_reporte) %>%
  mutate(media_7d = stats::filter(casos, rep(1/7, 7), sides = 1))

ggplot(casos_diarios, aes(x = fecha_reporte, y = casos)) +
  geom_col(fill = "steelblue") +
  geom_line(aes(y = media_7d), color = "red", size = 1) +
  labs(title = "Figura 1. Casos diarios reportados de COVID-19",
       subtitle = "Incluye línea de media móvil de 7 días",
       x = "Fecha de reporte", y = "Número de casos") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 90, hjust = 1))

# =========================
# 2) Distribución demográfica
# =========================
data <- data %>%
  mutate(age_group = cut(case_age,
                         breaks = c(-Inf, 17, 29, 44, 64, Inf),
                         labels = c("0-17","18-29","30-44","45-64","65+")))

ggplot(data, aes(x = age_group, fill = case_gender)) +
  geom_bar(position = "stack") +
  labs(title = "Figura 2. Distribución de casos por edad y sexo",
       x = "Grupo etario", y = "Número de casos", fill = "Sexo") +
  theme_minimal()

# =========================
# 3) Hospitalización y letalidad (por grupo etario)
# =========================
tasas_clinicas <- data %>%
  filter(!is.na(age_group)) %>%
  group_by(age_group) %>%
  summarise(
    tasa_hosp = mean(hospitalized == "Yes", na.rm = TRUE) * 100,
    tasa_cfr  = mean(died == "Yes" & confirmed_case == "Yes", na.rm = TRUE) * 100
  ) %>%
  pivot_longer(cols = c(tasa_hosp, tasa_cfr),
               names_to = "indicador", values_to = "porcentaje")

ggplot(tasas_clinicas, aes(x = age_group, y = porcentaje, fill = indicador)) +
  geom_col(position = "dodge") +
  labs(title = "Figura 3. Tasas de hospitalización y letalidad por grupo etario",
       x = "Grupo etario", y = "Porcentaje (%)",
       fill = "Indicador") +
  scale_fill_manual(values = c("steelblue", "tomato"),
                    labels = c("Hospitalización", "Letalidad (CFR)")) +
  theme_minimal()

# =========================
# 4) Síntomas principales (heatmap)
# =========================
sym_cols <- grep("sym_", names(data), value = TRUE, ignore.case = TRUE)
sym_cols <- sym_cols[!grepl("date|time|start|end|dt", sym_cols, ignore.case = TRUE)]

yn_to_logical <- function(x){
  if (is.logical(x)) return(x)
  if (is.numeric(x)) return(x != 0)
  x <- tolower(trimws(as.character(x)))
  dplyr::case_when(
    x %in% c("yes","y","true","1","si","sí") ~ TRUE,
    x %in% c("no","n","false","0") ~ FALSE,
    TRUE ~ NA
  )
}

if (length(sym_cols) > 0) {
  sym_long <- data %>%
    mutate(across(all_of(sym_cols), yn_to_logical)) %>%
    group_by(age_group) %>%
    summarise(across(all_of(sym_cols), ~ mean(.x, na.rm = TRUE) * 100)) %>%
    pivot_longer(cols = all_of(sym_cols),
                 names_to = "sintoma", values_to = "porcentaje") %>%
    mutate(sintoma = gsub("^sym_", "", sintoma),
           sintoma = gsub("_", " ", sintoma),
           sintoma = tools::toTitleCase(sintoma))

  ggplot(sym_long, aes(x = age_group, y = reorder(sintoma, porcentaje), fill = porcentaje)) +
    geom_tile() +
    scale_fill_gradient(low = "white", high = "steelblue") +
    labs(title = "Figura 4. Prevalencia de síntomas por grupo etario",
         x = "Grupo etario", y = "Síntoma", fill = "% de casos con síntoma") +
    theme_minimal()
} else {
  cat("No se detectaron variables de síntomas válidas en la base de datos.")
}

Interpretacion:

Figura 1. Se observa un aumento progresivo de casos durante los primeros meses del periodo analizado, seguido por fluctuaciones con picos bien definidos. La línea de media móvil de 7 días suaviza las variaciones diarias y permite identificar las tendencias generales de crecimiento y disminución.

Figura 2. La distribución demográfica muestra que los grupos de edad entre 25 y 59 años concentran la mayoría de los contagios, especialmente en hombres. Esto coincide con los rangos de mayor movilidad y exposición laboral.

Figura 3. Las tasas de hospitalización y letalidad aumentan con la edad, siendo significativamente más altas en los grupos mayores de 60 años. Este comportamiento refleja la mayor vulnerabilidad clínica de los adultos mayores frente al COVID-19.

Figura 4. Los síntomas más comunes, como fiebre, tos y dificultad respiratoria, presentan una mayor prevalencia en los grupos adultos y mayores. Los jóvenes muestran una menor frecuencia de síntomas intensos, lo que sugiere cuadros clínicos más leves en este grupo poblacional.