En este informe desarrollamos un análisis predictivo de la esperanza de vida en países de Sudamérica a partir de indicadores socioeconómicos, utilizando datos del Banco Mundial correspondientes al año 2015. El objetivo es explorar qué factores estructurales permiten distinguir a los países con mayor y menor esperanza de vida, apoyados en modelos de clasificación supervisada.
Se seleccionó el año 2015 por ser el más reciente con datos completos y comparables para la región. El dataset resultante presenta una fila por país y al menos 10 variables socioeconómicas (PIB per cápita, alfabetización, urbanización, gastos en salud, tasa de fertilidad, mortalidad infantil, población total, acceso a agua, desempleo y gasto en educación).
La variable objetivo es la esperanza de vida al nacer (años), transformada en binaria según la mediana regional de Sudamérica en 2015:
1 (“alta”) si el país tiene esperanza de vida igual o superior a la mediana, 0 (“baja”) si es inferior. La dicotomización se justifica para identificar factores asociados a desempeños por encima/más allá del promedio regional.
Se imputaron los valores faltantes con la mediana de cada variable numérica y se eliminaron las variables objetivo originales (esperanza de vida) y el nombre del país antes de modelar.
El dataset se dividió en entrenamiento (80%) y prueba (20%) utilizando muestreo estratificado para asegurar la representatividad de ambas clases en ambos conjuntos.
paquetes <- c("readxl", "tidyr", "dplyr", "caret", "class", "rpart", "rpart.plot")
instalar <- paquetes[!sapply(paquetes, require, character.only = TRUE)]
## Loading required package: readxl
## Loading required package: tidyr
## Loading required package: dplyr
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
## Loading required package: caret
## Loading required package: ggplot2
## Loading required package: lattice
## Loading required package: class
## Loading required package: rpart
## Loading required package: rpart.plot
if (length(instalar) > 0) install.packages(instalar)
lapply(paquetes, require, character.only = TRUE)
## [[1]]
## [1] TRUE
##
## [[2]]
## [1] TRUE
##
## [[3]]
## [1] TRUE
##
## [[4]]
## [1] TRUE
##
## [[5]]
## [1] TRUE
##
## [[6]]
## [1] TRUE
##
## [[7]]
## [1] TRUE
Dado que los datos están segregados por país y por año, se pivotan para disponerlos de manera que sea útil para la creación de un modelo predictivo. Se utilizará la regla de binarización establecida anteriormente.
df <- read_excel("./P_Data_Extract_From_World_Development_Indicators (5).xlsx", sheet = "Data")
df_wide <- df %>%
select(`Country Name`, `Series Name`, `2015 [YR2015]`) %>%
filter(`2015 [YR2015]` != "..") %>%
pivot_wider(names_from = `Series Name`, values_from = `2015 [YR2015]`)
df_ml <- df_wide %>%
mutate(across(-`Country Name`, as.numeric))
mediana_sv <- median(df_ml$`Life expectancy at birth, total (years)`, na.rm = TRUE)
df_ml <- df_ml %>%
mutate(LifeExp_Bin = ifelse(`Life expectancy at birth, total (years)` >= mediana_sv, 1, 0))
df_ml$LifeExp_Bin <- as.factor(df_ml$LifeExp_Bin) # Factor para clasificación
vars_num <- setdiff(names(df_ml), c("Country Name"))
for (v in vars_num) {
if(is.numeric(df_ml[[v]])) {
df_ml[[v]][is.na(df_ml[[v]])] <- median(df_ml[[v]], na.rm = TRUE)
}
}
vars <- c(
"Country Name",
"GDP per capita (current US$)",
"Urban population (% of total population)",
"Current health expenditure (% of GDP)",
"Fertility rate, total (births per woman)",
"Mortality rate, infant (per 1,000 live births)",
"Population, total",
"People using at least basic drinking water services (% of population)",
"Unemployment, total (% of total labor force) (modeled ILO estimate)",
"Government expenditure on education, total (% of GDP)",
"Life expectancy at birth, total (years)",
"Literacy rate, adult total (% of people ages 15 and above)"
)
df_ml <- df_ml %>% select(`Country Name`, all_of(vars))
Con el fin de evaluar los principales indicadores estadísticos, así como la distribución de los datos, se realizarán histogramas y diagramas de caja y bigote.
if (!"LifeExp_Bin" %in% colnames(df_ml)) {
mediana_sv <- median(df_ml$`Life expectancy at birth, total (years)`, na.rm = TRUE)
df_ml <- df_ml %>%
mutate(LifeExp_Bin = ifelse(`Life expectancy at birth, total (years)` >= mediana_sv, 1, 0))
}
df_ml %>%
group_by(LifeExp_Bin) %>%
summarise(across(where(is.numeric), list(media=mean, sd=sd)))
vars_graficar <- setdiff(
names(df_ml)[sapply(df_ml, is.numeric)],
c("LifeExp_Bin", "Life.expectancy.at.birth..total..years.") # Usa aquí el nombre que corresponda exactamente en tu objeto
)
for (var in vars_graficar) {
boxplot(df_ml[[var]] ~ df_ml$LifeExp_Bin,
main = paste("Boxplot de", var, "por grupo de esperanza de vida"),
xlab = "Esperanza de vida binaria (0=baja, 1=alta)",
ylab = var)
}
Con el fin de obtener una visualización precisa de la distribución de los datos, se separan los histogramas para observar la distribución de los datos por clase. Esto es, se generarán para cada variable predictora dos histogramas, uno mostrando la distribución para la esperanza de vida baja y otro para la esperanza de vida alta.
for (var in vars_graficar) {
hist(df_ml[[var]][df_ml$LifeExp_Bin == 0],
main = paste("Histograma de", var, "- Esperanza de vida baja"),
xlab = var, col = "red", breaks = 10)
hist(df_ml[[var]][df_ml$LifeExp_Bin == 1],
main = paste("Histograma de", var, "- Esperanza de vida alta"),
xlab = var, col = "blue", breaks = 10)
}
# Preparación Se imputarán los datos faltantes con la mediana, medida de
tendencia central que no es sensible a datos atípicos. Adicionalmente se
realizará una división 80/20 estratificada, con el fin de tener un
dataset de pruebas ocn clases suficientes para hacer una evaluación
efectiva.
Adicionalmente, se eliminarán las columnas no predictoras, se separarán las variables y se harán correcciones necesarias para evitar errores.
set.seed(3)
splitIndex <- createDataPartition(df_ml$LifeExp_Bin, p = 0.8, list = FALSE)
train <- df_ml[splitIndex, ]
test <- df_ml[-splitIndex, ]
train <- train %>% select(-`Life expectancy at birth, total (years)`, -`Country Name`)
test <- test %>% select(-`Life expectancy at birth, total (years)`, -`Country Name`)
train_x <- train %>% select(-LifeExp_Bin)
train_y <- train$LifeExp_Bin
test_x <- test %>% select(-LifeExp_Bin)
test_y <- test$LifeExp_Bin
colnames(train_x) <- make.names(colnames(train_x))
colnames(test_x) <- make.names(colnames(test_x))
preProc <- preProcess(train_x, method = c("center", "scale"))
train_x_stand <- predict(preProc, train_x)
test_x_stand <- predict(preProc, test_x)
Se desarrollarán y compararán los resultados de dos modelos diferentes: Uno entrenado mediante k-nearest-neightbors (kNN) y otro mediante árboles de decisión.
set.seed(3)
knn_pred <- knn(train = train_x_stand, test = test_x_stand, cl = train_y, k = 3)
# Ajustar niveles para matriz de confusión
niveles <- sort(unique(c(as.character(test_y), as.character(knn_pred))))
test_factor_knn <- factor(test_y, levels = niveles)
knn_pred_factor <- factor(knn_pred, levels = niveles)
confusionMatrix(knn_pred_factor, test_factor_knn)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 0 0
## 1 1 1
##
## Accuracy : 0.5
## 95% CI : (0.0126, 0.9874)
## No Information Rate : 0.5
## P-Value [Acc > NIR] : 0.75
##
## Kappa : 0
##
## Mcnemar's Test P-Value : 1.00
##
## Sensitivity : 0.0
## Specificity : 1.0
## Pos Pred Value : NaN
## Neg Pred Value : 0.5
## Prevalence : 0.5
## Detection Rate : 0.0
## Detection Prevalence : 0.0
## Balanced Accuracy : 0.5
##
## 'Positive' Class : 0
##
set.seed(3)
arbol <- rpart(
LifeExp_Bin ~ .,
data = data.frame(train_x, LifeExp_Bin = train_y),
method = "class",
control = rpart.control(
maxdepth = 5,
minsplit = 3,
minbucket = 3,
cp = 0.01
)
)
arbol_pred <- predict(arbol, newdata = test_x, type = "class")
# Ajustar niveles para matriz de confusión del árbol
niveles_tree <- sort(unique(c(as.character(test_y), as.character(arbol_pred))))
test_factor_arbol <- factor(test_y, levels = niveles_tree)
arbol_pred_factor <- factor(arbol_pred, levels = niveles_tree)
confusionMatrix(arbol_pred_factor, test_factor_arbol)
## Confusion Matrix and Statistics
##
## Reference
## Prediction 0 1
## 0 1 1
## 1 0 0
##
## Accuracy : 0.5
## 95% CI : (0.0126, 0.9874)
## No Information Rate : 0.5
## P-Value [Acc > NIR] : 0.75
##
## Kappa : 0
##
## Mcnemar's Test P-Value : 1.00
##
## Sensitivity : 1.0
## Specificity : 0.0
## Pos Pred Value : 0.5
## Neg Pred Value : NaN
## Prevalence : 0.5
## Detection Rate : 0.5
## Detection Prevalence : 1.0
## Balanced Accuracy : 0.5
##
## 'Positive' Class : 0
##
rpart.plot(arbol)
## Comparación
Si bien ambos modelos tienen la misma precisión (0.5), el modelo de árbol tiene una sensibilidad de 1, lo que podría indicar que es mejor. No obstante, dada la falta de datos de entrenamiento, los resultados hay que interpretarlos cuidadosamente y dentro de su contexto.
Los resultados obtenidos con base en la evidencia empírica, y de acuerdo con el modelo de árbol de decisión, es que el factor principal de un país para que su expectativa de vida sea alta es un PIB elevado. Valga la redundancia, este análisis está sujeto a la baja disponibilidad de datos y es posible que futuras investigaciones con un mayor volúmen de datos evidencie resultados diferentes a los expuestos.
Therneau, Atkinson, Ripley (2025). rpart. Disponible en (https://cran.r-project.org/web/packages/rpart/rpart.pdf)[https://cran.r-project.org/web/packages/rpart/rpart.pdf]. Consultado el 6 de mayo de 2025.
Wickham H, François R, Henry L, Müller K, Vaughan D (2023). dplyr: A Grammar of Data Manipulation. R package version 1.1.4, https://github.com/tidyverse/dplyr, https://dplyr.tidyverse.org.
World Bank (s.f.). World Development Indicators. Databank World Bank. Consultado el 6 de mayo de 2025. (https://databank.worldbank.org/source/world-development-indicators#)[https://databank.worldbank.org/source/world-development-indicators#].
Kuhn et al (2024). caret: Classification and Regression Training. Cran. Consultado el 6 de mayo de 2025. (https://cran.r-project.org/web/packages/caret/)[https://cran.r-project.org/web/packages/caret/]