1 Base teórica


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.

Recursos en la web:


2 Caso de estudio: — Clasificación red de ventas Bodegas Mureda —


Formamos parte de la Dirección Comercial de la Bodega de vinos Mureda y queremos analizar la actividad de nuestra red de ventas, formada por tres categorías de comerciales (A, B y C). Para ello, estamos interesados en conocer si existen diferencias en la actividad generada por cada uno de los comerciales y, en caso afirmativo, identificar cuáles son las variables que más contribuyen a dichas diferencias y si podemos predecir a qué categoría de comercial pertenece un nuevo empleado en función de su actividad.

Para ello, nuestro equipo de análisis dispone de un fichero con información de 150 clientes que recoge estadísticas de actividad de los tres grupos de Comerciales, a razón de 50 registros por grupo de comercial. El fichero contiene información de las siguientes variables:

Una vez definidos los objetivos de nuestra investigación, nuestro departamento de análisis nos propone desarrollar un proceso de clasificación que aplique tres tipos de algoritmos complementarios: K-NN, Árboles de decisión simples y Árboles de clasificación múltiples (random forest) sobre nuestro fichero de datos.


3 Apartados de la práctica


El código R que utilizaremos en la práctica se divide en apartados según las tareas que iremos realizando:

Apartados práctica:


4 Carga de paquetes y del fichero de datos


Empezaremos por cargar los packages R que necesitaremos tener en memoria.

Cargamos también los datos ubicados en el fichero PEC2.csv

#revisamos si es necesario instalar los paquetes necesarios para desarrollar la PEC 2
#install.packages("ggplot2")
#install.packages("rpart.plot")
#install.packages("useful")
#install.packages("randomForest")

#cargamos los paquetes necesarios para desarrollar la PEC 2

# Para representar gráficamente la relación entre variables
library("ggplot2")
# Para clasificar con K-NN
library("class")
# Para clasificar con rpart
library("rpart")
library("rpart.plot")
# Para clasificar con randomForest
library("useful")
library("randomForest")

#cargamos el fichero de datos que utilizamos para desarrollar la PEC 2
nombreruta_PEC2 <- paste(getwd(),"/PEC2.csv", sep = "")

Data_PEC2 <- read.csv(nombreruta_PEC2, encoding="UTF-8",
                     header=TRUE, sep=",", na.strings="NA", dec=".", strip.white=TRUE)


5 Análisis univariable y bivariable del fichero


La primera fase del análisis consiste siempre en un análisis descriptivo de las variables incluidas en el fichero y de la relación existente entre ellas. Para ello, aplicamos la siguiente secuencia de cálculos y representaciones gráficas.

  1. Estadísticos descriptivos de las variables
  2. Representación gráfica de cada una de las variables
  3. Estudio de la relación entre las variables cuantitativas
  4. Estudio de la existencia de diferencias por comercial
# 1.Calculamos los descriptivos univariables de las variables del fichero
summary(Data_PEC2) #Estadísticos descriptivos básicos de las variables
##     Ingresos        Margen            Km           Visitas      Comercial
##  Min.   :4300   Min.   :200.0   Min.   :10.00   Min.   : 1.00   A:50     
##  1st Qu.:5100   1st Qu.:280.0   1st Qu.:16.00   1st Qu.: 3.00   B:50     
##  Median :5800   Median :300.0   Median :43.50   Median :13.00   C:50     
##  Mean   :5843   Mean   :305.4   Mean   :37.59   Mean   :11.99            
##  3rd Qu.:6400   3rd Qu.:330.0   3rd Qu.:51.00   3rd Qu.:18.00            
##  Max.   :7900   Max.   :440.0   Max.   :69.00   Max.   :25.00

Las 5 variables presentan una distribución bastante homogénea:

# 2.Representamos gráficamente las variables del fichero mediante histogramas

#Histograma Ingresos
f1 <- hist(Data_PEC2$Ingresos, main="Histograma Ingresos", col = "gray", labels = TRUE) 

f1
## $breaks
## [1] 4000 4500 5000 5500 6000 6500 7000 7500 8000
## 
## $counts
## [1]  5 27 27 30 31 18  6  6
## 
## $density
## [1] 6.666667e-05 3.600000e-04 3.600000e-04 4.000000e-04 4.133333e-04
## [6] 2.400000e-04 8.000000e-05 8.000000e-05
## 
## $mids
## [1] 4250 4750 5250 5750 6250 6750 7250 7750
## 
## $xname
## [1] "Data_PEC2$Ingresos"
## 
## $equidist
## [1] TRUE
## 
## attr(,"class")
## [1] "histogram"
#Histograma Margen
f2 <- hist(Data_PEC2$Margen, main="Histograma Margen", col = "gray", labels = TRUE) 

