Cargar paquetes

library(pacman)
p_load("class","caret","tidyr","mlbench","e1071","ggplot2","knitr","dplyr","readr", "prettydoc","xfun")

Vecino cercano

Esta práctica se basa en una de las aplicaciones de algoritmos de clasificación, desarrolladas con K-NN y árboles de decisión.

K-NN (o k-vecinos) es un sistema de clasificación basado en la comparación de instancias nuevas con instancias presentes en el juego de datos de entrenamiento. La construcción de grupos se realiza a partir del propio juego de datos de entrenamiento, tomando como referencia el parámetro k indicado por el investigador. El algoritmo de los K vecinos más cercanos es uno de los algoritmos más simples que existen que muestran la esencia del aprendizaje basado en instancias. Este algoritmo asume que todas las instancias corresponden a puntos que se encuentran en un espacio de dimensión n. El vecino más cercano de una instancia es definido en términos de la distancia Euclidiana estándar.

Los árboles de decisión son algoritmos que construyen modelos de decisión que forman estructuras similares a los diagramas de flujo donde los nodos internos suelen ser puntos de decisión sobre un atributo del juego de datos. Son muy dependientes del concepto de ganancia de la información ya que es el criterio que utilizan para construir las ramificaciones del árbol. A grandes rasgos existen dos tipos de árboles de decisión: * Árboles de decisión simples: el resultado se construye mediante un proceso de clasificación. * Árboles de decisión múltiples (random forest): el resultado se construye mediante el desarrollo iterativo de n procesos de clasificación.

Introducción al k Nearest Neighbors (kNN) en R

El algoritmo kNN es muy sencilla: me guardo la tabla de datos de entrenamiento y cuando me llegue un nuevo dato, encuentro los k observaciones (vecinos) más cercanos y hago la clasificación en base a esas observaciones. Al fin y al cabo, es de esperar que observaciones cercanas sean similares a la nueva observación.

Como ves, aquí vemos una gran diferencia respecto a la mayoría de algoritmos supervisados, y es que se trata de un algoritmo no paramétrico. Es decir, que el algoritmo no debe aprender el valor de ningún parámetro, por lo que no hay un proceso de entrenamiento como tal.

Así pues, la clave del algoritmo kNN que programaremos en R se basa en tres aspectos clave que debemos conocer:

  1. Conocer las distintas medidas de distancia que existen, cómo funcionan y cuándo usar cada una de las medidas.

  2. Entender cómo elegir la cantidad de k vecinos a los que se debe observar.

  3. Conocer cómo hace el algortimo kNN las predicciones.

Así pues, si te parece, vamos a ir viendo cada uno de estos aspectos.

Medidas de distancia que puede usar el algoritmo kNN

Dentro del algoritmo kNN las medidas de distancia más utilizadas son: distancia Euclídea, distancia de Minkowski, distancia Manhattan, distancia de Coseno y distancia Jaccard. Estas no son las únicas, el algoritmo kNN puede usar cualquier otra medida de distancia, aunque con estas cubriremos la gran mayoría de casos.

Distancia Euclídea

La distancia Euclídea es algo que ya hemos visto en este blog al programar el algoritmo K-means tanto en R como en Python. La distancia Euclídea se basa en el teorema de Pitágoras, según el cual, la hipotenusa al cuadrado es igual a la suma de catetos al cuadrado.

Esta fórmula funcionará independientemente del número de variables que haya. Partiendo del teorema de Pitágoras, podremos encontrar la distancia en línea recta entre dos puntos, es decir, la distancia Euclídea. En la imagen siguiente podemos ver cómo se calcularía la distancia entre los puntos p y q.

Distancia euclidiana

La distancia euclídea es una de las posibles medidas de distancia que puede usar el algoritmo kNN, vamos a programar la distancia Euclídea desde 0 en R:

euclidean_distance = function(a, b){
  # Comprobamos que tienen la misma cantidad de observaciones
  if(length(a) == length(b)){
    sqrt(sum((a-b)^2))  
  } else{
    stop('Vectors must be of the same length')
  }
}
euclidean_distance(1:10, 11:20)
## [1] 31.62278

Distancia de Manhattan

La distancia de Manhattan no mide la distancia en línea recta, sino que considera la distancia como la suma de los catetos. Esto, que así dicho parece que no tiene mucho sentido, se entiende mejor con una imagen:

Ejemplo de distancia de Manhattan

Cargar datos

