Importe de Librerías

library(dplyr)
library(tibble)

Lectura de Datos

# 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 = ','
)

Caracterización de Capítulos (Tablas) de la Base de Datos

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:

Preprocesamiento de Datos

Generación de la Etiqueta

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:

  1. Cambiar los valores de P6051, así:

\[ \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")

Filtrado y Generación de Variables de Entrada

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)

Base de datos:

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:

Medidas de localizacion

Medidas de localizacion
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
Desviaciones Estandar
Edad Ingreso mensual del Hogar Personas en el hogar Hijos
16.34195 2732601 1.696472 1.198253

Variable Sexo (P6020)

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

Variable Estado Civil (P5502)

  • \(1\) No está casado(a) y vive en pareja hace menos de dos años
  • \(2\) No está casado(a) y vive en pareja hace dos años o más
  • \(3\) Está viudo(a)
  • \(4\) Está separado(a) o divorciado(a)
  • \(5\) Está soltero(a)
  • \(6\) Está casado(a)

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

Variable Etnia (P6080)

  • \(1\) Indígena
  • \(2\) Gitano(a) (Rom)
  • \(3\) Raizal del archipiélago de San Andrés, Providencia y Santa Catalina
  • \(4\) Palenquero (a) de San Basilio
  • \(5\) Negro (a), mulato (a)(afrodescendiente),afrocolombiano(a)
  • \(6\) Ninguno de los anteriores

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

Variable Región (REGION)

Pregunta relacionada a la región en que se encuentra el hogar de la persona encuestada, donde;

  • \(1\) Caribe
  • \(2\) Oriental
  • \(3\) Central
  • \(4\) Pacifica(sin valle)
  • \(5\) Bogota
  • \(6\) Antioquia
  • \(7\) Valle del Cauca
  • \(8\) San Andrés
  • \(9\) Orinoquía - Amazonía

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

Variable Estrato (P8520S1A1)

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:

  • \(0\) Recibos sin estrato o el servicio es pirata
  • \(1\) Bajo - Bajo
  • \(2\) Bajo
  • \(3\) Medio - Bajo
  • \(4\) Medio
  • \(5\) Medio - Alto
  • \(6\) Alto
  • \(8\) Planta eléctrica
  • \(9\) No conoce el estrato o no cuenta con recibo de pago.

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

Variable Nivel Educativo (P8587)

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;

  • \(1\) Ninguno
  • \(2\) Preescolar
  • \(3\) Básica Primaria \((1º - 5º)\)
  • \(4\) Básica secundaria \((6º-9º)\)
  • \(5\) Media \((10º-13º)\)
  • \(6\) Técnico sin título
  • \(7\) Técnico con título
  • \(8\) Tecnológico sin título
  • \(9\) Tecnológico con título
  • \(10\)Universitario sin titulo
  • \(11\)Universitario con titulo
  • \(12\)Postgrado sin titulo
  • \(13\) Postgrado con titulo

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.

Relaciones entre las variables y nuestra etiqueta (Hijos)

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

Importe de librería para el modelo de clasificación

# Modelo de clasificación utilizando KNN

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 y PyCaret

\(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:

  • categorical_features : Se señala las columnas que son categóricas
  • numerical_features: Se señalan las columnas que son numéricas
  • Target: Se establece la columna objetivo o de etiqueta para el modelo
  • use_gpu: Se habilita el uso de GPU para el entrenamiento

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.

## Muestreo y separación en entrenamiento y validación

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,]

## Preprocesamiento

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.

## Entrenamiento y control

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

## Validación y pruebas

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)

Matriz de confusión

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
[1]
Dirección de Metodología y Producción Estadística, Encuesta Nacional de Calidad de Vida 2019.” 2020 [Online]. Available: http://microdatos.dane.gov.co/index.php/catalog/678/study-description. [Accessed: 01-Apr-2021]

  1. 1:https://topepo.github.io/caret/↩︎

  2. 1:https://colab.research.google.com/↩︎

  3. 2:https://pycaret.org/↩︎