f2
## $breaks
##  [1] 200 220 240 260 280 300 320 340 360 380 400 420 440
## 
## $counts
##  [1]  4  7 13 23 36 25 18  9  9  3  2  1
## 
## $density
##  [1] 0.0013333333 0.0023333333 0.0043333333 0.0076666667 0.0120000000
##  [6] 0.0083333333 0.0060000000 0.0030000000 0.0030000000 0.0010000000
## [11] 0.0006666667 0.0003333333
## 
## $mids
##  [1] 210 230 250 270 290 310 330 350 370 390 410 430
## 
## $xname
## [1] "Data_PEC2$Margen"
## 
## $equidist
## [1] TRUE
## 
## attr(,"class")
## [1] "histogram"
#Histograma Km
f3 <- hist(Data_PEC2$Km, main="Histograma Km", col = "gray", labels = TRUE)

f3
## $breaks
##  [1] 10 15 20 25 30 35 40 45 50 55 60 65 70
## 
## $counts
##  [1] 37 13  0  1  4 11 21 21 17 16  5  4
## 
## $density
##  [1] 0.049333333 0.017333333 0.000000000 0.001333333 0.005333333
##  [6] 0.014666667 0.028000000 0.028000000 0.022666667 0.021333333
## [11] 0.006666667 0.005333333
## 
## $mids
##  [1] 12.5 17.5 22.5 27.5 32.5 37.5 42.5 47.5 52.5 57.5 62.5 67.5
## 
## $xname
## [1] "Data_PEC2$Km"
## 
## $equidist
## [1] TRUE
## 
## attr(,"class")
## [1] "histogram"
#Histograma Visitas
f4 <- hist(Data_PEC2$Visitas, main="Histograma Visitas", col = "gray", labels = TRUE)

f4
## $breaks
##  [1]  0  2  4  6  8 10 12 14 16 18 20 22 24 26
## 
## $counts
##  [1] 34 14  2  0  7  8 21 16 14 11  9 11  3
## 
## $density
##  [1] 0.113333333 0.046666667 0.006666667 0.000000000 0.023333333
##  [6] 0.026666667 0.070000000 0.053333333 0.046666667 0.036666667
## [11] 0.030000000 0.036666667 0.010000000
## 
## $mids
##  [1]  1  3  5  7  9 11 13 15 17 19 21 23 25
## 
## $xname
## [1] "Data_PEC2$Visitas"
## 
## $equidist
## [1] TRUE
## 
## attr(,"class")
## [1] "histogram"
#Histograma Comercial
f5 <- plot(Data_PEC2$Comercial)

f5
##      [,1]
## [1,]  0.7
## [2,]  1.9
## [3,]  3.1

Las variables cuantitativas presentan dos distribuciones diferenciadas:

# 3.Estudiamos la relación existente entre las variables del fichero

# Estudiamos la relación entre variables mediante gráficos de dispersión
f6<- plot(Data_PEC2)                                              

f6
## NULL
# Estudiamos la relación entre variables cuantitativas mediante correlaciones
cor(Data_PEC2[,c("Ingresos","Margen","Km","Visitas")], use="complete")
##            Ingresos     Margen         Km    Visitas
## Ingresos  1.0000000 -0.1093692  0.8717542  0.8179536
## Margen   -0.1093692  1.0000000 -0.4205161 -0.3565441
## Km        0.8717542 -0.4205161  1.0000000  0.9627571
## Visitas   0.8179536 -0.3565441  0.9627571  1.0000000

Analizando los gráficos de dispersión, apuntamos una fuerte relación entre Visitas-Km, Ingresos-Km, Margen-Km e Ingresos-Visitas que podemos validar con el coeficiente de correlación, estadístico que toma valores entre -1 y 1 y que mide la fuerza con la que dos variables quedan interrelacionadas (próximo a 1 cuando la relación es fuertemente directa y próximo a -1 cuando la relación es fuertemente inversa)

# Estudiamos la relación entre variables Km y Visitas
f7<-ggplot(Data_PEC2, aes(x=Km, y=Visitas)) + geom_point()
f7

# Estudiamos la relación entre variables Km y Visitas con tamaño ingresos
f8<-ggplot(Data_PEC2, aes(x=Km, y=Visitas)) + geom_point(aes(size=Ingresos))
f8

# Relación entre variables Km y Visitas con tamaño margen
f9<-ggplot(Data_PEC2, aes(x=Km, y=Visitas)) + geom_point(aes(size=Margen))
f9

# Relación entre variables Km y Visitas con tamaño margen
fA<-ggplot(Data_PEC2, aes(x=Km, y=Margen)) + geom_point(aes(size=Ingresos))
fA

# 3.Estudiamos la existencia de diferencias por Comercial

# promedio variables por comercial 
tapply(Data_PEC2$Ingresos,Data_PEC2$Comercial,mean)
##    A    B    C 
## 5006 5936 6588
tapply(Data_PEC2$Margen,Data_PEC2$Comercial,mean)
##     A     B     C 
## 341.8 277.0 297.4
tapply(Data_PEC2$Km,Data_PEC2$Comercial,mean)
##     A     B     C 
## 14.64 42.60 55.52
tapply(Data_PEC2$Visitas,Data_PEC2$Comercial,mean)
##     A     B     C 
##  2.44 13.26 20.26

Vemos que existen diferencias remarcables en el promedio de cada una de las variables para cada Comercial:

Graficamos a continuación las variables cuantitativas diferenciando por Comercial.