datos <- read.csv("https://raw.githubusercontent.com/rpizarrog/Industrias-4.0/main/datos/estado%20de%20felicidad%20variables.csv", encoding = "UTF-8")

kable(datos, caption = "Los datos")
Los datos
genero esto.civil edad satisfaccion.laboral satisfaccion.profesional vida.familiar vida.social salud dinero estado
MASCULINO SOLTERO 25 80.0 90.0 70.0 80.00 BUENO 90.0 FELIZ
FEMENINO CASADO 35 50.0 80.0 60.0 70.00 MALO 30.0 NO FELIZ
MASCULINO DIVORCIADO 45 70.0 78.0 80.0 40.00 REGULAR 70.0 FELIZ
FEMENINO VIUDO 54 50.0 80.0 60.0 80.00 BUENO 20.0 NO FELIZ
MASCULINO CASADO 52 40.0 50.0 60.0 70.00 BUENO 60.0 NO FELIZ
FEMENINO SOLTERO 28 50.0 60.0 54.0 60.00 MALO 50.0 NO FELIZ
MASCULINO VIUDO 56 71.5 60.8 86.9 70.60 MALO 70.0 FELIZ
FEMENINO DIVORCIADO 32 60.0 80.0 30.0 50.00 REGULAR 20.0 NO FELIZ
MASCULINO CASADO 35 70.0 60.0 72.0 60.00 BUENO 70.0 FELIZ
FEMENINO SOLTERO 29 80.0 80.0 90.0 60.00 MALO 80.0 FELIZ
MASCULINO DIVORCIADO 45 60.0 60.0 70.0 50.00 REGULAR 90.0 FELIZ
FEMENINO VIUDO 48 60.0 50.0 50.0 45.50 MALO 70.0 NO FELIZ
MASCULINO CASADO 26 50.0 45.0 80.0 60.00 MALO 20.0 NO FELIZ
FEMENINO SOLTERO 34 60.0 40.0 50.0 80.00 BUENO 65.9 FELIZ
MASCULINO DIVORCIADO 42 50.0 65.0 56.0 62.58 REGULAR 33.5 NO FELIZ
FEMENINO VIUDO 35 80.0 70.0 20.0 20.00 MALO 20.5 NO FELIZ
MASCULINO CASADO 48 50.0 50.0 50.0 50.00 BUENO 49.5 FELIZ
FEMENINO SOLTERO 34 54.0 80.0 56.0 60.00 REGULAR 55.0 FELIZ
MASCULINO DIVORCIADO 34 60.0 70.0 80.0 50.00 MALO 100.0 FELIZ
FEMENINO SOLTERO 32 40.0 50.0 80.0 90.00 BUENO 95.0 FELIZ
MASCULINO SOLTERO 29 50.0 60.0 60.0 80.00 MALO 70.0 FELIZ
FEMENINO SOLTERO 26 60.0 60.0 60.0 60.00 BUENO 60.0 FELIZ
MASCULINO CASADO 45 60.0 60.0 60.0 60.00 BUENO 50.0 FELIZ
FEMENINO VIUDO 45 50.0 60.0 60.0 30.00 REGULAR 35.0 NO FELIZ
MASCULINO CASADO 28 50.0 40.0 80.0 30.00 MALO 30.0 NO FELIZ
FEMENINO DIVORCIADO 32 80.0 70.0 40.0 40.00 MALO 40.0 NO FELIZ
MASCULINO SOLTERO 36 65.0 60.0 62.0 87.00 REGULAR 56.5 FELIZ
FEMENINO CASADO 40 45.0 50.0 40.0 90.00 MALO 60.0 NO FELIZ
MASCULINO SOLTERO 41 60.0 60.0 60.0 60.00 BUENO 49.0 FELIZ
FEMENINO VIUDO 38 80.0 70.0 30.0 40.00 BUENO 45.0 NO FELIZ
MASCULINO CASADO 36 85.0 80.0 90.0 50.00 BUENO 80.0 FELIZ
FEMENINO CASADO 38 60.0 80.0 90.0 80.00 BUENO 60.0 FELIZ
FEMENINO CASADO 37 80.0 60.0 70.0 50.00 BUENO 60.0 FELIZ
FEMENINO SOLTERO 40 60.0 80.0 40.0 50.00 REGULAR 30.0 NO FELIZ
MASCULINO CASADO 40 60.0 70.0 50.0 30.00 BUENO 50.0 FELIZ
FEMENINO CASADO 43 95.0 80.0 90.0 90.00 BUENO 80.0 FELIZ
MASCULINO CASADO 55 70.0 70.0 65.0 89.00 MALO 75.0 FELIZ
MASCULINO SOLTERO 45 65.0 70.0 45.0 65.00 BUENO 45.0 FELIZ
FEMENINO CASADO 41 24.0 57.0 33.0 71.00 BUENO 100.0 NO FELIZ
FEMENINO SOLTERO 40 80.0 80.0 90.0 30.00 REGULAR 50.0 FELIZ
MASCULINO SOLTERO 39 60.0 80.0 48.0 50.00 BUENO 60.0 FELIZ
MASCULINO SOLTERO 42 90.0 80.0 50.0 100.00 REGULAR 80.0 FELIZ
FEMENINO SOLTERO 45 90.0 95.0 90.0 100.00 BUENO 90.0 FELIZ
MASCULINO VIUDO 88 65.0 66.0 89.0 87.00 REGULAR 45.0 NO FELIZ
MASCULINO CASADO 54 90.0 90.0 90.0 100.00 BUENO 75.0 FELIZ
MASCULINO SOLTERO 80 80.0 80.0 90.0 90.00 BUENO 90.0 FELIZ
MASCULINO SOLTERO 39 50.0 80.0 90.0 80.00 REGULAR 90.0 FELIZ
FEMENINO SOLTERO 40 30.0 60.0 80.0 50.00 REGULAR 50.0 NO FELIZ
FEMENINO SOLTERO 49 80.0 80.0 80.0 80.00 BUENO 70.0 FELIZ
MASCULINO SOLTERO 39 60.0 80.0 48.0 50.00 REGULAR 50.0 NO FELIZ
FEMENINO SOLTERO 30 100.0 100.0 100.0 100.00 BUENO 100.0 NO FELIZ
FEMENINO SOLTERO 32 70.0 80.0 80.0 80.00 BUENO 80.0 FELIZ

