library(ggplot2) # librería para gráficar
library(class) # librería para el modelo KNN
data(iris)
El algorítmo de vecinos mas cercanos k o knn por sus siglas en inglés es un método no-paramétrico que sirve para clasificación y regresión de datos. El método consiste en utilizar las características de los vecinos más cercanos para ejecutar una clasificación o regresión de datos. De esta manera, el modelo logra generar y predecir “clusteres” o agrupaciones de datos similares. Es un modelo útil que puede incorporar diferente esquemas de asignación de “pesos” o importancia a diferentes atributos o casos analizados. Los “pesos” determinan las distancias entre cada uno de los elementos del conjunto de datos que se evalúan.
Para este ejemplo se utilizan los datos de Iris que se incluyen en R, y es un listado de plantas plantas con datos sobre las dimensiones de su sépalo y pétalo y agrupado por especie. La “distancia” o “cercanía” entre cada uno de los ejemplares de los datos se calcula en función de las diferencias entre las dimensiones de los pétalos y sépalos. La siguiente gráfica permite visualizar como la relación entre tamaños de sépalo y pétalo pueden agrupar a las distintas especies de plantas.
data(iris) # cargar data "iris" de R
# Crear gráfica de dispersión con datos de longitud y ancho de pétalo
qplot(Petal.Length, Petal.Width, data = iris, color = Species)
El objetivo del modelo es predecir la especie de planta en función de las dimensiones del sépalo y pétalo. Consiste en tres pasos:
# Asignar el nombre "datos" al dataframe de "iris". Esto facilita usar el ejemplo con otros conjutos de datos con mayor facilidad.
datos <- iris
Los datos generalmente se normalizan para poder. En este caso la normalización consiste en crear una función que transforme los datos de las dimensiones en una escala que se calcula del cociente del valor en cuestión menos el valor mínimo de la lista entre el rango de la lista.
# Normalizar datos
nor <-function(x) {(x -min(x))/(max(x)-min(x))}
# Aplicar la función de normalización a las columnas 1 a 4 del dataframe. La última columna se omite porque es la columna de la especie y no es numérica.
datos_norm <- as.data.frame(lapply(datos[,c(1,2,3,4)], nor))
# Crear un nuevo dataframe "datos_norm" combinando el dataframe anterior de datos normalizados con la columna 5 que corresponde a los valores de las especies
datos_norm <- cbind(datos_norm, datos[,5])
datos_norm es el datafram normalizado con el cual se entrenará y probará el modelo.
Al tratarse de un modelo supervisado, es necesario primero realizar un entrenamiento con una parte de los datos normalizados “datos_norm”. Se toma al 70% como una muestra para “entrenar” el modelo y el 30% restante para posteriormente probar el modelo.
# Extraer el id de los valores de una muestra del 70% de los valores del dataframe
id_muestra <- sample(1:nrow(datos_norm), 0.7*nrow(datos_norm))
# Se crea un dataframe denominado "modelo_knn_train" que corresponde a un 70% muestreado al azar
modelo_knn_train <- datos_norm[id_muestra, -5]
# Se crea un dataframe denominado "modelo_knn_train" a partir del 30% restante del dataframe
modelo_knn_test <- datos_norm[-id_muestra, -5]
# NOTA: La última columna se elimina porque es "factor" al ser factor, no permite correr el modelo, además, es la columna que se va a predecir.
# Extraer el valor real que se va a predecir. Son los valores de referencia con los cuales se evalúa la precición del modelo.
train_df <- datos_norm[id_muestra, 5]
test_df <- datos_norm[-id_muestra, 5]
Primero se debe determinar un número de vecinos para hacer la prueba. Ésto se refiere a la cantidad de vecinos más cercanos de los cuales se van a extraer los datos para hacer la predicción o clasificación. Este valor se puede determinar empíricamente aunque también por convención se estima calculando la raiz cuadrada del número de observaciones disponibles para diseñar el modelo. En este caso hay 150 datos de los cuales 105 son para entrenar el modelo. Se calculan k_1 = valor redondeado hacia “abajo” de la raíz de 105, y k_2 = valor redondeado hacia “arriba” de la raíz de 105.
# Determinar dos valores para K
k_1 <- floor(sqrt(nrow(modelo_knn_train)))
k_2 <- ceiling(sqrt(nrow(modelo_knn_train)))
Para entrenar el modelo, simplemente se corre la función knn de la librería “class” con la siguiente configuración:
Datos para entrenar: modelo_knn_train Datos para probar: modelo_knn_test k = coeficientes k_1 o k_2 cl = datos de salida train_df
Predicción de especies con un valor de k = k_1
# knn.A es el resltado de correr el modleo con k_1
knn.A <- knn(modelo_knn_train, modelo_knn_test, cl=train_df, k=k_1)
knn.A
## [1] setosa setosa setosa setosa setosa setosa
## [7] setosa setosa setosa setosa setosa setosa
## [13] setosa setosa versicolor versicolor versicolor versicolor
## [19] versicolor versicolor versicolor versicolor virginica versicolor
## [25] versicolor versicolor versicolor versicolor versicolor virginica
## [31] versicolor virginica virginica virginica versicolor virginica
## [37] virginica virginica versicolor versicolor virginica virginica
## [43] virginica virginica virginica
## Levels: setosa versicolor virginica
Predicción de especies con un valor de k = k_2
# knn.B es el resultado de correr el modelo con k_2
knn.B <- knn(modelo_knn_train, modelo_knn_test, cl=train_df, k=k_2)
knn.B
## [1] setosa setosa setosa setosa setosa setosa
## [7] setosa setosa setosa setosa setosa setosa
## [13] setosa setosa versicolor versicolor versicolor versicolor
## [19] versicolor versicolor versicolor versicolor virginica versicolor
## [25] versicolor versicolor versicolor versicolor versicolor virginica
## [31] versicolor virginica virginica virginica versicolor virginica
## [37] virginica virginica versicolor versicolor virginica virginica
## [43] virginica virginica virginica
## Levels: setosa versicolor virginica
El modelo se puede probar con los datos de “iris” y generando una matriz de confusión para evaluar la efectividad del modelo para predecir la especie en función de los otros parámetros de los datos. Las siguientes tablas de confusión comparan los valores reales del datafram “test_df” con los valores predichos con k_1, “knn.A” y con k_2, “knn.B”.
confusion_A <- table(knn.A, test_df)
confusion_B <- table(knn.B, test_df)
confusion_A
## test_df
## knn.A setosa versicolor virginica
## setosa 14 0 0
## versicolor 0 14 4
## virginica 0 1 12
confusion_B
## test_df
## knn.B setosa versicolor virginica
## setosa 14 0 0
## versicolor 0 14 4
## virginica 0 1 12
La tasa de los errores de clasificación o “confusiones” se puede calcular dividiendo los valores predichos entre los valores reales de las matrices de confusión.
# Matriz de los valores reales
real <- table(test_df,test_df)
# Matriz de valores reales / Matriz de confusión A
precision_knn.A <- sum(diag(confusion_A))/sum(diag(real))
precision_knn.A
## [1] 0.8888889
# Matriz de valores reales / Matriz de confusión B
precision_knn.B <- sum(diag(confusion_B))/sum(diag(real))
precision_knn.B
## [1] 0.8888889
¿Por qué crees que no hay diferencias entre el modelo con k=10 y k=11 (knn.A y knn.B)?
¿Qué son los errores tipo I y tipo II?
¿Cuales son los errores tipo I y tipo II del resultado del ejercicio?