# Relación entre variables Km y Visitas con tamaño ingresos y Color según Comercial
f10<-ggplot(Data_PEC2, aes(x=Km, y=Visitas, color=Comercial)) + geom_point(aes(size=Ingresos))
f10

# Relación entre variables Km y Visitas con tamaño ingresos y Color según Comercial, línea tendencia y elipse
f11<-ggplot(Data_PEC2, aes(x=Km, y=Visitas, color=Comercial)) + geom_point(aes(size=Ingresos)) + 
  geom_smooth(method=lm, aes(fill=Comercial))+ stat_ellipse(type = "norm")
f11

Identificamos un comportamiento diferenciado donde Km y Visitas ya que son las variables que presentan una mayor capacidad de diferenciación.



6 Proceso de clasificación mediante K-NN.


Una vez analizado descriptivamente el fichero, consideramos necesario evaluar la capacidad predictiva de tres modelos predictivos:

Con dicho objetivo, aplicaremos los algoritmos siguiendo la siguiente secuencia:

6 Clasificación de los clientes con K-NN

 6.1 Construcción del Modelo de clasificación con _K-NN_  
 
 6.2 Validación del Modelo de clasificación con _K-NN_  
 

7 Clasificación de los clientes con árboles de decisión simples

 7.1 Construcción del Modelo de clasificación con el paquete _rpart_  
 
 7.2 Validación del Modelo de clasificación con el paquete _rpart_  
 

8 Clasificación de los clientes con árboles de decisión múltiples (random forest)

 8.1 Construcción del Modelo de clasificación con el paquete _randomForest_  
 
 8.2 Validación del Modelo de clasificación con el paquete _randomForest_  
 

6.1 Construcción del juego de datos de entrenamiento


Construimos un juego de datos de entrenamiento con el 70% de registros para construir los modelos y un juego de datos de pruebas con el 30% de registros restantes para validar los modelos.

# Dividimos el fichero en 70% entreno y 30% validación  #
set.seed(1234)
ind <- sample(2, nrow(Data_PEC2), replace=TRUE, prob=c(0.7, 0.3))
trainData <- Data_PEC2[ind==1,]
testData <- Data_PEC2[ind==2,]

6.2 Clasificación de los Comerciales con K-NN


Aplicamos el modelo K-NN, pasándole como parámetros la matriz de entrenamiento compuesta por las 4 variables cuantitativas : Importe, Margen, Km y Visitas. No le pasamos el campo Comercial porque precisamente es el campo que el algoritmo debe predecir.

Dado que el modelo K-NN permite replicar el modelo para n valores diferentes de k, repetimos el análisis para k=1,2,3 y 4.

# Aplicamos el algoritmo K-NN seleccionando 1 como k inicial
KnnTestPrediccion_k1 <- knn(trainData[,1:4],testData[,1:4], trainData$Comercial , k = 1, prob = TRUE )
# Visualizamos una matriz de confusión
table ( testData$Comercial , KnnTestPrediccion_k1 )
##    KnnTestPrediccion_k1
##      A  B  C
##   A 10  0  0
##   B  0  9  3
##   C  0  6 10
# Calculamos el % de aciertos para k=1
sum(KnnTestPrediccion_k1 == testData$Comercial)/ length(testData$Comercial)*100
## [1] 76.31579
# Aplicamos el algoritmo K-NN seleccionando 2 como k inicial
KnnTestPrediccion_k2 <- knn(trainData[,1:4],testData[,1:4], trainData$Comercial , k = 2, prob = TRUE )
# Visualizamos una matriz de confusión
table ( testData$Comercial , KnnTestPrediccion_k2 )
##    KnnTestPrediccion_k2
##      A  B  C
##   A 10  0  0
##   B  2  8  2
##   C  0  7  9
# Calculamos el % de aciertos para k=2
sum(KnnTestPrediccion_k2 == testData$Comercial)/ length(testData$Comercial)*100
## [1] 71.05263
# Aplicamos el algoritmo K-NN seleccionando 3 como k inicial
KnnTestPrediccion_k3 <- knn(trainData[,1:4],testData[,1:4], trainData$Comercial , k = 3, prob = TRUE )
# Visualizamos una matriz de confusión
table ( testData$Comercial , KnnTestPrediccion_k3 )
##    KnnTestPrediccion_k3
##      A  B  C
##   A 10  0  0
##   B  2  7  3
##   C  0  8  8
# Calculamos el % de aciertos para k=3
sum(KnnTestPrediccion_k3 == testData$Comercial)/ length(testData$Comercial)*100
## [1] 65.78947
# Aplicamos el algoritmo K-NN seleccionando 4 como k inicial
KnnTestPrediccion_k4 <- knn(trainData[,1:4],testData[,1:4], trainData$Comercial , k = 4, prob = TRUE )
# Visualizamos una matriz de confusión
table ( testData$Comercial , KnnTestPrediccion_k4 )
##    KnnTestPrediccion_k4
##     A B C
##   A 9 1 0
##   B 2 7 3
##   C 0 7 9
# Calculamos el % de aciertos para k=4
sum(KnnTestPrediccion_k4 == testData$Comercial)/ length(testData$Comercial)*100
## [1] 65.78947

