Capítulo 2 ETL / Preprocesamiento

En esta sección se describen todas las transformaciones realizadas sobre los datos originales antes del análisis exploratorio.

2.1 Carga de datos

library(readr)
library(tidyverse)
## Warning: package 'tidyverse' was built under R version 4.4.3
## Warning: package 'ggplot2' was built under R version 4.4.2
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ purrr     1.0.2
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(janitor)
## Warning: package 'janitor' was built under R version 4.4.3
## 
## Adjuntando el paquete: 'janitor'
## 
## The following objects are masked from 'package:stats':
## 
##     chisq.test, fisher.test
library(lubridate)
library(scales)
## 
## Adjuntando el paquete: 'scales'
## 
## The following object is masked from 'package:purrr':
## 
##     discard
## 
## The following object is masked from 'package:readr':
## 
##     col_factor
library(kableExtra)
## 
## Adjuntando el paquete: 'kableExtra'
## 
## The following object is masked from 'package:dplyr':
## 
##     group_rows
library(DT)
## Warning: package 'DT' was built under R version 4.4.3
df <- readr::read_delim(
  file = "data/covid_colombia2.csv",   # <-- cambia esta ruta a la ubicación local de tu CSV
  delim = ";",
  locale = locale(encoding = "UTF-8"),
  show_col_types = FALSE
)

# Eliminar filas totalmente vacías
df <- df %>%
  filter(if_any(everything(), ~ !is.na(.)))

df <- clean_names(df)

df <- df[rowSums(is.na(df)) != ncol(df), ]

Nota: Coloca el archivo covid_colombia2.csv dentro de la carpeta data/ antes de renderizar el libro.

2.2 Operacionalización de variables

La siguiente tabla describe cada variable de la base de datos, su tipo, rol en el análisis y cómo será utilizada:

tabla_variables <- tibble(
  Variable = c(
    "id_de_caso","fecha_de_notificacion","codigo_divipola_departamento",
    "nombre_departamento","codigo_divipola_municipio","nombre_municipio",
    "edad","unidad_medida_edad","sexo","tipo_de_contagio","ubicacion",
    "estado","pais_de_procedencia","fecha_de_inicio_de_sintomas",
    "fecha_de_diagnostico","fecha_de_recuperacion","tipo_de_recuperacion",
    "fecha_de_muerte","fecha_de_reporte_web","fuente_tipo_contagio",
    "pertenencia_etnica","nombre_grupo_etnico","clasificacion_final"
  ),
  Tipo = c(
    "Numérica","Fecha","Numérica","Categórica","Numérica","Categórica",
    "Numérica","Categórica","Categórica","Categórica","Categórica",
    "Categórica","Categórica","Fecha","Fecha","Fecha","Categórica",
    "Fecha","Fecha","Categórica","Categórica","Categórica","Categórica"
  ),
  Descripcion = c(
    "Identificador único del caso confirmado",
    "Fecha de notificación del caso",
    "Código DANE del departamento",
    "Nombre del departamento",
    "Código DANE del municipio",
    "Nombre del municipio",
    "Edad del paciente",
    "Unidad de medida de la edad",
    "Sexo del paciente",
    "Tipo de contagio",
    "Ubicación del paciente",
    "Estado clínico del paciente",
    "País de procedencia",
    "Fecha de inicio de síntomas",
    "Fecha de diagnóstico",
    "Fecha de recuperación",
    "Tipo de recuperación",
    "Fecha de fallecimiento",
    "Fecha de reporte en web",
    "Fuente del contagio",
    "Pertenencia étnica",
    "Nombre del grupo étnico",
    "Clasificación final del caso"
  ),
  Rol = c(
    "Identificador","Temporal","Geográfica","Geográfica","Geográfica","Geográfica",
    "Explicativa","Explicativa","Explicativa","Explicativa","Explicativa",
    "Explicativa","Explicativa","Temporal","Temporal","Temporal","Explicativa",
    "Respuesta","Temporal","Explicativa","Explicativa","Explicativa","Explicativa"
  ),
  Operacionalizacion = c(
    "No se utiliza en análisis",
    "Análisis temporal de reportes",
    "Agrupación geográfica",
    "Análisis regional",
    "Agrupación geográfica",
    "Análisis regional",
    "Variable cuantitativa directa",
    "Normalización de edad a años",
    "Clasificación categórica",
    "Clasificación del origen del contagio",
    "Estado de atención médica",
    "Variable de estado clínico",
    "Identificación de casos importados",
    "Cálculo de tiempos de recuperación",
    "Seguimiento del diagnóstico",
    "Cálculo de días de recuperación (variable respuesta)",
    "Análisis del tipo de recuperación",
    "Variable para análisis de mortalidad",
    "Seguimiento temporal",
    "Análisis del origen del contagio",
    "Análisis poblacional",
    "Detalle étnico",
    "Clasificación epidemiológica"
  )
)