Preparando los datos

datos.prep <- select(datos, satisfaccion.laboral, satisfaccion.profesional, vida.familiar, vida.social, dinero, estado )

kable(datos.prep, caption = "Datos preparados. Variables de interés")
Datos preparados. Variables de interés
satisfaccion.laboral satisfaccion.profesional vida.familiar vida.social dinero estado
80.0 90.0 70.0 80.00 90.0 FELIZ
50.0 80.0 60.0 70.00 30.0 NO FELIZ
70.0 78.0 80.0 40.00 70.0 FELIZ
50.0 80.0 60.0 80.00 20.0 NO FELIZ
40.0 50.0 60.0 70.00 60.0 NO FELIZ
50.0 60.0 54.0 60.00 50.0 NO FELIZ
71.5 60.8 86.9 70.60 70.0 FELIZ
60.0 80.0 30.0 50.00 20.0 NO FELIZ
70.0 60.0 72.0 60.00 70.0 FELIZ
80.0 80.0 90.0 60.00 80.0 FELIZ
60.0 60.0 70.0 50.00 90.0 FELIZ
60.0 50.0 50.0 45.50 70.0 NO FELIZ
50.0 45.0 80.0 60.00 20.0 NO FELIZ
60.0 40.0 50.0 80.00 65.9 FELIZ
50.0 65.0 56.0 62.58 33.5 NO FELIZ
80.0 70.0 20.0 20.00 20.5 NO FELIZ
50.0 50.0 50.0 50.00 49.5 FELIZ
54.0 80.0 56.0 60.00 55.0 FELIZ
60.0 70.0 80.0 50.00 100.0 FELIZ
40.0 50.0 80.0 90.00 95.0 FELIZ
50.0 60.0 60.0 80.00 70.0 FELIZ
60.0 60.0 60.0 60.00 60.0 FELIZ
60.0 60.0 60.0 60.00 50.0 FELIZ
50.0 60.0 60.0 30.00 35.0 NO FELIZ
50.0 40.0 80.0 30.00 30.0 NO FELIZ
80.0 70.0 40.0 40.00 40.0 NO FELIZ
65.0 60.0 62.0 87.00 56.5 FELIZ
45.0 50.0 40.0 90.00 60.0 NO FELIZ
60.0 60.0 60.0 60.00 49.0 FELIZ
80.0 70.0 30.0 40.00 45.0 NO FELIZ
85.0 80.0 90.0 50.00 80.0 FELIZ
60.0 80.0 90.0 80.00 60.0 FELIZ
80.0 60.0 70.0 50.00 60.0 FELIZ
60.0 80.0 40.0 50.00 30.0 NO FELIZ
60.0 70.0 50.0 30.00 50.0 FELIZ
95.0 80.0 90.0 90.00 80.0 FELIZ
70.0 70.0 65.0 89.00 75.0 FELIZ
65.0 70.0 45.0 65.00 45.0 FELIZ
24.0 57.0 33.0 71.00 100.0 NO FELIZ
80.0 80.0 90.0 30.00 50.0 FELIZ
60.0 80.0 48.0 50.00 60.0 FELIZ
90.0 80.0 50.0 100.00 80.0 FELIZ
90.0 95.0 90.0 100.00 90.0 FELIZ
65.0 66.0 89.0 87.00 45.0 NO FELIZ
90.0 90.0 90.0 100.00 75.0 FELIZ
80.0 80.0 90.0 90.00 90.0 FELIZ
50.0 80.0 90.0 80.00 90.0 FELIZ
30.0 60.0 80.0 50.00 50.0 NO FELIZ
80.0 80.0 80.0 80.00 70.0 FELIZ
60.0 80.0 48.0 50.00 50.0 NO FELIZ
100.0 100.0 100.0 100.00 100.0 NO FELIZ
70.0 80.0 80.0 80.00 80.0 FELIZ