Una vez aplicados el algoritmo para k=1,2,3 y 4. Mediante la matriz de confusión valoramos el nivel de acierto del modelo. Con dicho objetivo, estudiamos el % de acierto de cada uno de ellos con el objetivo de escoger el valor de k que permite obtener un % de clasificación correcta más alto:

  • para k=1 el porcentaje de aciertos es 76%

  • para k=2 el porcentaje de aciertos es 71%

  • para k=3 el porcentaje de aciertos es 66%

  • para k=4 el porcentaje de aciertos es 74%

En consecuencia tomamos el valor k=1 con un 76% de clasificación correcta.



7 Proceso de clasificación mediante árboles de decisión simples


Para construir un árbol de decisión es necesario definir una función que relaciona una variable categórica dependiente (factor) con n variables independientes que pueden ser categóricas o numéricas. En nuestro caso trabajaremos con:

El algoritmo de clasificación busca cuál es la variable que permite obtener una submuestra más diferenciada para la variable dependiente (Comercial en nuestro caso) e identifica también qué intervalos (si la variable es cuantitativa) ó agrupación de categorías de la/s variable/s independiente/s permitiría/n maximizar dicha división.

Una vez identificada la variable independiente que permite obtener la clasificación con una mayor capacidad de diferenciación, el proceso se repite reiterativamente en cada uno de los nodos obtenidos hasta que el algoritmo no encuentra diferencias significativas que le permitan seguir profundizando en los nodos.

Una vez obtenido una primera versión del árbol, existen algoritmos que permiten hacer un podado del árbol (prunning), eliminando aquellas ramas que no acaban de justificar su presencia de acuerdo con algunos parámetros preestablecidos.

En todos los casos seguiremos la siguiente secuencia de pasos para obtener los árboles de clasificación:

  1. Definir la muestra de entrenamiento y la muestra de prueba

  2. Definir la función que relaciona la variable dependiente con las variables independientes

  3. Estimar el árbol de decisión

  4. Representar gráficamente una primera versión del árbol

  1. Estudiar la capacidad predictiva del árbol

7.1 Clasificación de los Comerciales con árboles de decisión simples (paquete rpart)


Estudiamos a continuación la capacidad predictiva del árbol de decisión simple obtenido mediante el paquete rpart

# Dividimos el fichero en 70% entreno y 30% validación  (parte recurrente en todo experimento)
set.seed(1234)
ind <- sample(2, nrow(Data_PEC2), replace=TRUE, prob=c(0.7, 0.3))
trainData <- Data_PEC2[ind==1,]
testData <- Data_PEC2[ind==2,]
#Declaramos función del árbol
ArbolRpart <- Comercial ~ Ingresos + Margen + Km + Visitas
#Aplicamos algoritmo
ArbolRpart_ctree <- rpart(ArbolRpart, method="class", data=trainData)
#Obtenemos la relación de reglas de asociación del árbol en formato listado
print(ArbolRpart_ctree) # estadísticas detalladas de cada nodo
## n= 112 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 112 72 A (0.3571429 0.3392857 0.3035714)  
##   2) Km< 24.5 40  0 A (1.0000000 0.0000000 0.0000000) *
##   3) Km>=24.5 72 34 B (0.0000000 0.5277778 0.4722222)  
##     6) Visitas< 17.5 40  3 B (0.0000000 0.9250000 0.0750000) *
##     7) Visitas>=17.5 32  1 C (0.0000000 0.0312500 0.9687500) *
#Obtenemos el árbol con un diseño gráfico cuidado
f13<-rpart.plot(ArbolRpart_ctree,extra=4) #visualizamos el árbol