tabla_variables %>%
  kable(
    caption = "Tabla de operacionalización de variables - COVID-19 Colombia",
    align = "c"
  ) %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed", "responsive"),
    full_width = TRUE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE) %>%
  column_spec(1, bold = TRUE)
Table 2.1: Table 2.2: Tabla de operacionalización de variables - COVID-19 Colombia
Variable Tipo Descripcion Rol Operacionalizacion
id_de_caso Numérica Identificador único del caso confirmado Identificador No se utiliza en análisis
fecha_de_notificacion Fecha Fecha de notificación del caso Temporal Análisis temporal de reportes
codigo_divipola_departamento Numérica Código DANE del departamento Geográfica Agrupación geográfica
nombre_departamento Categórica Nombre del departamento Geográfica Análisis regional
codigo_divipola_municipio Numérica Código DANE del municipio Geográfica Agrupación geográfica
nombre_municipio Categórica Nombre del municipio Geográfica Análisis regional
edad Numérica Edad del paciente Explicativa Variable cuantitativa directa
unidad_medida_edad Categórica Unidad de medida de la edad Explicativa Normalización de edad a años
sexo Categórica Sexo del paciente Explicativa Clasificación categórica
tipo_de_contagio Categórica Tipo de contagio Explicativa Clasificación del origen del contagio
ubicacion Categórica Ubicación del paciente Explicativa Estado de atención médica
estado Categórica Estado clínico del paciente Explicativa Variable de estado clínico
pais_de_procedencia Categórica País de procedencia Explicativa Identificación de casos importados
fecha_de_inicio_de_sintomas Fecha Fecha de inicio de síntomas Temporal Cálculo de tiempos de recuperación
fecha_de_diagnostico Fecha Fecha de diagnóstico Temporal Seguimiento del diagnóstico
fecha_de_recuperacion Fecha Fecha de recuperación Temporal Cálculo de días de recuperación (variable respuesta)
tipo_de_recuperacion Categórica Tipo de recuperación Explicativa Análisis del tipo de recuperación
fecha_de_muerte Fecha Fecha de fallecimiento Respuesta Variable para análisis de mortalidad
fecha_de_reporte_web Fecha Fecha de reporte en web Temporal Seguimiento temporal
fuente_tipo_contagio Categórica Fuente del contagio Explicativa Análisis del origen del contagio
pertenencia_etnica Categórica Pertenencia étnica Explicativa Análisis poblacional
nombre_grupo_etnico Categórica Nombre del grupo étnico Explicativa Detalle étnico
clasificacion_final Categórica Clasificación final del caso Explicativa Clasificación epidemiológica

2.3 Transformación 1: Creación variable días de recuperación

Qué se hizo: Se convirtieron las variables fecha_de_inicio_de_sintomas y fecha_de_recuperacion de tipo character a formato fecha, y se calculó la diferencia en días entre ambas.

Por qué: Las fechas llegaron como texto en el CSV, lo que impide cualquier cálculo temporal. Era necesario convertirlas a formato Date para poder operar con ellas matemáticamente.

Para qué sirve: Esta variable dias_recuperacion es la variable respuesta del modelo predictivo. Representa cuántos días tardó un paciente en recuperarse desde el inicio de sus síntomas, que es el objetivo central del estudio.

df <- df %>%
  mutate(across(
    c(fecha_de_inicio_de_sintomas, fecha_de_recuperacion),
    ~ parse_date_time(str_trim(.), orders = c("dmy HM", "dmy"))
  ))

df <- df %>%
  mutate(
    dias_recuperacion = as.numeric(
      fecha_de_recuperacion - fecha_de_inicio_de_sintomas
    )
  )

summary(df$dias_recuperacion)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##    2.00   14.00   15.00   23.23   21.00  386.00   15653

2.4 Transformación 2: Creación variable días hasta fallecimiento

Qué se hizo: Se convirtió la variable fecha_de_muerte a formato fecha y se calculó la diferencia en días respecto a fecha_de_inicio_de_sintomas.

Por qué: Al igual que las fechas anteriores, esta variable llegó como texto y requería conversión. Además, permite medir el tiempo de evolución de la enfermedad en casos fatales.

