Con este rápido monográfico voy a acercarnos a los árboles de regresión con R. Esta metodología de predicción realiza construcciones lógicas que establecen reglas que nos permiten clasificar observaciones en función de una variable respuesta y de las relaciones existentes entre las variables dependientes. En esta primera aproximación no no voy a entrar en algoritmos ni en tipos de árboles (hay suficiente documentación en la red) intentaré en despertar la curiosidad del lector sobre este tipo de análisis y sobre todo quiero acercar a R al mundo empresarial un ámbito donde creo que R no destaca (al menos en España).
Como es habitual voy a plantear un ejemplo e iremos analizando las posibilidades del paquete rpart. También quiero que este ejemplo sirva como introducción a la generación de datos aleatorios con R. Con esto la idea es “simular” la cartera de un banco que tiene 380 clientes y quiere estudiar la propensión a la contratación de una pensión vitalicia inmediata (PVI). Para el estudio va a emplear las siguientes variables:
- El saldo en vista
- El máximo saldo en planes de pensiones individuales en los últimos 3 años
- El saldo en fondos
- La edad del cliente
Generamos aleatoriamente un data.frame con la cartera de clientes:
saldo_vista=runif(380,0,1)*10000
saldo_ppi=(runif(380,0.1,0.2)*rpois(380,1))*100000
saldo_fondos=(runif(380,0.1,0.9)*(rpois(380,1)-1>0))*100000
edad=rpois(380,60)
datos_ini<-data.frame(cbind(saldo_vista,saldo_ppi,saldo_fondos,edad))
datos_inisaldo_ppi=(edad<=68)*datos_inisaldo_ppi
Runif y rpois generan vectores aleatorios, rpois con distribuciones de poisson de media indicada en el segundo parámetro y runif genera números aleatorios de distribución uniforme entre el segundo y el tercer parámetro de la función. Son dos funciones interesantes para generar números aleatorios enteros y reales. Estas serán nuestras variables dependientes, pero necesitamos generar nuestra variable independiente de nuestro modelo. Para ello vamos a crear un potencial de contratación de PVI para cada cliente y le vamos a aumentar este potencial ligeramente para ver como se comporta el árbol que realicemos:
datos_inipotencial=runif(1,0,1)+
(log(edad)/(log(68))/100) +
runif(1,0,0.001)*(saldo_vista>5000)+
runif(1,0,0.001)*(saldo_fondos>10000)+
runif(1,0,0.007)*(saldo_ppi>10000)-
runif(1,0,0.2)
datos_inipvi=as.factor((datos_inipotencial>=quantile(datos_inipotencial,0.90))*1)
El potencial es un número aleatorio entre 0 y 1 que aumentamos si la edad es superior a 68 años y si los saldos son superiores a determinadas cantidades. Por último creamos la variable pvi en el data.frame datos_ini que tomará valores 0 o 1 en función de si el potencial está por encima del percentil 90, así nos aseguramos que un 10% aproximadamente de las observaciones tienen un valor 1.
table(datos_ini$pvi)
0 1
340 39
Algo más de un 10% de las observaciones tiene valor 1 para pvi. En este punto ya podemos realizar nuestro árbol, necesitaremos disponer del paquete RPART:
library(rpart)
> arbol=rpart(pvi~saldo_vista+saldo_ppi+saldo_fondos+edad,data=datos_ini)
> arbol
n= 380
node), split, n, loss, yval, (yprob)
* denotes terminal node
1) root 380 39 0 (0.8973684 0.1026316)
2) saldo_vista< 5055.117 204 0 0 (1.0000000 0.0000000) *
3) saldo_vista>=5055.117 176 39 0 (0.7784091 0.2215909)
6) edad< 66.5 133 14 0 (0.8947368 0.1052632)
12) saldo_fondos< 6146.241 88 0 0 (1.0000000 0.0000000) *
13) saldo_fondos>=6146.241 45 14 0 (0.6888889 0.3111111)
26) edad< 57.5 23 0 0 (1.0000000 0.0000000) *
27) edad>=57.5 22 8 1 (0.3636364 0.6363636)
54) saldo_ppi< 5267.955 8 0 0 (1.0000000 0.0000000) *
55) saldo_ppi>=5267.955 14 0 1 (0.0000000 1.0000000) *
7) edad>=66.5 43 18 1 (0.4186047 0.5813953)
14) saldo_ppi< 6573.219 35 17 0 (0.5142857 0.4857143)
28) edad< 75.5 25 9 0 (0.6400000 0.3600000)
56) saldo_vista>=7725.819 18 5 0 (0.7222222 0.2777778) *
57) saldo_vista< 7725.819 7 3 1 (0.4285714 0.5714286) *
29) edad>=75.5 10 2 1 (0.2000000 0.8000000) *
15) saldo_ppi>=6573.219 8 0 1 (0.0000000 1.0000000) *
Vemos como se compone el objeto arbol. Es evidente que lo mejor es estudiar el árbol gráficamente, para ello hemos de emplear la siguiente sintaxis:
plot(arbol); text(arbol, use.n=TRUE)
Todos aquellos clients con un saldo en vista menor a 5.000 € no deben formar parte del modelo porque ellos mismos forman un grupo, deben ser eliminados. Empleamos subset:
datos_dos=subset(datos_ini,saldo_vista>5000)
arbol=rpart(pvi~saldo_vista+saldo_ppi+saldo_fondos+edad,data=datos_dos)
plot(arbol); text(arbol, use.n=TRUE)
Vemos una sumarización del modelo:
printcp(arbol)
Classification tree:
rpart(formula = pvi ~ saldo_vista + saldo_ppi + saldo_fondos +
edad, data = datos_dos)
Variables actually used in tree construction:
[1] edad saldo_fondos saldo_ppi saldo_vista
Root node error: 39/181 = 0.21547
n= 181
CP nsplit rel error xerror xstd
1 0.179487 0 1.00000 1.00000 0.14183
2 0.119658 1 0.82051 1.02564 0.14313
3 0.089744 4 0.46154 0.87179 0.13474
4 0.025641 6 0.28205 0.76923 0.12828
5 0.010000 7 0.25641 0.82051 0.13160
Podemos visualizar un gráfico para la validación cruzada del modelo realizado:
par(mfrow=c(1,2));rsq.rpart(arbol);
A la vista del gráfico del R-cuadrado es evidente que estamos sobreestimando la variable dependiente. Habría que trabajar el modelo. También es posible realizar podas de las ramas de nuestro árbol con la función prune:
podaarbol=prune(arbol,cp= 0.119658 )
plot(podaarbol);text(podaarbol)
par(mfrow=c(1,2));rsq.rpart(podaarbol);
Vemos que en este primer acercamiento a los árboles de clasificación con R disponemos de algunas herramientas interesantes. El problema es que el algoritmo que realiza la clasificación sólo admite 400 observaciones (aproximadamente) por lo que no nos serviría para una aplicación en el mundo empresarial. De todos modos contamos con una comunidad de programadores detrás que siguen mejorando el producto. Para una siguiente entrega trabajaremos con el party. Para cualquier duda, sugerencia o encontráis un error en mi planteamiento (aprendemos todos juntos) estoy a vuestra disposición en rvaquerizo@analisisydecision.es
¿400 observaciones? Yo usé una vez rpart, que recuerde, con 130.000 observaciones (y algo así como 40-50 variables) sin mayores problemas.
El motor de rpart está desarrollado en C y el que lo revise se dará cuenta de que es tremendamente eficiente. Es casi un ejemplo de cómo se debería programar en dicho lenguaje. Ni falta ni sobra una coma.
Uno de los principales problemas que observo en dicho código (derivado de su relativa antigüedad) es que no es paralelizable. Las técnicas multihilo son muy adecuadas para algoritmos recursivos ( y rpart significa «recursive partitioning»).
Tienes razón Carlos, algo mal debí hacer. He hecho pruebas con 30.000 observaciones y no tengo problemas.
De todos modos tengo que establecer un límite para rpart del tipo 1.000.000 de observaciones y 50 variables.
Deberíamos, pues, hacer un estudio a fondo de cuántos datos puede «tragarse» en función de la RAM disponible. Igual la solución es simplemente comprar alguna extensión de memoria, que es barata.
Hola,
ante todo gracias por hacer un blog tan interesante, soy otro enamorado del data mining que también tiene un blog junto con otro compañero, os dejo aquí la dirección:
http://geomarketingspain.blogspot.com/
Os invito a que os paseis.
En lo referente a la capacidad de R de tratar grandes volumnes de datos sería muy interesante encontar su límite con sistemas operativos de 64Bits, ya que R trabaja con memoria física, RAM, esto nos permitiría pasar de 2 ^32 (unos 4 GB) a 2 ^ 64 (16 exabytes) siempre hablando de manera teorica. Si no recuerdo mal las últimas versiones de Linux pueden gestionar 64 GB en comparacion con los 4 que puede lidiar un XP es una gran diferencia.
Por otro lado hay un paquete muy interesante para el tratamiento de árboles predictivos, R puede trabajar con los algoritmos de WEKA, pero estaremos de a cuerdo que que trabajar con objetos tan diferentes como los que da RPART y RWEKA es complicado, pues hay alguién que ya ha pensado en ello. El paquete se llama partikit y lo podeis encontrar aquí:
http://r-forge.r-project.org/projects/partykit/
No sólo estandariza los objetos «árbol predictivo» sinó que tiene un método propio de explotación así como la exportación a PMML.
Hola Francisco. Carlos Gil ya nos hablaba de partykit https://analisisydecision.es/partykit-un-paquete-de-r-para-generar-y-manipular-arboles-de-decision/ tengo pendiente realizar un monográfico estudiándolo para acercarlo a la gente que me sigue.
Está muy muy bien tu web, la añadimos a favoritos.
Saludos.
Hola de nuevo, me estoy iniciando con esto, y tengo otra pregunta.
Cuando uso rpart, ¿ha de ser así?:
la variable dependiente categorical
las independientes da lo mismo(categorical o continuas)
Es que como comenté ayer en el hilo de rforest, tengo muchas variables, continuas y categóricas, y quiero reducir la dimensión del problema. Busco una función de R que me permita hacer esto.
Gracias de nuevo
La dependiente mejor que sea un factor as.factor() para rpart, también puede ser continua, pero si quieres modelos con dependientes continuas hay otros modelos más apropiados.
Haz una selección previa de todas las variables en base a su importancia y a su significado. Este significado es el que al final lo complica todo.
Hola muy interesante esta publicación, me pueden ayudar con esta pregunta?
necesito guardar los limites de las particiones de los arboles en un data.frame, hay algún código para poder hacerlo, gracias de antemano
aperez, ¿no te sirve predict?
Mira el paquete pmml. Saludos.
Hola aperez, con tu.arbol$splits deberías obtener toda la información que necesitas. Respecto al rendimiento, he hecho pruebas con 380k filas x 180 variables y se ha portado. Lo único que no le presté mucha atención a cuanta memoria estaba consumiendo…
Fernando, sería interesante analizar cuánto puede tragarse un árbol en R. ¿No hay ningún análisis al respecto verdad?
No :( . Si vuelvo al tema le prestaré algo más de antención a la memoria…
Hola tengo un pregunta al correr el arbol me sale que solo es raiz (root) no se porque y no me grfica
HOla Nathaly, tengo el mismo problema, creo que tal vez se debe a que son pocas los registros que estoy poniendo…lo has podido resolver? o se te ocurre alguna otra razón por lo que no se obtiene el árbol? Saludos
rpart tiene una serie de parámetros que tienen que ver con la severidad de la poda. Podéis probar a relajar algunos de los valores por defecto. Normalmente solo es raiz cuando el algoritmo ha determinado que la variable o variables que se han introducido no tienen suficiente capacidad predictiva sobre la variable target. Relajando estos parámetros en el mejor de los casos podéis conseguir obtener un árbol con más ramas, pero OJO, con el riesgo de estar sobreajustando los datos. Evidentemente el número de observaciones tiene impacto en esto.
Hola….tengo un problema al correr rpart… los datos son 154334 con cuatro variables ……y solo obtengo un nodo no se construye el árbol…aparece:
n= 154334
node), split, n, deviance, yval
* denotes terminal node
1) root 154334 951827900000 331.9997 *
sin embargo cuando utilizo menos datos si se genera el árbol..cual es el problema o el exceso de datos?
y felicito por este importante blog…
Pedro
Hola. Mírate la ayuda de rpart y de rpart.control. Con rpart.control se pueden definir ciertos criterios sobre el tamaño del árbol, como el número mínimo de elementos en un nodo para considerar su partición (minsplit) o el parámetro de complejidad del árbol (cp)..
Saludos
Hola
Dejo un ejemplo de arbol en R, quizas pueda servir:
http://apuntes-r.blogspot.com/2014/09/predecir-perdida-de-clientes-con-arbol.html
hola! soy nueva en estema de arboles y programación, estoy haciendo un trabajo en la universidad tengo que trabajar con arboles de supervivencia con el paquete RPART, que tipos de algoritmos utilizan??? donde puedo buscar información..?? gracias..!!
Hola!
Estoy tratando de estimar varios arboles de clasificación y me gustaría saber si alguien conoce alguna forma de especificar el criterio de clasificacion (categorica): Gini, Chi2, entriopia. Para poder así, tener varios arboles con distinta metodología para compararlos entre sí. Por lo que he encontrado en el paquete «rpart» unicamente puedo seleccionar method=»class» para este tipo de arboles.
Muchas gracias.
Hola, me gustaría saber que hace rpart con los valores perdidos.Gracias
Saludos.