f13
## $obj
## n= 112 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 112 72 A (0.3571429 0.3392857 0.3035714)  
##   2) Km< 24.5 40  0 A (1.0000000 0.0000000 0.0000000) *
##   3) Km>=24.5 72 34 B (0.0000000 0.5277778 0.4722222)  
##     6) Visitas< 17.5 40  3 B (0.0000000 0.9250000 0.0750000) *
##     7) Visitas>=17.5 32  1 C (0.0000000 0.0312500 0.9687500) *
## 
## $snipped.nodes
## NULL
## 
## $xlim
## [1] -0.2  1.2
## 
## $ylim
## [1] -0.2  1.2
## 
## $x
## [1] 0.39728386 0.07949514 0.71507258 0.50321344 0.92693173
## 
## $y
## [1] 0.93468675 0.03376948 0.52517890 0.03376948 0.03376948
## 
## $branch.x
##        [,1]       [,2]      [,3]      [,4]      [,5]
## x 0.3972839 0.07949514 0.7150726 0.5032134 0.9269317
##          NA 0.07949514 0.7150726 0.5032134 0.9269317
##          NA 0.39728386 0.3972839 0.7150726 0.7150726
## 
## $branch.y
##       [,1]      [,2]      [,3]      [,4]      [,5]
## y 1.026378 0.1254608 0.6168702 0.1254608 0.1254608
##         NA 0.8327290 0.8327290 0.4232212 0.4232212
##         NA 0.8327290 0.8327290 0.4232212 0.4232212
## 
## $labs
## [1] "A\n.36  .34  .30"  "A\n1.00  .00  .00" "B\n.00  .53  .47" 
## [4] "B\n.00  .92  .07"  "C\n.00  .03  .97" 
## 
## $cex
## [1] 1
## 
## $boxes
## $boxes$x1
## [1]  0.28054780 -0.04785837  0.59833652  0.38647737  0.81019567
## 
## $boxes$y1
## [1]  0.88220993 -0.01870734  0.47270208 -0.01870734 -0.01870734
## 
## $boxes$x2
## [1] 0.5140199 0.2068487 0.8318086 0.6199495 1.0436678
## 
## $boxes$y2
## [1] 1.0263781 0.1254608 0.6168702 0.1254608 0.1254608
## 
## 
## $split.labs
## [1] ""
## 
## $split.cex
## [1] 1 1 1 1 1
## 
## $split.box
## $split.box$x1
## [1] 0.3076274        NA 0.5946265        NA        NA
## 
## $split.box$y1
## [1] 0.7935145        NA 0.3840067        NA        NA
## 
## $split.box$x2
## [1] 0.4869403        NA 0.8355187        NA        NA
## 
## $split.box$y2
## [1] 0.8719435        NA 0.4624357        NA        NA
# Estudiamos la evolución del error a medida que el árbol va creciendo
summary(ArbolRpart_ctree) # estadísticas detalladas de cada nodo
## Call:
## rpart(formula = ArbolRpart, data = trainData, method = "class")
##   n= 112 
## 
##          CP nsplit  rel error     xerror       xstd
## 1 0.5277778      0 1.00000000 1.15277778 0.06438675
## 2 0.4166667      1 0.47222222 0.55555556 0.07042952
## 3 0.0100000      2 0.05555556 0.08333333 0.03309688
## 
## Variable importance
##  Visitas       Km Ingresos   Margen 
##       34       31       21       14 
## 
## Node number 1: 112 observations,    complexity param=0.5277778
##   predicted class=A  expected loss=0.6428571  P(node) =1
##     class counts:    40    38    34
##    probabilities: 0.357 0.339 0.304 
##   left son=2 (40 obs) right son=3 (72 obs)
##   Primary splits:
##       Km       < 24.5 to the left,  improve=38.61111, (0 missing)
##       Visitas  < 8    to the left,  improve=38.61111, (0 missing)
##       Ingresos < 5450 to the left,  improve=27.51111, (0 missing)
##       Margen   < 305  to the right, improve=16.75806, (0 missing)
##   Surrogate splits:
##       Visitas  < 8    to the left,  agree=1.000, adj=1.00, (0 split)
##       Ingresos < 5450 to the left,  agree=0.929, adj=0.80, (0 split)
##       Margen   < 335  to the right, agree=0.839, adj=0.55, (0 split)
## 
## Node number 2: 40 observations
##   predicted class=A  expected loss=0  P(node) =0.3571429
##     class counts:    40     0     0
##    probabilities: 1.000 0.000 0.000 
## 
## Node number 3: 72 observations,    complexity param=0.4166667
##   predicted class=B  expected loss=0.4722222  P(node) =0.6428571
##     class counts:     0    38    34
##    probabilities: 0.000 0.528 0.472 
##   left son=6 (40 obs) right son=7 (32 obs)
##   Primary splits:
##       Visitas  < 17.5 to the left,  improve=28.401390, (0 missing)
##       Km       < 47.5 to the left,  improve=25.263500, (0 missing)
##       Ingresos < 7050 to the left,  improve= 6.469534, (0 missing)
##       Margen   < 295  to the left,  improve= 2.688889, (0 missing)
##   Surrogate splits:
##       Km       < 47.5 to the left,  agree=0.917, adj=0.812, (0 split)
##       Ingresos < 6350 to the left,  agree=0.708, adj=0.344, (0 split)
##       Margen   < 295  to the left,  agree=0.667, adj=0.250, (0 split)
## 
## Node number 6: 40 observations
##   predicted class=B  expected loss=0.075  P(node) =0.3571429
##     class counts:     0    37     3
##    probabilities: 0.000 0.925 0.075 
## 
## Node number 7: 32 observations
##   predicted class=C  expected loss=0.03125  P(node) =0.2857143
##     class counts:     0     1    31
##    probabilities: 0.000 0.031 0.969
printcp(ArbolRpart_ctree) # estadísticas de resultados
## 
## Classification tree:
## rpart(formula = ArbolRpart, data = trainData, method = "class")
## 
## Variables actually used in tree construction:
## [1] Km      Visitas
## 
## Root node error: 72/112 = 0.64286
## 
## n= 112 
## 
##        CP nsplit rel error   xerror     xstd
## 1 0.52778      0  1.000000 1.152778 0.064387
## 2 0.41667      1  0.472222 0.555556 0.070430
## 3 0.01000      2  0.055556 0.083333 0.033097
plotcp(ArbolRpart_ctree) # evolución del error a medida que se incrementan los nodos