Para qué sirve: Permite analizar la severidad de la enfermedad en pacientes fallecidos, comparar con el tiempo de recuperación de los sobrevivientes y servirá como variable auxiliar en el análisis exploratorio de mortalidad.

df <- df %>%
  mutate(
    fecha_de_muerte = parse_date_time(
      str_trim(fecha_de_muerte),
      orders = c("dmy HM", "dmy")
    )
  )

df <- df %>%
  mutate(
    dias_hasta_fallecimiento = as.numeric(
      as.Date(fecha_de_muerte) - as.Date(fecha_de_inicio_de_sintomas)
    )
  )

summary(df$dias_hasta_fallecimiento)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##     0.0    11.0    19.0    42.4    31.0   543.0  405931

2.5 Transformación 3: Normalización de la variable edad

Qué se hizo: Se convirtió la variable edad a numérica y se creó la variable edad_anos, normalizando todos los registros a años independientemente de si la unidad original era meses (código 2) o días (código 3).

Por qué: La base de datos registra la edad con diferentes unidades de medida según el paciente (años, meses, días). Sin esta normalización, no es posible comparar edades entre pacientes ni incluir la variable en el modelo predictivo.

Para qué sirve: La variable edad_anos será una de las principales variables explicativas del modelo predictivo, ya que se espera que la edad tenga relación con el tiempo de recuperación.

df <- df %>%
  mutate(
    edad = as.numeric(edad),
    unidad_de_medida_de_edad = as.numeric(unidad_de_medida_de_edad),
    edad_anos = case_when(
      unidad_de_medida_de_edad == 1 ~ edad,
      unidad_de_medida_de_edad == 2 ~ edad / 12,
      unidad_de_medida_de_edad == 3 ~ edad / 365,
      TRUE ~ edad
    )
  )

2.6 Manejo de valores faltantes (NA)

Se identificaron las variables con valores faltantes y se aplicó el siguiente tratamiento según la naturaleza de cada variable:

resumen_na <- df %>%
  summarise(across(everything(), ~ sum(is.na(.)))) %>%
  pivot_longer(everything(),
               names_to = "Variable",
               values_to = "Total NA") %>%
  mutate(
    "Porcentaje NA (%)" = round(100 * `Total NA` / nrow(df), 2)
  ) %>%
  filter(`Total NA` > 0) %>%
  arrange(desc(`Total NA`))

resumen_na %>%
  kable(caption = "Variables con valores faltantes antes del tratamiento") %>%
  kable_styling(
    bootstrap_options = c("striped", "hover", "condensed"),
    full_width = FALSE,
    position = "center"
  ) %>%
  row_spec(0, bold = TRUE)
Table 2.3: Table 2.4: Variables con valores faltantes antes del tratamiento
Variable Total NA Porcentaje NA (%)
codigo_iso_del_pais 418987 99.98
nombre_del_pais 418987 99.98
nombre_del_grupo_etnico 412797 98.51
dias_hasta_fallecimiento 405931 96.87
fecha_de_muerte 405922 96.86
dias_recuperacion 15653 3.74
tipo_de_recuperacion 12747 3.04
fecha_de_recuperacion 12744 3.04
fecha_de_inicio_de_sintomas 2916 0.70
fecha_de_diagnostico 1144 0.27
pertenencia_etnica 40 0.01

Tratamiento aplicado:

  • nombre_del_pais: Los NA corresponden a casos locales de Colombia. Se imputan con “Colombia”.
  • recuperado y estado: Los NA corresponden a pacientes con fecha de muerte registrada. Se imputan con “Fallecido”.
  • Variables de fecha (fecha_de_recuperacion, fecha_de_muerte): Los NA son estructurales — un fallecido no tiene fecha de recuperación y viceversa. Se conservan como NA y se excluyen según el análisis requerido.
  • Variables con alto porcentaje de NA y baja utilidad analítica (codigo_iso, nombre_grupo_etnico) se excluyen de los análisis.
# Imputar país
df$nombre_del_pais[is.na(df$nombre_del_pais)] <- "Colombia"

# Imputar estado y recuperado para fallecidos
df <- df %>%
  mutate(
    estado     = replace_na(estado, "Fallecido"),
    recuperado = replace_na(recuperado, "Fallecido"),
    estado     = str_squish(str_to_title(estado))
  )

# Limpiar nombre_municipio
df <- df %>%
  filter(!is.na(nombre_municipio), str_trim(nombre_municipio) != "",
         nombre_municipio != "NA")
saveRDS(df, "data/df_procesado.rds")