El presente reporte representa la última entrega de la Certificación Profesional en Ciencia de Datos de la UDELAR. En el mismo, presentaré y demostraré los conocimientos aprehendidos a lo largo del programa.
Para ello he seleccionado datos provenientes de la plataforma Kaggle. Estos datos pueden ser obtenidos a través del siguiente link World Economic Data. Tras realizar una investigación en Kaggle fue posible concluir que la fuente original es World Data Info.
Se cuenta con información para 110 países con relación a sus indicadores de corrupción, turismo, desempleo y costo de vida.
Adicionalmente, se han agregando columnas que indican el continente al que pertenece el país y su respectiva población como parte del proceso de minería de datos.
Preguntas guía
En este reporte me propongo analizar la realción existente entre el índice de corrupción y diversas variables socioeconómicas y demográficas.
Como se ha mencionado anteriormente, se implementan técnicas de minería de datos como imputación de valores faltantes y enriquecimiento de la base original.
Las preguntas que guían mi análisis son:
¿Qué relación existe entre las variables socioeconómicas y demográficas incluidas en la base de datos y el índice de corrupción en diferentes países?
¿Se pueden clasificar los países como “corruptos” o “no corruptos” basándonos en características socioeconómicas y demográficas?
Para responderlas se aplican algoritmos de Machine Learning sobre la variable que mide el Índice de Corrupción.
La primer pregunta es abordada con un Modelo de Regresión Lineal que explicar los determinantes del índice referido.
Una vez aplicada la lógica de regresión, se pasa a la de clasificación para responder la segunda pregunta. Antes, se realiza una transformación sobre la variable que mide el Índice de Corrupción para convertirla en una de tipo binomial. Así, se logra cumplir las condiciones necesarias para aplicar modelos de Regresión Logística, Decision Tree y Random Forest.
Descripción y Minería de Datos
En esta sección se presentan los datos originales, un detallada descripción del proceso de minería de datos y finalmente, un análisis exploratorio a partir de gráficos relevantes.
Descripción de Datos
Para la elaboración del análisis se han utilizado cinco conjuntos de datos en formato csv. que brindan información a nivel de diversas características socioeconómicas y demográficas de países alrededor del mundo.
A continuación, se describen las fuentes y las variables más importantes de cada conjunto de datos:
corruption.csv: Este conjunto de datos proporciona el Índice de Corrupción para cada país, una medida cuantitativa que refleja el nivel percibido de corrupción en el sector público. Cuanto mas alto sea su valor, mayor será el nivel de corrupción percibido en el país.
unemployment.csv: Contiene información sobre la Tasa de Desempleo por país, es decir, el porcentaje de la población económicamente activa que se encuentra desempleada.
tourism.csv: Este archivo incluye variables relacionadas con el turismo internacional, tales como el número de turistas que visitan cada país y los ingresos generados por el turismo. Sin embargo, debido a la presencia de numerosos valores faltantes para la gran mayoría de los países se optó por excluirlas en el análisis final.
richest_countries.csv: Proporciona datos sobre el Ingreso Anual per cápita de varios países, una métrica que mide el ingreso promedio de un ciudadano de cada país.
cost_of_living.csv: Incluye un Índice de Costo de Vida, que mide los costos relativos de bienes y servicios en cada país en comparación con otros países. Esto incluye factores como la vivienda, el transporte y los alimentos.
Minería de Datos
Proceso de integración de datos
Dado que la información para cada país se encontraba dispersa en los cinco archivos mencionados anteriormente, fue necesario consolidarla en un solo conjunto de datos. Para ello se aplicó un left join en cada archivo utilizando la variable country como primary key.
Código
# Uno todos los datasets en uno solo con la función left_joinmerged_df <- corruption_df %>%left_join(unemployment_df, by ="country") %>%left_join(tourism_df, by ="country") %>%left_join(richest_countries_df, by ="country") %>%left_join(cost_of_living_df, by ="country")
Manejo de valores faltantes
Durante el proceso de minería de datos, se indentificaron varias columnas que contenían contenían valores faltantes. Por ejemplo, muchas variables vinculadas al turismo, como el número de turistas y los ingresos generados, sólo tenían información para un conjunto acotado de países. Por esta razón se decidió eliminarlas.
Para otras variables con un número de datos faltantes relativamente menor, se realizó la imputación de la mediana. A fin de conservarlas como variables explicativas del Índice de Corrupción.
Código
# Creo un vector con los nombres de todas las columnas para visualizar la cantidad de NA´s presentes en mi dataframecolumnas <-c("country", "annual_income", "corruption_index","unemployment_rate", "tourists_in_millions","receipts_in_billions", "receipts_per_tourist", "percentage_of_gdp","gdp_per_capita", "cost_index","monthly_income", "purchasing_power_index")valores_nulos <-c(sum(is.na(merged_df$country)),sum(is.na(merged_df$annual_income)),sum(is.na(merged_df$corruption_index)),sum(is.na(merged_df$unemployment_rate)),sum(is.na(merged_df$tourists_in_millions)),sum(is.na(merged_df$receipts_in_billions)),sum(is.na(merged_df$receipts_per_tourist)),sum(is.na(merged_df$percentage_of_gdp)),sum(is.na(merged_df$gdp_per_capita)),sum(is.na(merged_df$cost_index)),sum(is.na(merged_df$monthly_income)),sum(is.na(merged_df$purchasing_power_index)) )# Elimino las columnas donde la cantidad de NA´s es altamente significativa (con relación al total de observaciones de mi dataset)merged_df <-select(merged_df, -tourists_in_millions, -receipts_in_billions, -receipts_per_tourist, -percentage_of_gdp,-gdp_per_capita)# Imputo valores faltantes con la mediana de cada columnamerged_df <- merged_df %>%mutate(across(where(is.numeric), ~ifelse(is.na(.), median(., na.rm =TRUE), .)))
Resumen de Valores Faltantes y Variables Consideradas
Variables conservadas y descartadas para el análisis
Variable
Cantidad de NA's
¿Considerada para Análisis?
country
0
Conservada
annual_income
0
Conservada
corruption_index
0
Conservada
unemployment_rate
60
Conservada
tourists_in_millions
82
Descartada
receipts_in_billions
82
Descartada
receipts_per_tourist
82
Descartada
percentage_of_gdp
82
Descartada
gdp_per_capita
76
Descartada
cost_index
39
Conservada
monthly_income
39
Conservada
purchasing_power_index
39
Conservada
Variables utilizadas
Las variables consideradas en este análisis se presentan en la siguiente tabla:
Resumen de Variables Utilizadas en el Análisis
Descripción de las principales variables empleadas en el modelo
Nombre de la Variable
Descripción
Tipo de Variable
corruption_index
Índice de corrupción (variable objetivo)
Numérica
annual_income
Ingreso anual per cápita
Numérica
unemployment_rate
Tasa de desempleo
Numérica
cost_index
Índice de costo de vida
Numérica
monthly_income
Ingreso mensual per cápita
Numérica
purchasing_power_index
Índice de poder adquisitivo
Numérica
continent
Continente al que pertenece el país
Categórica
population
Población total del país
Numérica
Generación de variables adicionales
Como se puede observar en la tabla anterior, se generó una variable categórica llamada continent, que clasifica a cada país en función del continente al que pertenece.
Por otro lado, se incluyó la variable population. Que corresponde a la población total de cada país, obtenida de la base de datos del Banco Mundial.
Código
# Creo un data frame con la lista de países que conforman el datasetcountries <-c("Denmark", "Finland", "New Zealand", "Norway", "Singapore", "Sweden", "Switzerland", "Netherlands", "Luxembourg", "Germany", "United Kingdom", "Hong Kong", "Austria", "Canada", "Estonia", "Iceland", "Ireland", "Australia", "Belgium", "Japan", "France", "United Arab Emirates", "United States", "Qatar", "Portugal", "South Korea", "Spain", "Israel", "Italy", "Poland", "Saudi Arabia", "Greece", "Malaysia", "China", "Romania", "South Africa", "India", "Vietnam", "Argentina", "Brazil", "Indonesia", "Turkey", "Sri Lanka", "Ecuador", "Thailand", "El Salvador", "Sierra Leone", "Algeria", "Egypt", "Nepal", "Philippines", "Zambia", "Eswatini", "Ukraine", "Gabon", "Mexico", "Niger", "Papua New Guinea", "Azerbaijan", "Bolivia", "Djibouti", "Dominican Republic", "Kenya", "Laos", "Paraguay", "Togo", "Angola", "Liberia", "Mali", "Russia", "Burma", "Mauritania", "Pakistan", "Uzbekistan", "Cameroon", "Kyrgyzstan", "Uganda", "Bangladesh", "Madagascar", "Mozambique", "Guatemala", "Guinea", "Iran", "Tajikistan", "Central Africa", "Lebanon", "Nigeria", "Cambodia", "Honduras", "Iraq", "Zimbabwe", "Eritrea", "Cape Verde", "Congo", "Chad", "Comoros", "Haiti", "Nicaragua", "Sudan", "Burundi", "Congo (Dem. Republic)", "Turkmenistan", "Equatorial Guinea", "Libya", "Afghanistan", "Yemen", "Venezuela", "Somalia", "Syria", "South Sudan")# Defino una función que clasifique cada país según su continenteget_continent <-function(country) {if (country %in%c("Denmark", "Finland", "Norway", "Sweden", "Switzerland", "Netherlands", "Luxembourg", "Germany", "United Kingdom", "Austria", "Estonia", "Iceland", "Ireland", "Belgium", "France", "Portugal", "Spain", "Italy", "Poland", "Greece", "Romania", "Ukraine", "Russia")) {return("Europe") } elseif (country %in%c("Canada", "United States", "Mexico", "Guatemala", "Honduras", "El Salvador", "Nicaragua","Haiti","Dominican Republic")) {return("North America") } elseif (country %in%c("Argentina", "Brazil", "Ecuador", "Bolivia", "Paraguay", "Venezuela")) {return("South America") } elseif (country %in%c("Singapore", "Hong Kong", "Japan", "South Korea", "Israel", "Turkey", "Malaysia", "China", "India", "Vietnam", "Indonesia", "Sri Lanka", "Thailand", "Nepal", "Philippines", "Azerbaijan", "Laos", "Burma", "Pakistan", "Uzbekistan", "Kyrgyzstan", "Bangladesh", "Iran", "Tajikistan", "Lebanon", "Cambodia", "Iraq", "Afghanistan", "Yemen", "Syria","Turkmenistan","Saudi Arabia" ,"United Arab Emirates", "Qatar")) {return("Asia") } elseif (country %in%c("South Africa", "Sierra Leone", "Algeria", "Egypt", "Zambia", "Eswatini", "Gabon", "Niger", "Djibouti", "Kenya", "Togo", "Angola", "Liberia", "Mali", "Cameroon", "Uganda", "Madagascar", "Mozambique", "Guinea", "Central Africa", "Nigeria", "Zimbabwe", "Eritrea", "Cape Verde", "Congo", "Chad", "Comoros", "Sudan", "Burundi", "Congo (Dem. Republic)", "Equatorial Guinea", "Libya", "Somalia", "South Sudan","Mauritania")) {return("Africa") } elseif (country %in%c("Australia", "New Zealand", "Papua New Guinea")) {return("Oceania") } else {return("Unknown") }}# Aplico la función para clasificar los países según continentescontinent <-sapply(countries, get_continent)# Creo el dataset final con la nueva columna de continentescontinent_df <-data.frame(country = countries, continent = continent)row.names(continent_df) <- continent_df$country# Uno este último dataset al merged_df por la columna "countrymerged_df <- merged_df %>%left_join(continent_df, by ="country")# Cambio el formato de las columnas según correspondamerged_df <- merged_df %>%mutate_if(is.character, as.factor)# Ahora voy a crear una columna que contenga la población para cada país listado# Obtengo los códigos ISO-3 de los paísesiso_codes <-countrycode(sourcevar = countries,origin ="country.name",destination ="iso3c")# Obtengo la población para los países de la lista para el año 2020poblacion <-WDI(country = iso_codes,indicator ="SP.POP.TOTL",start =2020,end =2020)# Renombro la columna "SP.POP.TOTL" a "population" y reduzco el número de columnas del datasetpopulation_df <-select(poblacion, country, population = SP.POP.TOTL)# Merge con el dataframe de poblaciónmerged_df <- merged_df %>%left_join(population_df, by ="country") # En el merge anterior quedaron filas sin completar, por tanto filtro los países con valores nulos en la columna "population" y creo una listanull_countries <- merged_df %>%filter(is.na(population)) %>%select(country)# Crear un data frame que resuma la información de país y población a partir de datos de internetnull_countries_df <-data.frame(country =c("South Korea", "Vietnam", "Turkey", "Egypt", "Laos", "Russia", "Burma", "Kyrgyzstan", "Iran", "Central Africa", "Cape Verde", "Congo", "Congo (Dem. Republic)", "Yemen", "Venezuela", "Syria","Hong Kong"),population =c(51700000, 97300000, 83600000, 102300000, 7200000, 145900000, 54000000, 6500000, 83900000, 4700000, 555987, 86800000, 86800000, 29800000, 28500000, 17500000,7500000),stringsAsFactors =FALSE)# Completo la columna population de mi dataframe "merged_df"merged_df$population <-ifelse(is.na(merged_df$population), null_countries_df$population[match(merged_df$country, null_countries_df$country)], merged_df$population)
Análisis Exploratorio de Datos
El análisis realizado en esta sección tiene como propósito conocer las distribuciones de las variables mas relevantes para responder las preguntas guía. También busca transmitir de manera clara las relaciones existentes entre las variables consideradas en la tabla anterior.
Distribución del Índice de Corrupción
El Índice de Corrupción será la variable objetivo en este análisis y por lo tanto, resulta importante conocer su distribución a lo largo de la muestra.
Código
# Distribución del índice de corrupción mejoradoggplot(merged_df, aes(x = corruption_index)) +geom_histogram(aes(y = ..density..), binwidth =1, fill ="darkblue", color ="white", alpha =0.7) +geom_density(color ="darkblue", fill ="lightblue", alpha =0.3) +labs(x ="Índice de Corrupción",y ="Densidad" ) +theme_minimal() +theme(plot.title =element_text(hjust =0.5, size =14, face ="bold"),plot.subtitle =element_text(hjust =0.5, size =10),axis.title.x =element_text(size =12, face ="bold"),axis.title.y =element_text(size =12, face ="bold"),axis.text =element_text(size =10),panel.grid.major =element_line(color ="gray80"),panel.grid.minor =element_blank() )
Figura 1: Si bien hay cierta concentración, la muestra persenta gran variabilidad en el Índice de Corrupción.
De la Figura 1 podemos notar que las observaciones tienden a concentrarse en valores altos del Índice, es posible inferir esto por dos razones. Esto puede tener efectos significativos sobre la etapa de clasificación de este reporte, dado que el punto de corte para definir si un país es “corrupto” o “no corrupto” es la mediana del Índice. Por lo tanto, si tengo una muestra altamente desbalanceada hacia valores altos del Índice, estaría sesgando los eventuales pronósticos de mis modelos.
Boxplot sobre el Índice de Corrupción por Continente
Los gráficos Boxplot son súmamente útiles para observar la distribución del Índice de Corrupción en función de variables categóricas como el Continente. A partir de ellos podemos ver claramente donde se ubica la mediana del Índice para cada Continente, así como también la dispersión existente dentro de cada grupo y la existencia de outliers.
A continuación de presenta el gráfico mencionado:
Código
# Creo un gráfico boxplot que muestra el nivel de corrupción en función del continenteggplot(merged_df, aes(x = continent, y = corruption_index, fill = continent)) +geom_boxplot(outlier.color ="red", outlier.shape =16, outlier.size =2) +scale_fill_brewer(palette ="Set3") +xlab("") +ylab("") +labs(y ="Índice de Corrupción",fill ="Continent") +theme_minimal() +theme(plot.title =element_text(hjust =0.5, size =14, face ="bold"),axis.title.x =element_text(size =12, face ="bold"),axis.title.y =element_text(size =12, face ="bold"),axis.text.x =element_text(angle =45, hjust =1) )
Figura 2: Se observa la presencia de mayor dispersión en continentes como Asia, Europa y Oceanía. Donde las cajas son mas amplias.
Distribución del Ingreso Anual
Sería esperable que el Ingreso Anual también influyera sobre los niveles del Índice de Corrupción. A continuación se presenta un histograma que permite visualizar cómo se distribuye esta variable en los países de la muestra.
Código
# Distribución del ingreso anual mejoradoggplot(merged_df, aes(x = annual_income)) +geom_histogram(aes(y = ..density..), binwidth =5000, fill ="darkgreen", color ="white", alpha =0.7) +geom_density(color ="darkgreen", fill ="lightgreen", alpha =0.3) +labs(x ="Ingreso Anual per Cápita",y ="Densidad" ) +theme_minimal() +theme(plot.title =element_text(hjust =0.5, size =14, face ="bold"),plot.subtitle =element_text(hjust =0.5, size =10),axis.title.x =element_text(size =12, face ="bold"),axis.title.y =element_text(size =12, face ="bold"),axis.text =element_text(size =10),panel.grid.major =element_line(color ="gray80"),panel.grid.minor =element_blank() )
Figura 3: La muestra está mayoritariamente conformada por países de ingreso anual bajo
La Figura 3 muestra que la mayoría de los países presentan un Ingreso Anual concentrado en los tramos bajos y medios del recorrido de la variable. Siendo muy pocos los países que presentan Ingreso Anual alto.
Relación entre el Ingreso Anual y el Índice de Corrupción
Los diagramas de puntos nos permiten contrastar de manera preliminar las afirmaciones hechas en los comentarios del Figura 3. Retomando, sería esperable obtener una relación entre el Ingreso Anual y el Índice de Corrupción.
Código
# Scatter plot mejorado de ingresos anuales vs corrupciónggplot(merged_df, aes(x = annual_income, y = corruption_index)) +geom_point(color ="darkblue", alpha =0.6, size =3) +# Puntos en azul oscuro, ligeramente transparentesgeom_smooth(method ="lm", se =FALSE, color ="red", size =1.2) +# Línea de tendencia en rojolabs(x ="Ingresos Anuales per Cápita (USD)",y ="Índice de Corrupción" ) +theme_minimal() +theme(plot.title =element_text(hjust =0.5, size =14, face ="bold"),plot.subtitle =element_text(hjust =0.5, size =10),axis.title.x =element_text(size =12, face ="bold"),axis.title.y =element_text(size =12, face ="bold"),axis.text =element_text(size =10),panel.grid.major =element_line(color ="gray80"),panel.grid.minor =element_blank() )
Figura 4: Parece haber una relación lineal decreciente entre el Índice de Corrupción y el Ingreso Anual
Como bien se muestra en el Figura 4 , a medida que el Ingreso Anual crece, los niveles del Índice de Corrupción observados tienden a disminuir. Si bien esta relación es clara, no es suficiente para afirmar una relación estadísticamente significativa. Para ello será necesaria la aplicación de algoritmos de Machine Learning.
Mapa de Correlaciones
Los mapas de calor de correlaciones son una herramienta gráfica súmamente útiles para detectar relaciones lineales entre variables numéricas que forman parte del análisis. A continuación se presenta el gráfico aplicado a los datos utilizados:
Código
correlation_matrix <-cor(merged_df %>%select(where(is.numeric)))corrplot( correlation_matrix, method ="color", # Usamos colores para las correlacionestype ="upper", # Mostrar solo la mitad superiorcol =colorRampPalette(c("#d73027", "#fdae61", "#ffffbf", "#a6d96a", "#1a9850"))(200), # Paleta de colores elegantetl.col ="black", # Color de los nombres de las variablestl.cex =0.8, # Tamaño de los nombres de las variablescl.cex =0.8, # Tamaño de la leyendaaddCoef.col ="black", # Mostrar los valores de correlaciónnumber.cex =0.7, # Tamaño de los valoresmar =c(0, 0, 1, 0) # Márgenes ajustados)
Figura 5: Las variables monetarias parecen estar altamente correlacionadas con el Índice de Corrupción
De la Figura 5 es posible concluir que hay una correlación negativa entre el Índice de Corrupción y el Ingreso Anual. Esto nos indicaría que en la medida que el Ingreso Anual crezca, sería esperable que el Índice de Corrupción descendiera.
El Modelo de Regresión Lineal Múltiple muestra la relación entre las variables socioeconómicas (ingreso, tasa de desempleo, índice de costo de vida, etc.) y el índice de corrupción. Es decir que se intenta explicar y posteriormente predecir dicho índice con base a las variables listadas previamente.
En cuanto a resultados, el R^2 del modelo fue de 0.88 , lo que indica que aproximadamente el 87.84% de la variabilidad en el índice de corrupción está explicada por las variables del modelo. Esto sugiere una capacidad aceptable de predicción del modelo, que podría ser mejorada con la inclusión de nuevas variables a la especificación.
El MSE (error cuadrático medio), que indica el promedio del error cuadrático entre los valores predichos y observados, fue de 38.23 .
Cabe resaltar que en este modelo las variables que resultaron significativas para un p-value menores a 0.10 fueron:
annual_income
cost_index
monthly_income
purchasing_power_index
continentEurope
continentOceania
Código
# Crear el gráfico con ggplot2ggplot(lm_results, aes(x = Observado, y = Predicho)) +geom_point(color ="blue") +# Puntos de observacióngeom_abline(intercept =0, slope =1, color ="red", linetype ="dashed") +# Línea de referencialabs(title ="Valores Observados vs Predichos en el Conjunto de Prueba",x ="Valor Observado",y ="Valor Predicho") +theme_minimal()
La Figura 6 muestra la relación entre los valores observados y los predichos. En la medida que los puntos se alinean con la diagonal punteada, el modelo estará haciendo buenas predicciones. Este pareciera ser el caso para los datos analizados.
Modelo de Regresión Logística
Especificación
Call:
glm(formula = corruption_binary ~ annual_income + unemployment_rate +
cost_index + monthly_income + purchasing_power_index + continent +
population, family = binomial, data = train_data)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -4.185e+00 4.339e+00 -0.965 0.335
annual_income -2.079e-04 1.373e-04 -1.514 0.130
unemployment_rate -7.275e-02 5.887e-02 -1.236 0.217
cost_index 1.809e-01 1.202e-01 1.505 0.132
monthly_income -1.522e-02 1.292e-02 -1.178 0.239
purchasing_power_index 2.941e-01 2.629e-01 1.119 0.263
continentAsia 2.894e-01 9.581e-01 0.302 0.763
continentEurope 1.080e+00 2.014e+00 0.536 0.592
continentNorth America 7.851e-02 1.204e+00 0.065 0.948
continentOceania -1.757e+01 1.771e+03 -0.010 0.992
continentSouth America 9.482e-02 1.267e+00 0.075 0.940
population -4.097e-09 5.451e-09 -0.752 0.452
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 123.099 on 88 degrees of freedom
Residual deviance: 58.895 on 77 degrees of freedom
AIC: 82.895
Number of Fisher Scoring iterations: 16
Con esta especificación se pretende clasificar a los países pertenecientes al grupo de entrenamiento en función de la variable corruption_binary. Para ello se plantea una regresión logística de esta variable en función de las variables previamente mencionadas.
Resultados
El Modelo de Regresión Logística tiene una exactitud de 85.71 %, lo que sugiere que el modelo estimado es capaz de clasificar correctamente la mayoría de los países como corruptos o no corruptos.
El F1 Score, que equilibra precisión y recall, es de 0.87, lo que indica un buen balance entre ambos.
A continuación se presenta una tabla con los resultados obtenidos para esta especificación:
Resultados del Modelo de Regresión Logística
Precisión, Recall y F1 Score (sobre corruption_binary=1)
Métrica
Valor
Precisión
0.77
Recall
1.00
F1 Score
0.87
Modelo de Arbol de Decisión
Especificación
En términos gráficos, sería posible definir el proceso de clasificación entre país “corrupto” y “no corrupto” a partir del siguiente diagrama.
Código
tree <-rpart(corruption_binary ~ annual_income + unemployment_rate + cost_index + monthly_income + purchasing_power_index + continent + population,data = train_data, method ="class", control =rpart.control(maxdepth =17))# Predecir en los datos de pruebapredictions_tree <-predict(tree, test_data, type ="class")confusion_matrix_tree <-table(predictions_tree, test_data$corruption_binary)# Calcular la precisiónaccuracy_tree <-sum(diag(confusion_matrix_tree)) /sum(confusion_matrix_tree)# Precisión, Recall y F1 Scoreprecision_tree <- confusion_matrix_tree[2, 2] / (confusion_matrix_tree[2, 1] + confusion_matrix_tree[2, 2])recall_tree <- confusion_matrix_tree[2, 2] / (confusion_matrix_tree[2, 2] + confusion_matrix_tree[1, 2])f1_tree <-2* (precision_tree * recall_tree) / (precision_tree + recall_tree)
Resultados
Según el diagrama anterior, las variables que han resultado significativas para clasificar a los países de la muestra de entrenamiento entre “corrupto” y “no corrupto” son annual_income y population .
Además, podemos decir que el Modelo de Árbol de Decisión tiene una exactitud de 80.95 %. Su simplicidad interpretativa lo hacen un método de clasificación útil, aunque su F1 Score de 0.82 sugiere que podría ser menos eficaz que otros modelos como Regresión Logística.
A continuación se presenta una tabla con los resultados obtenidos para esta especificación:
Código
# Crear tabla con gttree_results_df <-data.frame( Métrica =c("Precisión", "Recall", "F1 Score"),Valor =c(precision_tree, recall_tree, f1_tree))# Generar tabla con gtgt(tree_results_df) %>%tab_header(title =md("**Resultados del Modelo de Árbol de Decisión**"),subtitle =md("Precisión, Recall y F1 Score (sobre corruption_binary=1)") ) %>%cols_label(Métrica ="Métrica", Valor ="Valor")%>%fmt_number(columns =c(Valor), # Seleccionar la columna 'Valor' para formateardecimals =2# Mostrar 2 decimales )
Resultados del Modelo de Árbol de Decisión
Precisión, Recall y F1 Score (sobre corruption_binary=1)
Métrica
Valor
Precisión
0.75
Recall
0.90
F1 Score
0.82
Modelo Random Forest
Especificación
El Modelo Random Forest combina múltiples árboles de decisión para mejorar la precisión y estabilidad de las predicciones.
Cada árbol se entrena con una muestra aleatoria de datos y utiliza diferentes características para tomar decisiones. Luego, las predicciones de los árboles individuales se combinan para producir una predicción final. Esto permite que el modelo sea más resistente al sobreajuste y más preciso que un solo árbol de decisión.
Código
# Entrenar el modelo de Random Forestrf_model_clf <-randomForest(as.factor(corruption_binary) ~ annual_income + unemployment_rate + cost_index + monthly_income + purchasing_power_index + continent + population, data = train_data, ntree =500, mtry =3, importance =TRUE)# Predicciones en el conjunto de pruebapredictions_rf <-predict(rf_model_clf, test_data)confusion_matrix_rf <-table(predictions_rf, test_data$corruption_binary)# Calcular la precisiónaccuracy_rf <-sum(diag(confusion_matrix_rf)) /sum(confusion_matrix_rf)# Precisión, Recall y F1 Scoreprecision_rf <- confusion_matrix_rf[2, 2] / (confusion_matrix_rf[2, 1] + confusion_matrix_rf[2, 2])recall_rf <- confusion_matrix_rf[2, 2] / (confusion_matrix_rf[2, 2] + confusion_matrix_rf[1, 2])f1_rf <-2* (precision_rf * recall_rf) / (precision_rf+recall_rf)
Resultados
Las métricas de rendimiento para este modelo coinciden con el de Arbol de Decisión. A pesar de ello, a continuación se presenta una tabla con los resultados obtenidos para esta especificación:
Resultados del Modelo de Árbol de Decisión
Precisión, Recall y F1 Score (sobre corruption_binary=1)
Métrica
Valor
Precisión
0.75
Recall
0.90
F1 Score
0.82
Conclusiones
En este proyecto, se analizaron datos socioeconómicos y demográficos de 110 países para investigar la relación entre diferentes variables y el índice de corrupción. Los modelos predictivos, como la Regresión Lineal Múltiple, la Regresión Logística, el Árbol de Decisión, y el Random Forest, permitieron explorar tanto la predicción del índice de corrupción como la clasificación de los países en “corruptos” o “no corruptos”. A continuación, se destacan las principales conclusiones:
Relación entre ingreso y corrupción:
El análisis realizado permitió fundamentar de manera estadística que el ingreso anual per cápita y el índice de poder adquisitivo son variables clave para predecir el índice de corrupción. Aquellos países con mayor nivel de ingreso anual tienden a tener menores niveles de corrupción. En esta línea nos inducía a pensar la Figura 4.
Desempeño de los modelos:
El modelo de Regresión Logística mostró el mejor desempeño, con una precisión del 86% y un F1 Score de 0.87, lo que indica que este modelo es eficaz para clasificar a los países según su nivel de corrupción.
Los modelos de Random Forest y Árbol de Decisión también obtuvieron buenos resultados, aunque con una leve disminución en la precisión en comparación con Regresión Logística.
Importancia de las variables geográficas:
La inclusión de la variable continente permitió identificar diferencias significativas en los niveles de corrupción entre regiones geográficas, que pueden ser visualizadas en la Figura 2. Hecho que refuerza la hipótesis de que las características socioeconómicas y políticas regionales influyen en la percepción y el nivel real de corrupción en un país.
Implicaciones socioeconómicas:
Los resultados obtenidos podrían ser de utilidad para responsables de políticas públicas que busquen reducir los niveles de corrupción. Enfocar sus esfuerzos en la mejora del bienestar económico parecería ser un camino que contribuye a la disminución del Índice de Corrupción a nivel mundial.
Limitaciones
A pesar de los resultados obtenidos, el presente estudio tiene varias limitaciones que deben tenerse en cuenta:
Valores faltantes e imputación de datos:
Durante el proceso de minería de datos, fue necesario realizar imputaciones manuales en las celdas con valores faltantes. El método empleado, que fue el de la imputación por la mediana de las variables, permitió evitar la pérdida de datos. A pesar de ello, puede haber introducido sesgos sobre las observaciones con valores faltantes, al tratarse de una imputación arbitraria.
En adición a ello, algunas variables como el turismo fueron descartadas debido a la gran cantidad de datos faltantes. Esta y otras variables descartadas podrían haber aportado mayor poder explicativo a los modelos estimados.
Simplificación de la variable de corrupción:
El índice de corrupción se trató como una variable continua y posteriormente como una variable binaria para simplificar la clasificación de los países. Esta simplificación puede haber reducido la capacidad del modelo para captar toda la complejidad de los factores que influyen en la corrupción.
Desbalance de clases:
La clasificación de los países como corruptos o no corruptos se basó en la mediana del índice de corrupción. Ello puede haber generado un desbalance en las clases que afectara el rendimiento de los modelos.
Factores externos no considerados:
Aunque se incluyeron variables socioeconómicas importantes, existen factores políticos, históricos y culturales que no se consideraron en este análisis y que podrían tener un impacto significativo en los niveles de corrupción de un país. La ausencia de estas variables puede haber limitado la capacidad predictiva de los modelos.
Glosario de Métricas de Rendimiento
En este apartado se presentan las definiciones de las métricas de rendimiento utilizadas para evaluar los modelos predictivos en el análisis:
1. Precisión (Precision)
La precisión mide la proporción de predicciones correctas positivas (verdaderos positivos) en relación con el total de predicciones positivas (verdaderos positivos más falsos positivos). Es una métrica clave para evaluar qué tan preciso es un modelo a la hora de identificar correctamente los ejemplos positivos.
Un valor de precisión cercano a 1 indica que el modelo comete pocos falsos positivos.
Un valor bajo sugiere que el modelo predice incorrectamente muchos falsos positivos.
2. Recall (Sensibilidad o Tasa de Verdaderos Positivos)
El recall mide la capacidad del modelo para identificar todos los ejemplos positivos reales. Es la proporción de verdaderos positivos detectados por el modelo en relación con el total de ejemplos positivos reales (verdaderos positivos más falsos negativos).
Un recall alto significa que el modelo detecta la mayoría de los casos positivos.
Un recall bajo indica que el modelo pasa por alto muchos ejemplos positivos (es decir, comete muchos falsos negativos).
3. F1 Score
El F1 Score es la media armónica entre la precisión y el recall. Se utiliza como una métrica equilibrada que toma en cuenta tanto los falsos positivos como los falsos negativos, y es especialmente útil cuando existe un desbalance en las clases o cuando es necesario un equilibrio entre precisión y recall.
Un F1 Score cercano a 1 indica un buen equilibrio entre precisión y recall.
Un valor bajo de F1 Score sugiere que el modelo no logra un buen equilibrio entre falsos positivos y falsos negativos.
4. Matriz de Confusión
Una matriz de confusión es una tabla que permite evaluar el desempeño de un modelo de clasificación, mostrando las verdaderas etiquetas frente a las predicciones realizadas por el modelo. La matriz incluye:
Falsos Negativos (FN): Casos positivos que fueron incorrectamente predichos como negativos.
5. Exactitud (Accuracy)
La exactitud mide la proporción total de predicciones correctas (positivas y negativas) en relación con el total de predicciones realizadas por el modelo.
Un valor alto de exactitud indica que el modelo predice correctamente la mayoría de los ejemplos, pero puede no ser confiable si las clases están desbalanceadas.
6. Especificidad (Specificity)
La especificidad mide la proporción de verdaderos negativos en relación con el total de ejemplos negativos. Esta métrica es relevante cuando es importante evitar falsos positivos.