# Validamos la capacidad de predicción del árbol con el fichero de validación
testPredRpart <- predict(ArbolRpart_ctree, newdata = testData, type = "class")
# Visualizamos una matriz de confusión
table(testPredRpart, testData$Comercial)
##              
## testPredRpart  A  B  C
##             A 10  0  0
##             B  0 12  2
##             C  0  0 14
# Calculamos el % de aciertos 
sum(testPredRpart == testData$Comercial)/ length(testData$Comercial)*100
## [1] 94.73684

El árbol de decisión obtenido mediante el paquete rpart clasifica correctamente un 94,73% de los registros. Un resultado bastante alto y aceptable.

Una vez construida una primera versión del árbol, estudiamos la viabilidad de un podado de árbol.

# Podado del árbol
pArbolRpart_ctree<- prune(ArbolRpart_ctree, cp= ArbolRpart_ctree$cptable[which.min(ArbolRpart_ctree$cptable[,"xerror"]),"CP"])
  pArbolRpart_ctree<- prune(ArbolRpart_ctree, cp= 0.02)
# Representación del árbol podado
f14<-rpart.plot(pArbolRpart_ctree,extra=4) #visualizamos el árbol

f14
## $obj
## n= 112 
## 
## node), split, n, loss, yval, (yprob)
##       * denotes terminal node
## 
## 1) root 112 72 A (0.3571429 0.3392857 0.3035714)  
##   2) Km< 24.5 40  0 A (1.0000000 0.0000000 0.0000000) *
##   3) Km>=24.5 72 34 B (0.0000000 0.5277778 0.4722222)  
##     6) Visitas< 17.5 40  3 B (0.0000000 0.9250000 0.0750000) *
##     7) Visitas>=17.5 32  1 C (0.0000000 0.0312500 0.9687500) *
## 
## $snipped.nodes
## NULL
## 
## $xlim
## [1] -0.2  1.2
## 
## $ylim
## [1] -0.2  1.2
## 
## $x
## [1] 0.39728386 0.07949514 0.71507258 0.50321344 0.92693173
## 
## $y
## [1] 0.93468675 0.03376948 0.52517890 0.03376948 0.03376948
## 
## $branch.x
##        [,1]       [,2]      [,3]      [,4]      [,5]
## x 0.3972839 0.07949514 0.7150726 0.5032134 0.9269317
##          NA 0.07949514 0.7150726 0.5032134 0.9269317
##          NA 0.39728386 0.3972839 0.7150726 0.7150726
## 
## $branch.y
##       [,1]      [,2]      [,3]      [,4]      [,5]
## y 1.026378 0.1254608 0.6168702 0.1254608 0.1254608
##         NA 0.8327290 0.8327290 0.4232212 0.4232212
##         NA 0.8327290 0.8327290 0.4232212 0.4232212
## 
## $labs
## [1] "A\n.36  .34  .30"  "A\n1.00  .00  .00" "B\n.00  .53  .47" 
## [4] "B\n.00  .92  .07"  "C\n.00  .03  .97" 
## 
## $cex
## [1] 1
## 
## $boxes
## $boxes$x1
## [1]  0.28054780 -0.04785837  0.59833652  0.38647737  0.81019567
## 
## $boxes$y1
## [1]  0.88220993 -0.01870734  0.47270208 -0.01870734 -0.01870734
## 
## $boxes$x2
## [1] 0.5140199 0.2068487 0.8318086 0.6199495 1.0436678
## 
## $boxes$y2
## [1] 1.0263781 0.1254608 0.6168702 0.1254608 0.1254608
## 
## 
## $split.labs
## [1] ""
## 
## $split.cex
## [1] 1 1 1 1 1
## 
## $split.box
## $split.box$x1
## [1] 0.3076274        NA 0.5946265        NA        NA
## 
## $split.box$y1
## [1] 0.7935145        NA 0.3840067        NA        NA
## 
## $split.box$x2
## [1] 0.4869403        NA 0.8355187        NA        NA
## 
## $split.box$y2
## [1] 0.8719435        NA 0.4624357        NA        NA

Dado que el árbol original es muy simple. El podado no devuelve ninguna versión nueva reducida.


8 Proceso de clasificación mediante árboles de decisión múltimples (paquete randomForest)


Una vez evaluada la capacidad predictiva del algoritmo K-NN, y los árboles de decisión simples obtenidos mediante el paquete rpart, estimamos el modelo que obtendríamos si ejecutásemos n árboles de decisión simultáneamente (para n=100 en nuestro caso) mediante el algoritmo randomForest.

El algoritmo randomForest es un método de estimación combinado, donde el resultado de la estimación se construye a partir de los resultados obtenidos mediante el cálculo de n árboles donde los predictores son incluidos al azar.

Es un método complejo con ventajas e inconvenientes respecto a los árboles de clasificación simples:

Ventajas

Inconvenientes

# Dividimos el fichero en 70% entreno y 30% validación  (parte recurrente en todo experimento)
set.seed(1234)
ind <- sample(2, nrow(Data_PEC2), replace=TRUE, prob=c(0.7, 0.3))
trainData <- Data_PEC2[ind==1,]
testData <- Data_PEC2[ind==2,]
#Declaramos función del árbol
ArbolRF <- Comercial ~ Ingresos + Margen + Km + Visitas
#Aplicamos algoritmo
ArbolRF_ctree <- randomForest(ArbolRF, data=trainData, ntree=100,proximity=T) #indicamos el número de árboles mediante ntree=100
#Obtenemos la importancia de cada variable en el proceso de clasificación
importance(ArbolRF_ctree)      #Importancia de las variables en formato text
##          MeanDecreaseGini
## Ingresos         7.731585
## Margen           1.775483
## Km              28.376887
## Visitas         35.959795
f15<-varImpPlot(ArbolRF_ctree) #Importancia de las variables en formato gráfico

f15
##          MeanDecreaseGini
## Ingresos         7.731585
## Margen           1.775483
## Km              28.376887
## Visitas         35.959795
#evolución del error según el número de árboles
f16<-plot(ArbolRF_ctree, main = "")  

head(f16)
##             OOB A          B          C
## [1,] 0.04347826 0 0.05882353 0.07142857
## [2,] 0.05714286 0 0.11111111 0.06250000
## [3,] 0.08333333 0 0.10344828 0.16000000
## [4,] 0.08510638 0 0.09375000 0.16666667
## [5,] 0.07920792 0 0.08571429 0.15625000
## [6,] 0.07619048 0 0.08333333 0.14705882
# Validamos la capacidad de predicción del árbol con el fichero de validación
  testPredRF <- predict(ArbolRF_ctree, newdata = testData)
  table(testPredRF, testData$Comercial)
##           
## testPredRF  A  B  C
##          A 10  0  0
##          B  0 12  2
##          C  0  0 14
# Calculamos el % de aciertos 
sum(testPredRF == testData$Comercial)/ length(testData$Comercial)*100
## [1] 94.73684

El árbol de decisión obtenido mediante el paquete randomForest clasifica correctamente un 94,73% de los registros. Un resultado bastante alto y aceptable.



9 Ejercicios


9.1 Pregunta 1. ¿La capacidad predictiva del modelo K-NN se incrementa si aumentamos la k hasta 10 (k=5, 6, 7, 8, 9 y 10)?

Al aumentar el K la probabilidad se mantiene estable salvo en k=6 que sube ligereamente hasta un 73%. Tras k=6, el resto de porcentajes de acierto para k=7,8,9,10 desciende respecto a los valores anteriores.

9.2 Pregunta 2. ¿Qué variables influyen significativamente en el proceso de estimación de cada método?

Indica cómo podemos identificar las variables influyentes de cada modelo. Verifica si cada algoritmo (knn, rpart o randomForest) contiene información relativa a la relevancia de cada variable del juego de datos.

Mientras que el KNN clasifica y predice, el rprt nos da información sobre las variables significativas: km y visitas (en ese orden); por otro lado, randomForest nos ofrece también las variables significativas, que coinciden con las anteriores pero en orden inverso: visitas y km.

9.3 Pregunta 3. Interpreta el gráfico fA donde se relacionan las variables Km, Margen e Ingresos

Interpreta el gráfico en términos de negocio e identifica oportunidades de mejora y riesgos.

En el gráfico apreciamos que los ingresos son más altos en aquellos clientes en los que se invierten más kilómetros. No obstante, el margen que deja cada cliente debería ser un dato a tener en cuenta de forma mas significativa. De esta forma, vemos que esta variable toma sus valores más altos en algunos clientes que no tienen tantos ingresos pero en los que no se invierten tantos kilómetros. Igual ocurre con el número de visitas, donde el margen toma sus valores más altos en algunos clientes que requieren menos visitas. Haciendo un par de gráficos adicionales, vemos cómo se clasifican estas variables en función del tipo de comerciales, por lo que:

# Relación entre variables Km y Margen con tamaño ingresos y Color según Comercial, línea tendencia y elipse
f20<-ggplot(Data_PEC2, aes(x=Km, y=Margen, color=Comercial)) + geom_point(aes(size=Ingresos)) + geom_smooth(method=lm, aes(fill=Comercial))+ stat_ellipse(type = "norm")
f20

# Relación entre variables Km y Visitas con tamaño ingresos y Color según Comercial, línea tendencia y elipse
f21<-ggplot(Data_PEC2, aes(x=Visitas, y=Margen, color=Comercial)) + geom_point(aes(size=Ingresos)) + geom_smooth(method=lm, aes(fill=Comercial))+ stat_ellipse(type = "norm")
f21

Así, vemos que el comercial A invierte menos en kilómetros y visitas, a pesar de tener unos ingresos menores, pero dejando un mayor margen en los clientes. El margen que obtiene el comercial C es más alto que el B, a pesar de invertir más kilómetros y visitas, pero consigue unos ingresos mayores. A nivel de negocio, parece ser que el proceso que lleva a cabo el comercial A es más eficiente que el resto, seguido por el comercial C y el B. Debería apostarte por seguir la dinámica del comercial A.