Construir el modelo KNN

En este ejercicio, el modelo de vecinos mas cercanos (KNN) se construye con los mismos datos preparados y las columnas numéricas [,1:5] para posteriormente evaluar el modelo mediante los criterios de una matriz de confusión.

modelo <- knn(train = datos.prep[,1:5], test = datos.prep[,1:5], k = 4, cl = datos.prep[,6] )

modelo
##  [1] FELIZ    NO FELIZ FELIZ    NO FELIZ FELIZ    FELIZ    FELIZ    NO FELIZ
##  [9] FELIZ    FELIZ    FELIZ    FELIZ    NO FELIZ FELIZ    NO FELIZ NO FELIZ
## [17] FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    NO FELIZ
## [25] NO FELIZ NO FELIZ FELIZ    FELIZ    FELIZ    NO FELIZ FELIZ    FELIZ   
## [33] FELIZ    NO FELIZ NO FELIZ FELIZ    FELIZ    FELIZ    NO FELIZ FELIZ   
## [41] FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    FELIZ    NO FELIZ
## [49] FELIZ    FELIZ    FELIZ    FELIZ   
## Levels: FELIZ NO FELIZ

Resumen del modelo

summary(modelo)
##    FELIZ NO FELIZ 
##       38       14

Evaluar el modelo

Se construye un conjunto de datos llamado datos.r.p con valores reales y valores predichos a partir de los datos preparados incorporados en el algoritmo KNN

datos.r.p <- data.frame(reales = datos.prep$estado, prediccion = modelo)
datos.r.p
##      reales prediccion
## 1     FELIZ      FELIZ
## 2  NO FELIZ   NO FELIZ
## 3     FELIZ      FELIZ
## 4  NO FELIZ   NO FELIZ
## 5  NO FELIZ      FELIZ
## 6  NO FELIZ      FELIZ
## 7     FELIZ      FELIZ
## 8  NO FELIZ   NO FELIZ
## 9     FELIZ      FELIZ
## 10    FELIZ      FELIZ
## 11    FELIZ      FELIZ
## 12 NO FELIZ      FELIZ
## 13 NO FELIZ   NO FELIZ
## 14    FELIZ      FELIZ
## 15 NO FELIZ   NO FELIZ
## 16 NO FELIZ   NO FELIZ
## 17    FELIZ      FELIZ
## 18    FELIZ      FELIZ
## 19    FELIZ      FELIZ
## 20    FELIZ      FELIZ
## 21    FELIZ      FELIZ
## 22    FELIZ      FELIZ
## 23    FELIZ      FELIZ
## 24 NO FELIZ   NO FELIZ
## 25 NO FELIZ   NO FELIZ
## 26 NO FELIZ   NO FELIZ
## 27    FELIZ      FELIZ
## 28 NO FELIZ      FELIZ
## 29    FELIZ      FELIZ
## 30 NO FELIZ   NO FELIZ
## 31    FELIZ      FELIZ
## 32    FELIZ      FELIZ
## 33    FELIZ      FELIZ
## 34 NO FELIZ   NO FELIZ
## 35    FELIZ   NO FELIZ
## 36    FELIZ      FELIZ
## 37    FELIZ      FELIZ
## 38    FELIZ      FELIZ
## 39 NO FELIZ   NO FELIZ
## 40    FELIZ      FELIZ
## 41    FELIZ      FELIZ
## 42    FELIZ      FELIZ
## 43    FELIZ      FELIZ
## 44 NO FELIZ      FELIZ
## 45    FELIZ      FELIZ
## 46    FELIZ      FELIZ
## 47    FELIZ      FELIZ
## 48 NO FELIZ   NO FELIZ
## 49    FELIZ      FELIZ
## 50 NO FELIZ      FELIZ
## 51 NO FELIZ      FELIZ
## 52    FELIZ      FELIZ

