Diferencia en la implementación de divisiones binarias en árboles de decisión

12

Tengo curiosidad acerca de la implementación práctica de una división binaria en un árbol de decisión, ya que se relaciona con los niveles de un predictor categórico .Xj

Específicamente, a menudo utilizaré algún tipo de esquema de muestreo (por ejemplo, embolsado, sobremuestreo, etc.) al construir un modelo predictivo utilizando un árbol de decisión, con el fin de mejorar su precisión y estabilidad predictivas. Durante estas rutinas de muestreo, es posible presentar una variable categórica a un algoritmo de ajuste de árbol con un conjunto de niveles inferior al completo.

Digamos que una variable X toma niveles {A,B,C,D,E}. En una muestra, quizás solo {A,B,C,D}hay niveles presentes. Luego, cuando el árbol resultante se usa para la predicción, el conjunto completo puede estar presente.

Continuando con este ejemplo, digamos que un árbol se divide en X y envía {A,B}a la izquierda y {C,D}a la derecha. Esperaría que la lógica de la división binaria diga, cuando se enfrente con nuevos datos: "Si X tiene el valor A o B, envíe a la izquierda, de lo contrario, envíe este caso a la derecha". Lo que parece suceder en algunas implementaciones es "si X tiene el valor A o B, enviar a la izquierda, si X tiene el valor C o D enviar a la derecha". Cuando este caso adquiere el valor E, el algoritmo se descompone.

¿Cuál es la forma "correcta" de manejar una división binaria? Parece que la forma mucho más robusta se implementa a menudo, pero no siempre (ver Rpart más abajo).

Aqui hay un par de ejemplos:

Rpart falla, los otros están bien.

#test trees and missing values

summary(solder)
table(solder$PadType)

# create train and validation
set.seed(12345)
t_rows<-sample(1:nrow(solder),size=360, replace=FALSE)
train_solder<-solder[t_rows,]
val_solder<-solder[-t_rows,]

#look at PadType
table(train_solder$PadType)
table(val_solder$PadType)
#set a bunch to missing
levels(train_solder$PadType)[train_solder$PadType %in% c('L8','L9','W4','W9')] <- 'MISSING'


#Fit several trees, may have to play with the parameters to get them to split on the variable

####RPART
mod_rpart<-rpart(Solder~PadType,data=train_solder)
predict(mod_rpart,val_solder)
#Error in model.frame.default(Terms, newdata, na.action = na.action, xlev = attr(object,  : 
#factor 'PadType' has new level(s) D6, L6, L7, L8, L9, W4

####TREE
mod_tree<-tree(Solder~PadType,data=train_solder,split="gini")
predict(mod_tree,val_solder) #works fine

####ctree
mod_ctree<-ctree(Solder~PadType,data=train_solder,control = ctree_control(mincriterion = 0.05))
predict(mod_ctree,val_solder) #works fine
B_Miner
fuente

Respuestas:

9

De hecho, hay dos tipos de factores: ordenados (como Tiny <Small <Medium <Big <Huge) y no ordenados (Pepino, Zanahoria, Hinojo, Berenjena).
La primera clase es igual a las continuas: solo es más fácil verificar todos los pivotes, tampoco hay ningún problema con extender la lista de niveles.
Para la segunda clase, debe hacer un conjunto de elementos que se dirigirán en una rama, dejando el resto en la otra, en este caso puede:

  1. error de lanzamiento
  2. suponga que la clase invisible se dirige a su rama favorita
  3. trate esto como NA y seleccione una rama de forma menos aleatoria.

12#categories-1-1yoyo

Diría que la idea más sensata es hacer que el usuario defina el conjunto completo de factores (por ejemplo, R hace esto orgánicamente, preservando los niveles a través de operaciones de subconjunto) y use la opción 1. para niveles no declarados y la opción 2. para los declarados . La opción 3. puede tener sentido si ya tiene alguna infraestructura de manejo de NA.

*) También existe una estrategia secundaria para realizar una nueva codificación no trivial de los niveles en números, como por ejemplo la codificación Breiman, aunque esto genera aún más problemas.


fuente
1
¿Estás diciendo que ctree o tree en mi ejemplo en realidad trata este factor desordenado como un factor ordenado y por lo tanto lo envía a la rama "0"?
B_Miner
@mbq, ¿puede explicar por qué el número total de formas en que puede hacer las divisiones es 2 ^ (# categorías + 1) - 2. No entiendo por qué la parte "-2".
kasa
Hmm, parece que he jodido esta fórmula; hay como 2 ^ n palabras de n bits, pero no contamos las palabras a y ~ a, entonces 2 ^ (n-1), y no nos gustan las divisiones que no se derraman en absoluto, entonces 2 ^ (n-1) -1 (en otras palabras, contamos desde 1). n = 1 es entonces un caso especial.