1.a) Explore y explique qué características posee el dataset MPI_national.csv.
data = read.csv('https://raw.githubusercontent.com/dm-uba/dm-uba.github.io/master/2021/laboratorios/LAB03/MPI_national.csv', sep = ",",header = TRUE)
Observamos la estructura del dataset y las características de las variables numéricas:
library(tidyverse)
library(knitr)
# Structura de variables no numéricas
# Para usar nagate y %>% usar librería tidyverse
data %>% select_if(negate(is.numeric)) %>% str(data)
## 'data.frame': 102 obs. of 2 variables:
## $ ISO : chr "KAZ" "SRB" "KGZ" "TUN" ...
## $ Country: chr "Kazakhstan" "Serbia" "Kyrgyzstan" "Tunisia" ...
# Veamos el rango de las variables numéricas
# Kable del paquete knitr sirve para mostrar en formato tabla
# El rango de las variables para zonas rurales es mayor que el rango de la misma variable para zona urbanas.
kable(data %>% select_if(is.numeric) %>% summarise_each(funs(range)))
MPI.Urban | Headcount.Ratio.Urban | Intensity.of.Deprivation.Urban | MPI.Rural | Headcount.Ratio.Rural | Intensity.of.Deprivation.Rural |
---|---|---|---|---|---|
0.000 | 0.0 | 33.3 | 0.000 | 0.09 | 33.3 |
0.459 | 82.5 | 55.7 | 0.669 | 96.92 | 69.5 |
# Cantidad de paises
length(unique(data$Country))
## [1] 102
Se cuenta con tres tipos de mediciones recolectadas para zonas urbanas y rurales en 102 países en desarrollo. Existe un solo registro por país (largo de dataset coincide con cantidad de paises). Se desconoce la fecha del relevamiento de los datos. El rango de las variables para zonas rurales es mayor que el rango de la misma variable para zona urbanas.
1.b) Cuál es la distribución de las variables:
# Calculamos el desvío standard y comparamos las zonas para cada medida
sd <- round(apply(data[,c(3:8)],2, sd),2)
sd[order(names(sd))]
## Headcount.Ratio.Rural Headcount.Ratio.Urban
## 33.27 18.50
## Intensity.of.Deprivation.Rural Intensity.of.Deprivation.Urban
## 8.78 5.14
## MPI.Rural MPI.Urban
## 0.20 0.09
Las zonas rurales presentan mayor dispersión comparadas con la misma métrica en zonas urbanas.
# Para el histograma usaremos ggplot2
library(ggplot2)
# Instalamos una librería que permite visualizar varios charts en una ventana
# https://github.com/thomasp85/patchwork
# devtools::install_github("thomasp85/patchwork")
library(patchwork)
## Warning: package 'patchwork' was built under R version 4.0.5
# Generamos los histogramas para zonas urbanas
tamaño_texto = 7
cant_bins = 30
u1 <- qplot(data$MPI.Urban, geom="histogram", main = "Hist MPI.Urban",
xlab = "MPI.Urban", ylab = "Frecuencia", binwidth=diff(range(data$MPI.Urban))/cant_bins) + theme(text = element_text(size = tamaño_texto))
u2 <- qplot(data$Headcount.Ratio.Urban, main = "Hist HC Ratio Urban", geom="histogram", xlab = "Headcount Ratio Urban", ylab = "Frecuencia", binwidth=diff(range(data$Headcount.Ratio.Urban))/cant_bins) + theme(text = element_text(size = tamaño_texto))
u3 <- qplot(data$Intensity.of.Deprivation.Urban, main = "Hist. Intensity of Depr. Urban", geom="histogram", xlab = "Intensity of Depr. Urban", ylab = "Frecuencia",binwidth=diff(range(data$Intensity.of.Deprivation.Urban))/cant_bins) + theme(text = element_text(size = tamaño_texto))
# Y los mismos para zonas Rurales
r1 <- qplot(data$MPI.Rural, geom="histogram", main = "Hist MPI Rural",
xlab = "MPI.Rural", ylab = "Frecuencia", binwidth=diff(range(data$MPI.Rural))/cant_bins) + theme(text = element_text(size = tamaño_texto))
r2 <- qplot(data$Headcount.Ratio.Rural, main = "Hist HC Ratio Rural", geom="histogram", xlab = "Headcount Ratio Rural", ylab = "Frecuencia", binwidth=diff(range(data$Headcount.Ratio.Rural))/cant_bins) + theme(text = element_text(size = tamaño_texto))
r3 <- qplot(data$Intensity.of.Deprivation.Rural, main = "Hist. Int. of Depr. Rural", geom="histogram", xlab = "Intensity of Depr. Rural", ylab = "Frecuencia",binwidth=diff(range(data$Intensity.of.Deprivation.Rural))/cant_bins) + theme(text = element_text(size = tamaño_texto))
# Y los graficamos en paralelo para facilitar la comparación
(u1 | u2 | u3) /
(r1 | r2 | r3)
Las zonas urbanas presentan una asimetría a derecha más marcada que las zonas rurales para MPI y Ratio de pobreza per capita, mientras que la distribución de Intensidad de Privaciones para ambas zonas se asemeja.
2.a) ¿Existen atributos que poseen valores atípicos?
Normalizamos de variables con z-score para que sean comparables. Este método también nos informa sobre a cuántos desvíos de la media se distribuyen los datos.
for(i in 3:ncol(data)) {
data[,i] <- (data[,i]-mean(data[,i]))/(sd(data[,i]))}
# Configuramos el tamaño del lienzo (ver "par" del paquete graphics)
par(mfrow=c(1, 1), mar=c(5, 12, 4, 2) )
# Y graficamos un solo boxplot con todas las medidas
boxplot(data[,c(3:8)],horizontal = TRUE, las=1, xlab="z-score")
stripchart(data[,c(3:8)], vertical = FALSE ,
method = "jitter", add = TRUE, pch = 20, col = 'blue')
# Que paises son outliers segun boxplot de MPI Urban
MPI.Urban_Umbral = boxplot(data[,c(3)])$stats[5]
data["Country"][data$MPI.Urban > MPI.Urban_Umbral,]
## [1] "Chad" "South Sudan"
# Que paises son outliers segun boxplot de Headcount Ratio Urban
HC.Ratio.Urban_umbral = boxplot(data[,c(4)])$stats[5]
data["Country"][data$Headcount.Ratio.Urban > HC.Ratio.Urban_umbral,]
## [1] "South Sudan"
Según el boxplot para MPI Urban Chad y South Sudan son países atípicos, mientras que si observamos el ratio de pobreza per capita para zonas urbanas (HC Ratio Urban), el país considerado outlier es South Sudan.
2.b) Seleccione una variable con outliers y analice utilizando los métodos univariados IRQ, SD, y Z-score
### Focalicemos el análisis en MPI Urban ###
variable = data$MPI.Urban
# Rango intercuartil
Q1 = as.numeric(quantile(variable)["25%"])
Q3 = as.numeric(quantile(variable))[4]
IQR = Q3 - Q1
lim_sup = Q3 + 1.5*IQR
lim_inf = Q1 - 1.5*IQR
print(lim_sup)
## [1] 2.403141
# Veamos de qué paises estamos hablando
# Coinciden con los detectados en el boxplot porque ambos usan 1.5 * IQR
sort(data$Country[variable > lim_sup])
## [1] "Chad" "South Sudan"
# con Z Score
# Ya hicimos el escalado a z-score
umbral_zscore =3
unique(sort(variable[variable> umbral_zscore]))
## [1] 4.062822
# Veamos de qué país estamos hablando
sort(data$Country[variable > umbral_zscore])
## [1] "South Sudan"
Utilizando z-score el pais que representa un valor atipico es South Sudan
# Bonus Track
# veamos que pasa con z-score modificado
data_original = read.csv('https://raw.githubusercontent.com/dm-uba/dm-uba.github.io/master/2021/laboratorios/LAB03/MPI_national.csv', sep = ",",header = TRUE)
variable_zm = data_original$MPI.Urban
mediana = median(variable_zm)
MAD = median(abs(variable_zm-mediana))
Zm = ((0.6745*(variable_zm - median(variable_zm))) / MAD)
umbral_Zm = 3.5
# Cuantos outliers identifica?
length(Zm[Zm > umbral_Zm])
## [1] 11
# Que paises conforman la lista?
sort(data$Country[Zm > umbral_Zm])
## [1] "Burkina Faso" "Burundi"
## [3] "Central African Republic" "Chad"
## [5] "Congo, Democratic Republic of the" "Ethiopia"
## [7] "Liberia" "Niger"
## [9] "Sierra Leone" "Somalia"
## [11] "South Sudan"
# Observemos el diagrama de dispersión
plot(Zm, main="Diag. de Dispersión Z Score Modificado")
abline(h=c(mediana, umbral_Zm), col=c("red","green"),lty=5)
legend(1,8 , legend=c("Mediana", "Zm Umbral"),col=c("red","green"), lty=5)
2.c) Observe qué ocurre con la distribución de la feature elegida en caso de eliminar los outliers. Grafique un boxplot de con la nueva distribución. Concluya al respecto
# Observemos la distribución de MPI Urban quitando los outliers identificados en el boxplot
MPI.Urban_nuevo_bp = boxplot(data[c(3)][data[c(3)] <= MPI.Urban_Umbral],main="MPI Urban sin outliers", ylab="z-score", xlab="MPI Urban acotada")
# Luego de eliminar los outliers, el nuevo valor atipico es:
MPI.Urban_nuevo_bp$out
## [1] 2.291073
# Este nuevo outlier corresponde a:
data["Country"][data[, "MPI.Urban"] == MPI.Urban_nuevo_bp$out,]
## [1] "Somalia"
Dada la asimetría de la distribución, luego de eliminar outliers, Somalía es ahora clasificado como atípico.
2.d) Extienda el análisis a 3 variables y analice si existen valores atípicos utilizando algún método multivariado.
# Distancia Mahalanobis para zonas urbanas
vector_medias = colMeans(data[,c(3:5)])
matriz_var_cov = cov(data[,c(3:5)])
# Creamos una variable con la distancia
data$maha = sqrt(mahalanobis(data[,c(3:5)],vector_medias,matriz_var_cov))
# Los 3 registros mas distantes
top_maha <- head(data[order(data$maha,decreasing = TRUE),],3)
print(top_maha$Country)
## [1] "South Sudan" "Chad" "Montenegro"
¿Qué países son considerados outliers según LOF?
library(Rlof)
data$LOF_score<-lof(data[,c(3:5)], k=5)
top_LOF <- head(data[order(data$LOF_score,decreasing = TRUE),],3)
print(top_LOF$Country)
## [1] "South Sudan" "Montenegro" "Trinidad and Tobago"
Veamos donde se ubican los puntos
library(scatterplot3d)
lof_3d <- scatterplot3d(x = data[,3], y = data[,4], z = data[,5],
color=ifelse(data$LOF_score>=min(top_LOF$LOF_score),"red","black"), xlab="z-score MPI Urban",ylab="z-score HC Ratio Urban",zlab="z-score Intensity of Deprivation Urban" , main="3D Scatterplot LOF Scores")
# Uso el método xyz.convert para convertir el objeto a 2 variables
lof_3d.coords <- lof_3d$xyz.convert(data[,3], data[,4], data[,5])
# Asigno como etiquetas los nombres de los países
text(lof_3d.coords$x,
lof_3d.coords$y,
labels = ifelse(data$LOF_score>=min(top_LOF$LOF_score),data$Country,""),
cex = .5,
pos = 4)
Ahora hagamos el análisis utilizando Isolation Forest
library(isotree)
library(ggplot2)
# Entrenamos el modelo IForest y nos quedamos con el score
# Utilizamos el conjunto de datos en su totalidad
data$iforest= isolation.forest(data[,c(3:5)], ntrees = 100, ndim=1, random_seed = 13, output_score=TRUE)$score
library(dplyr)
library(highcharter)
# Set highcharter options
options(highcharter.theme = hc_theme_smpl(tooltip = list(valueDecimals = 2)))
hchart(data$iforest, name="iForest score", color="green") %>%
hc_title(text = "Histograma Scores Isolation Forest")
top_iforest <- head(data[order(data$iforest,decreasing = TRUE),],3)
print(top_iforest$Country)
## [1] "South Sudan" "Chad" "Somalia"
#
iforest_3d <- scatterplot3d(x = data[,3], y = data[,4], z = data[,5],
color=ifelse(data$iforest>=min(top_iforest$iforest),"red","black"), xlab="z-score MPI Urban",ylab="z-score HC Ratio Urban",zlab="z-score Intensity of Deprivation Urban", main="3D Scatterplot Isolation Forest Scores")
# Uso el método xyz.convert para convertir el objeto a 2 variables
iforest_3d.coords <- iforest_3d$xyz.convert(data[,3], data[,4], data[,5])
# Asigno el nombre de país como etiquetas
text(iforest_3d.coords$x,
iforest_3d.coords$y,
labels = ifelse(data$iforest>=min(top_iforest$iforest),data$Country,""),
cex = .5,
pos = 4)
## Warning: package 'rgl' was built under R version 4.0.5
# Ejemplo de un gráfico 3D interactivo
# Prueben el código como R Script
library(rgl)
data$Outlier_iforest = ifelse(data$iforest>=min(top_iforest$iforest),"S","N" )
colors <- c("royalblue1", "red")
data$color <- colors[ as.numeric( as.factor(data$Outlier_iforest) ) ]
# Para poder interactuar uso plot3d
plot3d(x=data[,3], y=data[,4],z=data[,5], col = data$color, type = "s", size = 1, xlab="z-score MPI Urban",ylab="z-score HC Ratio Urban",zlab="z-score Intensity of Deprivation Urban" )
# rglwidget()
Veamos ahora cómo sería la implementación utilizando un muestreo en las iteraciones de los árboles. No podemos utilizar el tamaño de muestra recomendado (256) porque solo contamos con 102 registros en el conjunto de datos:
#Ajusto el modelo
# Agregamos el parámetro sample_size
iforest_sample = isolation.forest(data[,c(3:5)], ntrees = 100, ndim=1, random_seed = 13, sample_size = 50)
# Utilizo el método predict para calcular el score sobre todos los registros
data$iforest_pred = predict(iforest_sample, data[,c(3:5)])
# Qué países identifico?
top_iforest_sample <- head(data[order(data$iforest_pred,decreasing = TRUE),],3)
print(top_iforest_sample$Country)
## [1] "South Sudan" "Chad" "Somalia"