Aprenderemos qué son los algoritmos de clasificación con RStudio y para qué sirven. Mostraremos los árboles de decisión y el modelo de bosque aleatorio. Aprenderemos a medir las bondades del modelo mediante matrices de confusión y curvas ROC para ver cómo de preciso es.
Índice de contenidos
- 1. Introducción a los Algoritmos de clasificación con RStudio
- 2. Particionando el juego de datos
- 3. Árboles de Decisión
- 4. Matrices de confusión
- 5. Bosque aleatorio
- 6. Curva ROC
- 7. Conclusiones
- Enlaces y referencias
1. Introducción a los Algoritmos de clasificación con RStudio
Dado un conjunto de datos, muchas veces queremos saber cómo clasificar datos nuevos en base a datos que ya tenemos. Imaginemos que tenemos acceso a millones de analíticas de sangre de pacientes desde 1990. Y sabemos qué personas murieron 1 año después de esos análisis. Sería interesante poder entrenar un modelo que dado unos análisis nuevos nos alertara sobre los pacientes que se morirán en el próximo año.
Otro ejemplo: al pedir una hipoteca nos piden un montón de información. Algunas preguntas son normales: ingresos, otras deudas, etc., pero suelen incorporar una pregunta aparentemente inocua, y es «en qué sector trabajas». Os puedo adelantar que los algoritmos de clasificación aciertan bastante sobre qué personas tienen más posibilidades de convertirse en morosas. El mismo estudio se aplica a cualquier tipo de seguro: del hogar, de accidentes, de vida, médico, etc.
Para todos estos sistemas es fundamental saber catalogar qué tipo de cliente/paciente eres y con la mayor fiabilidad posible.
Se usa mucho en comercio electrónico, para saber qué tipo de cliente eres, o con qué periodicidad volverás. Para centrar los esfuerzos de fidelización en quienes les merezcan la pena. En fin, hay infinidad de aplicaciones.
1.1. Nuestro objetivo
En este artículo vamos a seguir con alguno de los ejemplos que hemos visto en artículos previos. En él obteníamos la rentabilidad de los pisos en función del precio del m2 de compra y el precio del m2 de alquiler. Cuanto menor fuera el de compra y mayor el de alquiler, mayor rentabilidad. Esos datos los teníamos geoposicionados, y en base a distancia de paradas de metro.
Cabe hacernos la siguiente pregunta: dada una localización exacta que cumpliera el precio medio de la zona ¿sería una buena idea comprar un piso para alquilar ahí? De alguna forma, dadas unas coordenadas debemos saber si es una buena inversión o no.
2. Particionando el juego de datos
Lo primero que debemos hacer es leer el juego de datos.
#seteamos el directorio de trabajo setwd("/lab/adictosaltrabajo/rstudio/06") # leemos los precios que vimos en el ejemplo 02 precios <- readRDS("./precios.rds") #creamos la columna con el rendimiento anualizado precios$rendimiento <- (12 * precios$rental)/precios$sale
Ahí tenemos una nueva columna que indica una aproximación al rendimiento anual de un piso. Pero nos interesa clasificarla en categorías. Podríamos clasificar los datos en 5 niveles, según su rendimiento.
# creamos un factor con la rentabilidad precios$rentabilidad <- cut(precios$rendimiento, breaks = 5, labels = c("Muy Bajo","Bajo","Medio","Alto","Muy Alto")) View(precios)
Hemos creado una nueva columna «rentabilidad», donde se ven las categorías (son factores) que hemos dispuesto.
Ahora queremos dividir nuestro dataFrame en dos conjuntos de datos. Un conjunto de entrenamiento, y otro conjunto de test. La idea es que usaremos en de entrenamiento para elaborar un modelo de predicción. Y luego veremos cómo de bueno es ese modelo cotejándolo con el conjunto de test.
Así que lo primero que necesitamos es una forma de dividir aleatoriamente nuestro dataFrame fijándonos en la columna «rentabilidad». Para ello emplearemos la librería caret.
Le indicamos que para el conjunto de entrenamiento queremos el 70% de las filas, y que no las devuelva como una lista, si no como un array.
install.packages("caret") library(caret) filas.entrenamiento <- createDataPartition(precios$rentabilidad, p = 0.7, list = FALSE)
De esta forma, tenemos un array con los IDs de las filas de entrenamiento y podemos seleccionar fácilmente el subconjunto del dataFrame que se corresponde con el conjunto de entrenamiento (229 filas) y cuál con el de test (96 filas).
#El conjuto de entrenamiento son esas filas y todas las columnas conjuntoEntrenamiento <- precios[filas.entrenamiento,] #Y el conjunto de test son las filas restantes, y todas las columnas conjuntoTest <- precios[-filas.entrenamiento,]
3. Árboles de Decisión
El primero de los algoritmos de clasificación con RStudio que vamos a utilizar son los árboles de decisión. Para ello cargamos la librería rpart y le indicamos que deseamos crear un modelo, que nos prediga la «rentabilidad» en base a la «latitud y longitud», usando el conjunto de entrenamiento.
install.packages("rpart") library(rpart) # vamos a hacer un modelo de clasificación basado en árboles de decisión # estudiando la rentabilidad en base a la localizacion geografica (latitud, longitud) modeloArbolDecicion <- rpart( rentabilidad ~ lat + lng, data = precios[filas.entrenamiento,], method = "class")
Me gustaría llamar la atención sobre varios puntos:
- Por un lado, indicamos la variable independiente en función de las variables dependientes: rentabilidad en función de latitud y longitud. Realmente es lo mismo que una función con dos valores funcion(lat,lng) y se indica de la siguiente manera rentabilidad ~ lat + lng
- Por otro lado, el conjunto de datos sobre el que calculamos el modelo, no es el conjunto total, si no que es el subconjunto de entrenamiento data = precios[filas.entranamiento, ]
- El método que indicamos para calcular el modelo es el de clasificación. Existen otros muchos posibles métodos como són: anova, poisson, exponencial,…
¿Y qué pinta tendría este árbol de decisión?
Nuestro nodo raíz nos indica que tiene 229 items. En la primera bifurcación mira que la latitud sea mayor que 40.40384 dejando a ese lado 156 casos y al otro 73. El Árbol sigue ramificándose, y así hasta llegar a los nodos hojas, que son marcados con asteriscos. Es decir, dada una ubicación nueva, con su latitud y longitud, podemos aplicar el árbol de decisión y ver en qué nodo hoja final cae.
Lo cierto es que no es muy atractivo visualmente. Vamos a usar otra librería para pintar este árbol de decisión.
#pintamos el arbol de decision install.packages("rpart.plot") library(rpart.plot) prp(modeloArbolDecicion, type = 2, extra = 102)
En base a este árbol de decisión, podemos clasificar las filas del conjunto de test y añadir los resultados en una columna nueva.
#hacemos una predicción sobre el conjunto de test prediccionArbolDecicion <- predict(modeloArbolDecicion, precios[-filas.entrenamiento,], type="class") #añadimos la predicción en una columna nueva precios[-filas.entrenamiento,c("P(arbol_decision)")] <- prediccionArbolDecicion
Ahí vemos la nueva columna P(arbol_decisión) junto a la columna rentabilidad. Hay filas donde pone N/A. Eso es porque sólo hemos hecho la predicción para el conjunto de test, no para el de entrenamiento. Y vemos que los valores que ha predicho parece que coinciden bastante. Sólo estamos viendo el principio del dataFrame. ¿Será siempre igual?
Necesitamos algún mecanismo para saber cómo de eficaz es un modelo.
4. Matrices de confusión
Una matriz de confusión es una tabla de doble entrada. Por un lado en el eje Y tenemos los valores reales, y por otro en el eje X los valores predichos.
tabla[valores reales, valores predichos]
Veamos cómo sería:
rentabilidad <- precios[-filas.entrenamiento,c("rentabilidad")] matrizDeConfusionArbolDecicion <- table(rentabilidad, prediccionArbolDecicion, dnn = c("retabilidad","rentabilidad predicha")) matrizDeConfusionArbolDecicion > #clasificacion de aciertos vs fallos > matrizDeConfusionArbolDecicion rentabilidad predicha retabilidad Muy Bajo Bajo Medio Alto Muy Alto Muy Bajo 18 3 0 3 0 Bajo 11 21 0 3 0 Medio 0 0 12 3 6 Alto 0 1 4 1 3 Muy Alto 0 0 0 1 6
Los valores que hemos «clavado» son los que están en la diagonal principal. Hemos predicho como «muy Bajo», había 18 que realmente eran «muy bajo», pero había 11 que eran realmente «Bajo». En fin hemos acertado en algunos valores, pero otros los hemos clasificado mal. ¿Habría alguna forma de verlo en porcentajes?
Podemos hacer la probabilidad distribuida por columnas (valores predichos) y mostrarla en porcentaje
#probabilidad por COLUMNAS en porcentaje round(100 * prop.table(matrizDeConfusionArbolDecicion,2)) rentabilidad predicha retabilidad Muy Bajo Bajo Medio Alto Muy Alto Muy Bajo 62 12 0 27 0 Bajo 38 84 0 27 0 Medio 0 0 75 27 40 Alto 0 4 25 9 20 Muy Alto 0 0 0 9 40
Por ejemplo, para la clasificación «Muy Alta» hemos acertado en el 40% de los casos, pero hemos clasificado mal un 20% que realmente son «Alto» y un 40% de valores que realmente son «Medio».
5. Bosque aleatorio
Otro de los algoritmos de clasificación con RStudio es el randomForest, llamado de bosque aleatorio. Este algoritmo realmente nos lanza n árboles de decisión distintos y los combina para obtener mejores resultados. En este ejemplo usaremos la librería «randomForest» y crearemos 200 árboles para generar nuestro modelo.
#veamos otros algoritmos de clasificacion con RStudio: randomForest install.packages("randomForest") library(randomForest) modeloRandomForest <- randomForest(x = precios[filas.entrenamiento,c("lat","lng")], y = precios[filas.entrenamiento,c("rentabilidad")], ntree = 200) prediccionRandomForest <- predict(modeloRandomForest, precios[-filas.entrenamiento,], type="class") #añadimos la predicción en una columna nueva precios[-filas.entrenamiento,c("P(bosque_aleatorio)")] <- prediccionRandomForest
¿Nos habremos equivocado más o menos que antes? Parece a simple vista que nos hemos equivocado menos.
Pero para esto está la matriz de confusión.
matrizDeConfusionRandomForest <- table(rentabilidad, prediccionRandomForest, dnn = c("retabilidad","rentabilidad predicha")) rentabilidad predicha rentabilidad Muy Bajo Bajo Medio Alto Muy Alto Muy Bajo 20 4 0 0 0 Bajo 1 32 2 0 0 Medio 0 1 19 0 1 Alto 0 1 2 4 2 Muy Alto 0 0 0 0 7 #probabilidad por COLUMNAS en porcentaje round(100 * prop.table(matrizDeConfusionRandomForest,2)) rentabilidad predicha rentabilidad Muy Bajo Bajo Medio Alto Muy Alto Muy Bajo 95 11 0 0 0 Bajo 5 84 9 0 0 Medio 0 3 83 0 10 Alto 0 3 9 100 20 Muy Alto 0 0 0 0 70
Vaya, parece que en la diagonal principal tengo porcentajes mucho más altos. Mi modelo explica con éxito el 95% de los clasificados como «Muy Bajo», el 84% de los «Bajo», el 83% de los casos catalogados como rentabilidad «Media», explica el 100% de los casos con rentabilidad «Alta». Y un 70% de los «Muy Alta»
Aún así clasifica como «Muy Alta» un 20% que son realmente «Alta» y un 10% que son realmente «Media».
Con este modelo, podría arriesgarme y no equivocarme mucho.
6. Curva ROC
Normalmente las clasificaciones pueden tomar dos posibles valores: o te conviertes en cliente o no. Eres moroso, o lo no eres. Mañana va a llover, o no. Normalmente se trata de una clasificación binaria. Es una buena zona para invertir o no.
Vamos a crear una nueva columna en nuestro data frame, de forma que si es Alto o Muy Alto, es una buena opción de inversión.
# creamos un factor con la rentabilidad precios$inversion <- cut(precios$rendimiento, breaks = 5, labels = c("NO","NO","NO","SI","SI")) # creamos un modelo de Bosque Aleatorio con el conjunto de entrenamiento modRF <- randomForest(x = precios[filas.entrenamiento,c("lat","lng")], y = precios[filas.entrenamiento,c("inversion")], ntree = 200) # hacemos una predicción sobre el conjunto de test predRF <- predict(modRF, precios[-filas.entrenamiento,], type="class") precios[-filas.entrenamiento,c("predRF")] <- predRF # visualizamos los resultados sólo del conjunto de test View(precios[-filas.entrenamiento,])
Estamos partiendo del supuesto de que equivocarnos en nuestra predicción es malo. Nuestra predicción será mejor cuanto menos equivocaciones tenga ¿no?. Pero aquí ahora cabe hacerse una pregunta importante: ¿Es igual de malo predecir que es una mala inversión cuando realmente es buena, que predecir que es una buena inversión y que luego resulte ser una pésima inversión? Veremos que no.
6.1. Un poquito de teoría
En este caso un falso positivo es mucho peor que el error contrario. A veces no es tan importante maximizar el número de aciertos, como minimizar el número de falsos positivos. Aquí hay que hablar de sensibilidad y especificidad. Imaginemos que tenemos que clasificar a pacientes por cierta enfermedad y el tratamiento es la amputación de una extremidad. Ahí un falso positivo sería imperdonable.
Vamos a dar una serie de definiciones:
- Verdadero positivo: VP, cuando predecimos que es positivo y realmente es positivo. Por ejemplo, hemos aventurado que el paciente está enfermo, y realmente lo está.
- Falso positivo: FP, cuando predecimos que es positivo pero realmente es negativo. Nuestra predicción afirma que está enferme cuando en realidad no lo está
- Verdadero negativo: VN, cuando predecimos que es negativo y realmente es negativo. Por ejemplo, para un paciente el modelo predice que no está enfermo, y efectivamente no lo está
- Falso negativo: FN, cuando predecimos que es negativo pero realmente es positivo. En nuestro ejemplo, hemos predicho que el paciente no tiene la enfermedad, y sin embargo sí la tiene
Está claro que los aciertos no nos preocupan, pero los errores sí:
Cuado predecimos que no está enfermo, pero realmente sí lo está, podríamos estarle tratando contra la enfermedad y mejorar su estado de salud. En el caso contrario, cuando el tratamiento es muy agresivo, como puede ser la amputación de una extremidad, si damos falsos positivos, estaremos procediendo a la amputación de personas que realmente están sanas.
Se define así la sensibilidad: el ratio de verdaderos positivos sobre la tasa de aciertos (verdaderos positivos + falsos negativos)
Y la especificidad como: el ratio de verdaderos negativos entre los negativos originales (falsos positivos + verdaderos negativos)
Lo primero que debemos entender es que no son términos complementarios. La suma de uno y otro no nos dan 1.
6.2. Aplicando estos conceptos en la matriz de confusión
Veamas como es la matriz de confusión de la predicción de nuestro modelo.
inversion <- precios[-filas.entrenamiento, c("inversion")] matrizDeConfusionRF <- table(inversion, predRF, dnn = c("inversion","pred")) #clasificacion de aciertos vs fallos matrizDeConfusionRF > matrizDeConfusionRF pred inversion NO SI NO 80 0 # 80 - VN: Verdadero Negativo SI 4 12 # 4 - FN: Falso negativo # 0 - FP: Falso positivo PREDICHO # 12 - VP: Verdaderos positivos REAL VN FP FN VP
Hemos predicho que:
- 80 elementos son mala inversión y efectivamente lo son.
- 12 elementos son una buena inversión y efectivamente lo son.
- 4 elementos los hemos calificado de mala inversión cuando realmente era una buena inversión.
- Y no hemos calificado de buena inversión ninguno que sea realmente malo.
6.3. obteniendo el ROC con la probabilidad del SI
Así que en apariencia nuestro modelo parece muy bueno ¿no?. El algoritmo de bosque aleatorio nos ha clasificado las filas en SI y NO, pero con qué probabilidad son SI y NO. Hacemos una predicción, pero que en lugar de decir que Sí o NO, nos diga también las probabilidades.
# hacemos una predicción pero en lugar de class que nos de la probabilidad # predRF <- predict(modRF, precios[-filas.entrenamiento,], type="class") probRF <- predict(modRF, precios[-filas.entrenamiento,], type="prob")
Ahora vamos a instalar la librería pROC para hacer curvas ROC y ver cómo de bueno es nuestro modelo y la relación entre sensibilidad y 1-especificidad. Cuando más a la izquierda y arriba mejor.
install.packages("pROC") library(pROC) #comparamos el valor original con la probabilidad del SI roc.objRF <- roc( inversion, probRF[,2], auc = TRUE, ci = TRUE ) plot(roc.objRF)
Vemos que la curva sube casi vertical y a partir del 75%-80% empiezo a clasificar como falsos negativos, algunos SIes.
Una medida que nos ayuda mucho es el «Area bajo la curva». Cuando más cercana a 1 mejor.
Podemos acceder al area bajo la curva con la propiedad auc (area under curve):
> roc.objRF$auc Area under the curve: 0.9906
Nuestra estimación de Bosque Aleatorio es buenísima. Tiene un 0.9906.
7. Conclusiones
Este artículo ha sido especialmente denso. Pero es la base para cualquier concepto relacionado con los algoritmos de clasificación en RStudio, o en cualquier otro sistema..
En este tutorial hemos aprendido:
- qué son los algoritmos de clasificación en RStudio y para qué sirven
- árboles de decisión
- modelo del bosque aleatorio
- matrices de confusión
- ver cómo de bueno es nuestro modelo
- curvas ROC
- particionar el juego de datos y crear conjuntos de entrenamiento y de pruebas
Enlaces y referencias
- El código fuente de este tutorial en GitHub
https://github.com/eContento/rstudio