library(tidyverse)
library(class)
library(gmodels)
library(caret)
Para este análisis, se utilizará el dataset que se encuentra en el sitio web de la editorial oreilly.
Este dataset contiene 569 observaciones y 32 variables relativas a las propiedades relacionadas con biopsias de tumores, calificados como “M” (maligno) o “B” (benigno).
Descargamos el archivo .csv y lo guardamos en un data frame
download.file("https://resources.oreilly.com/examples/9781784393908/raw/ac9fe41596dd42fc3877cfa8ed410dd346c43548/Machine%20Learning%20with%20R,%20Second%20Edition_Code/Chapter%2003/wisc_bc_data.csv", destfile = "wisc_bc_data.csv")
wisc_data <- read.csv(file = "wisc_bc_data.csv")
A continuación eliminamos la variable ID, ya que puede dar lugar a un sobreajuste ya que identifica cada observación de forma única.
wisc_data <- wisc_data[,-1]
Modificamos los nombres de los valores de la variable diagnosis para que sean más claros
wisc_data <- mutate(wisc_data,
diagnosis = fct_recode(wisc_data$diagnosis,
"Bening" = "B",
"Malignant" = "M"))
Obtenemos los porcentajes de tumores benignos y malignos
round(prop.table(table(wisc_data$diagnosis)) * 100, 1)
##
## Bening Malignant
## 62.7 37.3
Las variables manejan magnitudes muy diversas, por lo cual algunas variables cuya magnitud de medida sea más grande que otras, tendrán mayor peso a la hora de calcular la distancia entre vecinos. Al ser ello un problema para el clasificador, normalizaremos para reescalar todas las variables a un rango standar de valores.
Creamos una función para normalizar, y la aplicamos a nuestro data frame (creando uno nuevo normalizado)
normalize <- function(x){
return ((x - min(x))/(max(x) - min(x)))
}
wisc_data_n <- as.data.frame(lapply(wisc_data[2:31], normalize))
Dividimos la muestra en dos datasets, uno para tranining y otro para test. Podemos escoger las primeras 469 para el primero, y las últimas 100 para el segundo, puesto que todas las observaciones de este dataset ya vienen en orden aleatorio.
wisc_training <- wisc_data_n[1:469,]
wisc_test <- wisc_data_n[470:569,]
Por otro lado vamos a guardar las etiquetas de diagnóstico de todas las observaciones en dos vectores por separado
wisc_training_labels <- wisc_data[1:469,1]
wisc_test_labels <- wisc_data[470:569,1]
Ahora nos disponemos a clasificar nuestras observaciones. Para el algoritmo k-NN, la fase de entrenamiento no supone la creación de un modelo, sino simplemente guardar la información en un formato estructurado. A diferencia de muchos algoritmos de clasificación, k-NN no hace ningún aprendizaje. Simplemente almacena los datos de entrenamiento textualmente. Los ejemplos de prueba sin etiqueta se comparan con los registros más similares en el conjunto de entrenamiento utilizando una función de distancia, y al ejemplo sin etiqueta se le asigna la etiqueta de sus vecinos. Como número k escogeremos la raiz cuadrada del total de observaciones, y redondeamos a un número impar para evitar un empate entre categorías.
wisc_test_predicted <- knn(wisc_training, wisc_test, cl = wisc_training_labels, k = 21)
Con esta operación hemos obtenido un vector con la predicción de categorías que hemos hecho, y que después compararemos con las categorías reales.
Compararemos con una tabla cruzada los valores para el dataset de Test que hemos predicho con nuestro modelo, con los valores reales de Test.
confusionMatrix(data = wisc_test_predicted, reference = wisc_test_labels)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bening Malignant
## Bening 61 2
## Malignant 0 37
##
## Accuracy : 0.98
## 95% CI : (0.9296, 0.9976)
## No Information Rate : 0.61
## P-Value [Acc > NIR] : <2e-16
##
## Kappa : 0.9576
##
## Mcnemar's Test P-Value : 0.4795
##
## Sensitivity : 1.0000
## Specificity : 0.9487
## Pos Pred Value : 0.9683
## Neg Pred Value : 1.0000
## Prevalence : 0.6100
## Detection Rate : 0.6100
## Detection Prevalence : 0.6300
## Balanced Accuracy : 0.9744
##
## 'Positive' Class : Bening
##
Con este modelo hemos obtenido una exactitud del 98% a la hora de acertar en una predicción. Aunque es una cifra que puede parecer buena, hemos de tener en cuenta que en un 2% de las predicciones hemos predicho un tumor benigno siendo en realidad maligno.
El problema que tiene la normalización min-max es que los valores extremos son comprimidos hacia el centro. Puesto que la estandarización z no tiene valores extremos predefinidos, se evita este problema. Lo que vamos a hacer es otorgar mayor peso a valores extremos. Para ello efectuaremos las mismas operaciones, solo que esta vez habiendo hecho una estandarización z.
wisc_data_z <- as.data.frame(scale(wisc_data[2:31]))
wisc_training_z <- wisc_data_z[1:469,]
wisc_test_z <- wisc_data_z[470:569,]
wisc_test_predicted_z <- knn(wisc_training_z, wisc_test_z, cl = wisc_training_labels, k = 21)
confusionMatrix(data = wisc_test_predicted_z, reference = wisc_test_labels)
## Confusion Matrix and Statistics
##
## Reference
## Prediction Bening Malignant
## Bening 61 5
## Malignant 0 34
##
## Accuracy : 0.95
## 95% CI : (0.8872, 0.9836)
## No Information Rate : 0.61
## P-Value [Acc > NIR] : 2.983e-15
##
## Kappa : 0.8924
##
## Mcnemar's Test P-Value : 0.07364
##
## Sensitivity : 1.0000
## Specificity : 0.8718
## Pos Pred Value : 0.9242
## Neg Pred Value : 1.0000
## Prevalence : 0.6100
## Detection Rate : 0.6100
## Detection Prevalence : 0.6600
## Balanced Accuracy : 0.9359
##
## 'Positive' Class : Bening
##
En este caso podemos ver que nuestra exactitud ha disminuido al 5%, por lo que no hemos conseguido mejorarla con respecto a la normalización min-max.