Ahora se construye la matriz de confusión con la función confusionMatrix() no sin antes categorizar o factorizar los valores de datos.r.p

Categorizar o factorizar

datos.r.p$reales <- as.factor(datos.r.p$reales)
datos.r.p$prediccion <- as.factor(datos.r.p$prediccion)

Matriz de confusión

matriz <- confusionMatrix(datos.r.p$reales, datos.r.p$prediccion)

matriz
## Confusion Matrix and Statistics
## 
##           Reference
## Prediction FELIZ NO FELIZ
##   FELIZ       31        1
##   NO FELIZ     7       13
##                                           
##                Accuracy : 0.8462          
##                  95% CI : (0.7192, 0.9312)
##     No Information Rate : 0.7308          
##     P-Value [Acc > NIR] : 0.03743         
##                                           
##                   Kappa : 0.6556          
##                                           
##  Mcnemar's Test P-Value : 0.07710         
##                                           
##             Sensitivity : 0.8158          
##             Specificity : 0.9286          
##          Pos Pred Value : 0.9687          
##          Neg Pred Value : 0.6500          
##              Prevalence : 0.7308          
##          Detection Rate : 0.5962          
##    Detection Prevalence : 0.6154          
##       Balanced Accuracy : 0.8722          
##                                           
##        'Positive' Class : FELIZ           
## 

Ahora, la función de la matriz de confusión hace de forma automática las métricas de calidad del modelo, sin embargo es importante que sepamos de donde salen dichas métricas y cómo se calculan, a continuación lo veremos:

Calculos para las métricas del modelo

exactitud <- round(as.numeric(matriz$overall[1]) * 100,2)
sensibilidad <- round(as.numeric(matriz$byClass[1]) * 100,2)
especificidad <- round(as.numeric(matriz$byClass[2]) * 100,2)
precision.FELIZ <- round(as.numeric(matriz$byClass[3]) * 100,2)
precision.NOFELIZ <- round(as.numeric(matriz$byClass[4]) * 100,2)
kappa <- round(as.numeric(matriz$overall[2]) * 100,2)


sensibilidad
## [1] 81.58

Ese 86% de precisión sale de ese cálculo.

Hacer predicciones

#genero <- c('MASCULINO', 'FEMENINO', 'FEMENINO')
#esto.civil <- c('SOLTERO', 'CASADO', 'DIVORCIADO')
#edad <- c(30, 25, 40)
satisfaccion.laboral <- c(40, 50, 60)
satisfaccion.profesional <- c(60, 50, 40)
vida.familiar <- c(80, 70, 60)
vida.social <- c(60,50,76)
#salud <- c('BUENO', 'REGULAR', 'MALO')
dinero <- c(40, 50, 60)
estado = c('?', '?', '?')


datos.nuevos <- data.frame(satisfaccion.laboral, satisfaccion.profesional, vida.familiar, vida.social, dinero, estado )

kable(datos.nuevos, caption = "Datos nuevos")
Datos nuevos
satisfaccion.laboral satisfaccion.profesional vida.familiar vida.social dinero estado
40 60 80 60 40 ?
50 50 70 50 50 ?
60 40 60 76 60 ?
modelo <- knn(train = datos.prep[,1:5], test = datos.nuevos[,1:5], k = 4, cl = datos.prep[,6] )

modelo
## [1] NO FELIZ FELIZ    FELIZ   
## Levels: FELIZ NO FELIZ
datos.nuevos <- mutate(datos.nuevos, prediccion = modelo)
kable(datos.nuevos, caption = "Predicción de datos nuevos")
Predicción de datos nuevos
satisfaccion.laboral satisfaccion.profesional vida.familiar vida.social dinero estado prediccion
40 60 80 60 40 ? NO FELIZ
50 50 70 50 50 ? FELIZ
60 40 60 76 60 ? FELIZ

Descarga del código

xfun::embed_file("A11U2_KNN.Rmd")

Download A11U2_KNN.Rmd