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
## 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
## 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
##
## Adjuntando el paquete: 'scales'
##
## The following object is masked from 'package:purrr':
##
## discard
##
## The following object is masked from 'package:readr':
##
## col_factor
##
## Adjuntando el paquete: 'kableExtra'
##
## The following object is masked from 'package:dplyr':
##
## group_rows
## 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.csvdentro de la carpetadata/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)| 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.
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)| 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”.recuperadoyestado: 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")