library(dplyr)
library(tibble)
# Todos los nombres de los archivos .csv en el directorio `/data`
# precedido por el directorio.
file_paths <-
list.files(path = '../data',
pattern = '*.csv',
full.names = TRUE)
# Todos los nombres de los archivos .csv en el directorio `/data`.
file_names <-
list.files(path = '../data',
pattern = '*.csv')
# Importa todos los archivos de la lista `file_paths`
# y los almacena en la lista `files`.
files <- lapply(
file_paths,
read.table,
header = TRUE,
sep = ';',
dec = ','
)
| Tabla | Público encuestado |
|---|---|
| Datos de la vivienda | Vivienda |
| Servicios del hogar | Hogar |
| Características y composición del hogar | Persona |
| Salud | Persona |
| Atención integral de los niños y niñas menores de 5 años | Persona, edad < 5 |
| Educación | Persona, edad > 5 |
| Fuerza de trabajo | Persona, edad > 12 |
| Tecnologías de información y comunicación | Persona, edad > 5 |
| Trabajo infantil | Persona, 5 < edad < 11 |
| Tenencia y financiación de la vivienda que ocupa el hogar | Hogar |
| Condiciones de vida del hogar y tenencia de bienes | Hogar |
| Uso de energéticos del hogar | Hogar |
Con esta caracterización, podemos concluir que se encuestaron:
En la tabla Características y composición del hogar, la variable P6051 indica el parentesco de la persona encuestada con el jefe de hogar. Cuando esta variable es 3, significa que la persona es un hijo del jefe de hogar. Por lo tanto, podemos computar cuántos hijos hay en un hogar de la siguiente manera:
\[
\begin{cases}
1 & \text{cuando } P6051 = 3 \\
0 & \text{en otro caso }
\end{cases}
\] 2. Agrupamos por hogar. Según los metadatos y la estructura de la encuesta [1], las claves de un hogar en las tablas de personas (como Características y composición del hogar) son ï..DIRECTORIO el cual es único para cada vivienda y SECUENCIA_P, el cual es único por cada hogar dentro de esa vivienda.
# Trae la tabla de Características y composición del hogar
# donde se encuentra la variable P6051.
carac_persona <- files[[2]]
labeled <-
carac_persona %>% mutate(P6051 = if_else(P6051 == 3, 1, 0)) %>% group_by(ï..DIRECTORIO, SECUENCIA_P) %>% summarise(HIJOS = sum(P6051), .groups = "keep")
Seleccionamos las siguientes variables de la base de datos:
| Tabla | Variable | Etiqueta | Tipo | Formato |
|---|---|---|---|---|
| Características y composición del hogar | P6020 | Sexo | Discreta | Numérica |
| Características y composición del hogar | P6040 | Edad | Discreta | Numérica |
| Característiicas y composición del hogar | P5502 | Estado civil | Discreta | Numérica |
| Característiicas y composición del hogar | P6080 | Pueblo o etnia | Discreta | Numérica |
| Educación | P8587 | Nivel educativo | Discreta | Numérica |
| Datos de la vivienda | REGION | Región | Discreta | Numérica |
| Datos de la vivienda | P8520S1A1 | Estrato | Discreta | Numérica |
| Servicios del hogar | I_HOGAR | Ingreso mensual total del hogar | Discreta | Numérica |
# Cogemos cada tabla
carac_hogar <- files[[2]] # Personas
edu <- files[[7]] # Personas
servicios_hogar <- files[[10]] # Hogar
vivienda <- files[[6]] # Vivienda
# Definimos columnas a seleccionar de cada tabla
carac_cols <- c('ï..DIRECTORIO', 'SECUENCIA_P', "SECUENCIA_ENCUESTA", 'P6051', 'P6020', 'P6040', 'P5502', 'P6080')
edu_cols <- c('ï..DIRECTORIO', 'SECUENCIA_P', "SECUENCIA_ENCUESTA", 'P8587')
vivienda_cols <- c('ï..DIRECTORIO', 'REGION', 'P8520S1A1')
servicios_cols <- c('ï..DIRECTORIO', 'SECUENCIA_ENCUESTA', 'I_HOGAR', 'CANT_PERSONAS_HOGAR')
###
all_input <- carac_hogar %>% select(any_of(carac_cols)) %>% filter(P6051 == 1)
all_input <- all_input %>% left_join(select(edu, any_of(edu_cols)), by = c("ï..DIRECTORIO", "SECUENCIA_P", "SECUENCIA_ENCUESTA"))
all_input <- all_input %>% left_join(select(servicios_hogar, any_of(servicios_cols)), by = c("ï..DIRECTORIO", "SECUENCIA_ENCUESTA"))
all_input <- all_input %>% left_join(select(vivienda, any_of(vivienda_cols)), by = c("ï..DIRECTORIO"))
write.csv(all_input,"../db/input_with_dir.csv", row.names = FALSE)
###
# Drop DIRECTORIO, SECUENCIA_P y SECUENCIA_ENCUESTA
clean_input <- all_input %>% select(-c("ï..DIRECTORIO", "SECUENCIA_P", "SECUENCIA_ENCUESTA", "P6051"))
write.csv(clean_input,"../db/input.csv", row.names = FALSE)
labeled_input <- all_input %>% left_join(labeled, by = c('ï..DIRECTORIO', 'SECUENCIA_P'))
# Removemos datos anómalos basados en la columna I_HOGAR
clean_labeled_input <- labeled_input %>% select(-c("ï..DIRECTORIO", "SECUENCIA_P", "SECUENCIA_ENCUESTA", "P6051")) %>% filter(I_HOGAR < 100000000)
## Convertimos las variables categóricas a factores
clean_labeled_input$P6020 <- as.factor(clean_labeled_input$P6020)
clean_labeled_input$P5502 <- as.factor(clean_labeled_input$P5502)
clean_labeled_input$P6080 <- as.factor(clean_labeled_input$P6080)
clean_labeled_input$P8587 <- as.factor(clean_labeled_input$P8587)
clean_labeled_input$REGION <- as.factor(clean_labeled_input$REGION)
clean_labeled_input$P8520S1A1 <- as.factor(clean_labeled_input$P8520S1A1)
clean_labeled_input$HIJOS <- as.factor(clean_labeled_input$HIJOS)
write.csv(clean_labeled_input,"../db/labeled_input.csv", row.names = FALSE)
La base de datos fue tomada de la Encuesta Nacional de calidad de vida en Colombia por el DANE y después de un proceso de limpieza y depuración de los datos. Hemos selecciona las siguientes variables de interés:
| Edad | Ingreso mensual del Hogar | Personas en el hogar | Hijos | |
|---|---|---|---|---|
| Min. : 13.00 | Min. : 0 | Min. : 1.000 | Min. : 0.000 | |
| 1st Qu.: 36.00 | 1st Qu.: 530000 | 1st Qu.: 2.000 | 1st Qu.: 0.000 | |
| Median : 48.00 | Median : 1021667 | Median : 3.000 | Median : 1.000 | |
| Mean : 48.86 | Mean : 1725545 | Mean : 3.085 | Mean : 1.117 | |
| 3rd Qu.: 61.00 | 3rd Qu.: 1880073 | 3rd Qu.: 4.000 | 3rd Qu.: 2.000 | |
| Max. :106.00 | Max. :94500000 | Max. :19.000 | Max. :11.000 |
| Edad | Ingreso mensual del Hogar | Personas en el hogar | Hijos |
|---|---|---|---|
| 16.34195 | 2732601 | 1.696472 | 1.198253 |
Las edades de las cabezas de hogares encuestadas en Colombia se encuentra en un rango de 13 años a 106 años con promedio de 49 años de edad que varían en +- 16 años .
Los ingresos mensuales de los hogares colombianos encuestados se encuentran en un rango de 530.000 a 284.600.000 pesos colombianos con un promedio de 1.726.408 pesos colombianos por hogar que varían en +- 2.948.921 pesos colombianos. Se hace énfasis en que un valor que describe mejor los ingresos mensuales de un hogar colombiano es la mediana que nos indica que el 50% de los hogares encuestados tienen ingresos mensuales de 1.021.667 pesos Colombianos .
El numero de personas en los hogares Colombianos encuestados se encuentran en un rango de 1 persona a 19 personas por hogar con un promedio de 3 personas por hogar que varían +- 2 personas.
Los hijos de los hogares colombianos encuestados se encuentran en un rango de 0 hijos a 11 hijos con un promedio de 1 hijo por hogar y varían en +- 1 hijo.
A continuación se muestra el conteo de la variable sexo con \(1\) siendo Hombre y \(2\) siendo Mujer mostrando una variable dicotómica con una muestra de mas hombres que mujeres
| Sexo | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 1 | 59880 | 0.6371026 |
| 2 | 34108 | 0.3628974 |
De la muestra se puede ver que aproximadamente el 35% de los encuestados no estan casados y viven en pareja hace mas de 2 años
| Estado Civil | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 1 | 2317 | 0.0246521 |
| 2 | 33747 | 0.3590565 |
| 3 | 7963 | 0.0847236 |
| 4 | 16077 | 0.1710538 |
| 5 | 12080 | 0.1285270 |
| 6 | 21804 | 0.2319871 |
En cuanto a la etnia , el 80% no pertencen a ninguna etnia o pueblo especifico de alguno de los presentados por la encuesta
| Etnia | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 1 | 8875 | 0.0944269 |
| 2 | 38 | 0.0004043 |
| 3 | 309 | 0.0032877 |
| 4 | 54 | 0.0005745 |
| 5 | 9168 | 0.0975444 |
| 6 | 75544 | 0.8037622 |
Pregunta relacionada a la región en que se encuentra el hogar de la persona encuestada, donde;
Se puede ver que la región con mayor cantidad de encuestados pertenece a la región \(Caribe\) seguido por la región \(Central\), ademas,las regiones con menos encuestados son \(San Andrés\) y \(Bogota\). Probablemente debido a la extención de tierra de las dos regiones.
| Region | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 1 | 21326 | 0.2269013 |
| 2 | 16698 | 0.1776610 |
| 3 | 20260 | 0.2155594 |
| 4 | 9125 | 0.0970869 |
| 5 | 2072 | 0.0220454 |
| 6 | 3669 | 0.0390369 |
| 7 | 3231 | 0.0343767 |
| 8 | 1230 | 0.0130868 |
| 9 | 16377 | 0.1742456 |
Pregunta relacionada con el estrato en que se encuentra el hogar de la persona encuestada, basado en la información presentada por el recibo de servicios públicos de energía eléctrica, donde:
Notese que los valores categoricos de nivel se encuentran entre los valores \(0 - 6\), mientras que los valores \(8\) y \(9\) se refieren al uso de una planta eléctrica o el desconocimiento de la unformación. Por otro lado se puede ver que la mayoria de encuestados pertencen a a los estratos \(bajo-bajo\) y \(bajo\) según el recibo de servicios públicos
| Estrato | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 0 | 3999 | 0.0450368 |
| 1 | 45058 | 0.5074442 |
| 2 | 23620 | 0.2660090 |
| 3 | 7960 | 0.0896457 |
| 4 | 2420 | 0.0272541 |
| 5 | 1147 | 0.0129175 |
| 6 | 700 | 0.0078834 |
| 8 | 2005 | 0.0225804 |
| 9 | 1885 | 0.0212289 |
Pregunta relacionada al nivel educativo más alto alcanzado por la cabeza del hogar y el último año o grado aprobado en este nivel, donde;
Los valores \(3\),\(4\) y \(5\) corresponden a mas del \(70\%\) de la población, encontrandose un nivel educativo promedio en basica secundaria, y media.
| Nivel Educación | Frecuencia | Frecuencia.Abs. |
|---|---|---|
| 1 | 8603 | 0.0938956 |
| 10 | 779 | 0.0085022 |
| 11 | 5536 | 0.0604215 |
| 12 | 60 | 0.0006549 |
| 13 | 1780 | 0.0194274 |
| 3 | 36758 | 0.4011875 |
| 4 | 12350 | 0.1347915 |
| 5 | 18845 | 0.2056798 |
| 6 | 297 | 0.0032415 |
| 7 | 4721 | 0.0515264 |
| 8 | 135 | 0.0014734 |
| 9 | 1759 | 0.0191982 |
Del grafico anterior se observa:
La Edades de las cabezas de hogar encuestadas se concentran en 35 y 55 años de edad.
Los Ingresos mensuales en los hogares colombianos presentan valores Extremos Atípicos algo que se esperaba después de ver los resúmenes numéricos donde el promedio de los ingresos mensuales se encontraba 1.726.408 pesos colombianos y variaba +- 2.948.921 pesos colombianos.
Los Estratos predominantes en la encuesta de los hogares colombiano son el estrato 1, estrato 2 y estrato 3.
La mayoría de hogares colombianos tiene un numero de personas en el hogar entre 1 persona y 5 personas por hogar.
La mayoría de hogares colombianos tiene un numero de hijos entre 0 hijos y 2 hijos por hogar.
El 64% de las cabezas de hogar encuestadas son Hombres y el 36% son Mujeres.
Variables Categoricas:
No se alcanza a percibir algún tipo de relación en el grafico anterior.
Variables Continuas:
Se observan una posible relación fuerte entre el Número de personas en el hogar y la variable hijo.
| Correlacion de las Peronas del Hogar entre el Numero de Hijos |
|---|
| 0.8095036 |
Efectivamente dichas variables están relacionadas
Debido a la cantidad de variables utilizadas \((10)\) ,y el tamaño final de la muestra \((93992)\) el entrenamiento y la búsqueda del \(K\) optimo es una tarea complicada para desarrollarla por métodos comunes como ciclos iterativos, por lo cual, se da uso de la librería \(Caret\)1 de \(R\), la librería, cuenta con distintos modelos para regresión y clasificación, junto con diferentes parámetros que permiten calibrar el entrenamiento al gusto del modelador ayudando a modificar el tipo de entrenamiento, e l preprocesamiento, parámetros específicos de cada modelo (como los vecinos cercanos \(K\) para KNN) ,entre otras. Para el entrenamiento de nuestro modelo por el método \(KNN\) era preciso saber el valor \(K\) que, aunque la librería \(Caret\) lo permitía, el tiempo transcurrido en cada entrenamiento y los recursos computaciones que implicaban impedían hacer pruebas de manera continua, tardando aproximadamente \(15\) minutos por entrenamiento, ya que la librería genera distintos modelos hasta encontrar el K que genere el mejor modelo entre todos, por esta razón, se optó por usar una herramienta externa que permitiera usar GPU que ayudaran a optimizar el entrenamiento de los diferentes modelos y hacer las pruebas a diferentes configuraciones.
\(Google Colab\)2 es un entorno de desarrollo en línea de lenguaje Python que da al usuario herramientas como GPUs gratuitas, notebooks sin instalación local, acceso en línea, y disposición de librerías Python, de esta manera, \(Colab\) permitió el entrenamiento de forma remota de nuestro modelo de prueba. Además, era necesaria una librería que trabajara de forma parecida a \(Caret\), en lenguaje Python, por esto, se usa \(PyCaret\)3 , esta es una librería de acceso libre enfocada en machine learning con el propósito de implementar las herramientas de \(Caret\) en un entorno Python, funcionando de la misma forma y manteniendo su simplicidad en aplicación. Dando uso de estas herramientas se procede a buscar el K optimo subiendo la base de datos a un notebook de \(Colab\) como un dataframe, y por medio de \(Pycaret\) se procede a entrenar el modelo con los siguientes parámetros:
De esta forma la librería, reconoce el tipo de dato ingresado como categórico o numérico, genera un conjunto de entrenamiento de \(47981\) datos, y un conjunto de test de \(20564\), haciendo una distribución \(70\%-30\%\) respectivamente, los conjuntos formados llevan un preprocesamiento donde normaliza las variables numéricas y permite el correcto funcionamiento del modelo, generando un \(K\) optimo igual a \(5\), valor que es de gran interés puesto que es el valor que se asignara a la librería \(Caret\) y generará el modelo final.
Se fija la semilla generadora de números aleatorios para garantizar la reproducibilidad de los resultados.
Adicionalmente, se separa el marco de datos en datos de entrenamiento y datos de validación o prueba. La proporción utilizada fue de \(0.8\) para entrenamiento y \(0.2\) para validación.
# Semilla para generación de números aleatorios
set.seed(20210406)
# Asignación de índices del dataset para training 0.8 y testing 0.2
indexTraining <- createDataPartition(y = clean_labeled_input$HIJOS, p = 0.8, list = FALSE)
# Parte del dataset designada para training
train <- clean_labeled_input[indexTraining,]
# Parte del dataset designada para testing
test <- clean_labeled_input[-indexTraining,]
Adicional al preprocesamiento realizado anteriormente para la obtención del conjunto de datos inicial, se realiza un pre-procesamiento específico al modelo KNN. Debido a que KNN es muy sensible a la distancia entre los datos y las unidades de estos, es necesario normalizar los datos antes de entrenar el modelo. El paquete caret hace este paso de normalización por nosotros, como una de las opciones a la hora de entrenar el modelo.
Debido a que se tiene una cantidad significativa de datos \((93993)\), se decide no hacer imputación de datos, y en cambio, omitir los valores que contengan información incompleta.
Se realiza el entrenamiento del modelo utilizando el método KNN, sin utilizar ningún método de re-muestreo y haciendo el pre-procesamiento mencionado anteriormente (normalización).
# Habilitar cluster para trabajo en paralelo
cluster <- makeCluster(detectCores() - 2) # habilitar n - 2 threads de la cpu
registerDoParallel(cluster)
# none: sin resampling ni validación cruzada
ctrl <- trainControl(method="none", allowParallel = TRUE)
# modelo
knn_fit <- train(HIJOS ~., data = train, method = "knn", trControl = ctrl, preProcess = c("center","scale"), tuneGrid=data.frame(k=5), na.action = na.omit)
# detener procesamiento en paralelo
stopCluster(cluster)
# Guardamos el modelo
saveRDS(knn_fit, './knnModel.rds')
# veamos cuál valor de K fue seleccionado como el mejor
knn_fit
## k-Nearest Neighbors
##
## 75194 samples
## 9 predictor
## 12 classes: '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'
##
## Pre-processing: centered (41), scaled (41)
## Resampling: None
Se utiliza el conjunto de prueba para comprobar la validez del modelo y se visualiza su desempeño con la matriz de confusión.
# Probamos el modelo con el conjunto de prueba
test_pred <- predict(knn_fit, newdata = test, na.action = na.omit)
Se observa que la precisión del modelo es de aproximadamente 0.65.
# omitimos las filas NA
test_clean <- na.omit(test)
cm <- confusionMatrix(test_pred, test_clean$HIJOS)
knitr::kable(cm$table) %>% kable_material_dark()
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 5305 | 947 | 239 | 50 | 9 | 2 | 0 | 0 | 0 | 0 | 0 | 0 |
| 1 | 1092 | 3023 | 797 | 182 | 28 | 8 | 4 | 0 | 0 | 0 | 0 | 0 |
| 2 | 268 | 783 | 2220 | 431 | 89 | 31 | 6 | 2 | 0 | 0 | 0 | 0 |
| 3 | 31 | 145 | 289 | 645 | 146 | 28 | 9 | 0 | 1 | 0 | 0 | 0 |
| 4 | 4 | 22 | 67 | 89 | 147 | 41 | 14 | 2 | 1 | 1 | 0 | 0 |
| 5 | 2 | 6 | 11 | 16 | 21 | 20 | 8 | 1 | 3 | 0 | 0 | 0 |
| 6 | 0 | 2 | 2 | 2 | 2 | 4 | 5 | 4 | 0 | 0 | 0 | 0 |
| 7 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
| 8 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 10 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 11 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
knitr::kable(cm$overall) %>% kable_material_dark(c("striped"))
| x | |
|---|---|
| Accuracy | 0.6565948 |
| Kappa | 0.5203291 |
| AccuracyLower | 0.6494661 |
| AccuracyUpper | 0.6636709 |
| AccuracyNull | 0.3871974 |
| AccuracyPValue | 0.0000000 |
| McnemarPValue | NaN |