9.4 Pregunta 4. A partir de los resultados obtenidos ¿Qué algoritmo/s deberíamos tomar como referencia para hacer predicciones sobre el fichero de trabajo?

Elaborad vuestra respuesta considerando la facilidad de cálculo, la facilidad de interpretación de resultados, la capacidad predictiva de cada modelo y el principio de parsimonia (ante dos soluciones tomaremos siempre la más simple)

Según los resultados obtenidos, rpart y randomForest nos dan mejor porcentaje de acierto en la predicción de la clasificación. Entre estos dos, con random forest podemos distinguir el error en cada una de las clasificaciones.

9.5 Pregunta 5.1. Si rpart y randomForest tiene el mismo porcentaje de clasificación correcta ¿están asignando los mismos individuos a cada grupo de comerciales?

En este caso rpart y rforest coinciden en el porcentaje de clasificación correcta y en la asignación de los mismos individuos a cada grupo de comerciales, pero no tiene por qué ser así en todos los casos, ya que podrían haber asignado erróneamente comerciales a diferentes grupos pero manteniendo el mismo porcentaje de acierto.

9.6 Pregunta 5.2 ¿Qué individuos no están clasificados del mismo modo que el resultado de K-NN para k=2

Viendo las matrices de confusión:

#Matriz confusión KNN=2
table(testData$Comercial , KnnTestPrediccion_k2 )
##    KnnTestPrediccion_k2
##      A  B  C
##   A 10  0  0
##   B  2  8  2
##   C  0  7  9
#Matriz de confusión rpart
table(testData$Comercia, testPredRpart)
##    testPredRpart
##      A  B  C
##   A 10  0  0
##   B  0 12  0
##   C  0  2 14
#Matriz de confusión randomForest
table(testData$Comercia , testPredRF)
##    testPredRF
##      A  B  C
##   A 10  0  0
##   B  0 12  0
##   C  0  2 14

Podemos entonces apreciar las diferencias de clasificación entre los 3 algoritmos. El algoritmo KNN con K=2 predice 2 individuos en A que pertenecen a B, 2 en C que pertenecen a B y 7 en B que pertenecen a C.

9.7 Pregunta 6. Mejorar el resultado del algoritmo K-NN, mediante la estandarización de las variables y mediante el algoritmo PCA.

Mediante el análisis de componentes principales (PCA) generaremos una variable nueva (el componente principal comp1), que podemos incluir como variable de nuestro juego de datos. Aplicando el algoritmo K-NN al nuevo juego de datos, verificaremos si se experimenta alguna mejora en la capacidad predictiva.

El análisis de componentes principales (PCA) trata de buscar un sistema de coordenadas más adiente para representar el juego da datos.

Estas coordenadas nuevas les llama componentes y lo que persigue el algoritmo es que estos componentes o coordenadas recojan la máxima variabilidad posible.

En nuestro caso variabilidad significa información y relevancia. Cuanta más variabilidad haya en un componente, más información acumula del juego de datos y en consecuencia más relevante será para realizar predicciones sobre el juego de datos.

En esta pregunta veremos como podemos usar PCA para mejorar las predicciones de otros algoritmos como los árboles de decisión y KNN.

Inicialmente generamos el juego de datos newData_PEC2 al que añadimos una nueva columna que contiene las proyecciones del juego de datos sobre el componente 1 del sistema de coordenadas óptimo que se ha determinado con el algoritmo PCA.

# Estandarizamos el juego de datos para que todas las variables estén expresadas en la misma escala. 
scData_PEC2 <- as.data.frame(scale(Data_PEC2[,1:4]))

# Aplicamos el algoritmo de componentes principales
pcaPEC2 <- princomp(scData_PEC2[,1:4])
summary(pcaPEC2) 
## Importance of components:
##                           Comp.1    Comp.2     Comp.3      Comp.4
## Standard deviation     1.7004154 0.9565979 0.38258453 0.143074535
## Proportion of Variance 0.7277045 0.2303052 0.03683832 0.005151927
## Cumulative Proportion  0.7277045 0.9580098 0.99484807 1.000000000
# Construimos un nuevo juego de datos, añadiendo
# el componente PCA que contiene más información: Comp1
newData_PEC2 = scData_PEC2[,1:4]
newData_PEC2$Comp1 = pcaPEC2$scores[,1]
newData_PEC2$Comercial = Data_PEC2[,5]
head(newData_PEC2)
##     Ingresos     Margen        Km   Visitas     Comp1 Comercial
## 1 -0.8976739  1.0286113 -1.336794 -1.308593 -2.256981         A
## 2 -1.1392005 -0.1245404 -1.336794 -1.308593 -2.079459         A
## 3 -1.3807271  0.3367203 -1.393470 -1.308593 -2.360044         A
## 4 -1.5014904  0.1060900 -1.280118 -1.308593 -2.296504         A
## 5 -1.0184372  1.2592416 -1.336794 -1.308593 -2.380802         A
## 6 -0.5353840  1.9511326 -1.166767 -1.046525 -2.063623         A

Al añadir una variable adicional (componente 1) la predicción mejora pasando a un 94% para K=1, 97% para k=5 y 97% para k=10.