Monográfico. Arboles de clasificación con RPART

31 Ago

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_ini
pvi=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

21 respuestas a «Monográfico. Arboles de clasificación con RPART»

  1. ¿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»).

  2. 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.

  3. 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.

  4. 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

  5. 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.

  6. 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

  7. 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…

  8. 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

  9. 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.

  10. 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

  11. 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

  12. 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..!!

  13. 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.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *