Bosques Aleatorios y Valores Faltantes
Random Forests and Missing Values.
Hay una solución práctica muy intrigante
Fuera de algunos conjuntos de datos excesivamente limpios que se encuentran en línea, los valores faltantes están en todas partes. De hecho, cuanto más complejo y grande sea el conjunto de datos, es más probable que haya valores faltantes. Los valores faltantes son un campo fascinante de investigación estadística, pero en la práctica a menudo son una molestia.
Si se trata de un problema de predicción en el que se desea predecir una variable Y a partir de covariables de p dimensiones X =(X_1,…,X_p) y se enfrenta a valores faltantes en X, hay una solución interesante para los métodos basados en árboles. Este método es en realidad bastante antiguo, pero parece funcionar notablemente bien en una amplia gama de conjuntos de datos. Estoy hablando del “criterio de incorporación de valores faltantes en atributos” (MIA; [1]). Si bien hay muchos buenos artículos sobre valores faltantes (como este ), este enfoque poderoso parece ser algo subutilizado. En particular, no es necesario imputar, eliminar o predecir sus valores faltantes de ninguna manera, sino que simplemente se puede ejecutar la predicción como si se tuviera datos completamente observados.
Explicaré rápidamente cómo funciona el método en sí, y luego presentaré un ejemplo con el bosque aleatorio distribucional (DRF) explicado aquí . Elegí DRF porque es una versión muy general de Random Forest (en particular, también se puede usar para predecir un vector aleatorio Y) y porque estoy algo sesgado aquí. MIA está implementado en realidad para el bosque aleatorio generalizado ( GRF ), que cubre una amplia gama de implementaciones de bosques. En particular, dado que la implementación de DRF en CRAN se basa en GRF, después de una ligera modificación, también se puede utilizar el método MIA.
Por supuesto, tenga en cuenta que esta es una solución rápida que (hasta donde yo sé) no tiene garantías teóricas. Dependiendo del mecanismo de valores faltantes, puede sesgar en gran medida el análisis. Por otro lado, la mayoría de los métodos comúnmente utilizados para tratar valores faltantes no tienen garantías teóricas o están directamente sesgados en el análisis y, al menos empíricamente, MIA parece funcionar bien y
Cómo funciona
Recuerde que en un RF, las divisiones se construyen en la forma X_j < S o X_j ≥ S , para una dimensión j=1,…,p . Para encontrar este valor de división S, se optimiza algún tipo de criterio en los Y, por ejemplo el criterio CART. Así, las observaciones se dividen sucesivamente a través de reglas de decisión que dependen de X .
- ¿Es Analista de Datos una buena carrera?
- Ingeniería de datos moderna con MAGE Potenciando el procesamiento e...
- Análisis de múltiples grupos en Modelado de Ecuaciones Estructurales

El artículo original lo explica de manera un poco confusa, pero según entiendo, MIA funciona de la siguiente manera: Consideremos una muestra ( Y_1 , X _1),…, (Y_n, X _n), con
X _i=(X_i1,…,X_ip)’.
La división sin valores faltantes consiste simplemente en buscar el valor S como se indica arriba y luego enviar todos los Y_i con X_ij < S al Nodo 1 y todos los Y_i con X_ij ≥ S al Nodo 2. Al calcular el criterio objetivo, como CART, para cada valor S, podemos elegir el mejor. Con valores faltantes, en cambio, hay 3 opciones para cada valor de división candidato S a considerar:
- Usar la regla habitual para todas las observaciones i tal que X_ij está observado y enviar i al Nodo 1 si X_ij está faltante.
- Usar la regla habitual para todas las observaciones i tal que X_ij está observado y enviar i al Nodo 2 si X_ij está faltante.
- Ignorar la regla habitual y simplemente enviar i al Nodo 1 si X_ij está faltante y al Nodo 2 si está observado.
Cuál de estas reglas seguir se decide nuevamente según el criterio en Y_i que utilizamos.

Un pequeño ejemplo
Es importante mencionar en este punto que el paquete drf en CRAN aún no se ha actualizado con la nueva metodología. Habrá un momento en el futuro en el que todo esto esté implementado en un solo paquete en CRAN (!) Sin embargo, por el momento, hay dos versiones:
Si desea utilizar la implementación rápida de drf con valores faltantes (sin intervalos de confianza), puede utilizar la función “drfown” adjunta al final de este artículo. Este código está adaptado de
lorismichel/drf: Distributional Random Forests (Cevid et al., 2020) (github.com)
Por otro lado, si desea intervalos de confianza con sus parámetros, utilice este código (más lento)
drfinference/drf-foo.R en main · JeffNaef/drfinference (github.com)
En particular, drf-foo.R contiene todo lo que necesita en este último caso.
Nos centraremos en el código más lento con intervalos de confianza, como se explica en este artículo, y también consideraremos el mismo ejemplo que en dicho artículo:
set.seed(2)n<-2000beta1<-1beta2<--1.8# Simulación del modeloX<-mvrnorm(n = n, mu=c(0,0), Sigma=matrix(c(1,0.7,0.7,1), nrow=2,ncol=2))u<-rnorm(n=n, sd = sqrt(exp(X[,1])))Y<- matrix(beta1*X[,1] + beta2*X[,2] + u, ncol=1)
Debe tenerse en cuenta que este es un modelo lineal heterocedástico con p=2 y con la varianza del término de error dependiendo de los valores de X_1. Ahora también agregamos valores faltantes a X_1 en un patrón aleatorio faltante (MAR):
prob_na <- 0.3X[, 1] <- ifelse(X[, 2] <= -0.2 & runif(n) < prob_na, NA, X[, 1])
Esto significa que X_1 está faltante con una probabilidad del 0.3, cada vez que X_2 tiene un valor menor que -0.2. Por lo tanto, la probabilidad de que falte X_1 depende de X_2, lo que se conoce como “faltante aleatorio”. Esta es una situación compleja y hay información que se puede obtener al observar el patrón de valores faltantes. Es decir, la falta de datos no es “Faltante Completamente al Azar (MCAR)”, porque la falta de X_1 depende del valor de X_2. Esto, a su vez, significa que la distribución de X_2 de la que extraemos es diferente, condicionada a si X_1 está presente o no. Esto significa en particular que eliminar las filas con valores faltantes podría sesgar gravemente el análisis.
Ahora fijamos x y estimamos la esperanza condicional y la varianza dada X = x, exactamente como en el artículo anterior.
# Elija un x que no esté demasiado lejosx<-matrix(c(1,1),ncol=2)# Elija alpha para los CIalpha<-0.05
Luego también ajustamos DRF y predecimos los pesos para el punto de prueba x (lo que corresponde a predecir la distribución condicional de Y| X = x ):
## Ajustamos el nuevo marco de trabajo de DRFdrf_fit <- drfCI(X=X, Y=Y, min.node.size = 5, splitting.rule='FourierMMD', num.features=10, B=100)## predecimos los pesosDRF = predictdrf(drf_fit, x=x)weights <- DRF$weights[1,]
Ejemplo 1: Expectativa Condicional
Primero estimamos la expectativa condicional de Y| X = x.
# Estimamos la expectativa condicional en x:
condexpest<- sum(weights*Y)
# Usamos la distribución de pesos, ver más abajo
distofcondexpest<-unlist(lapply(DRF$weightsb, function(wb) sum(wb[1,]*Y) ))
# Podemos utilizar lo anterior directamente para construir el intervalo de confianza, o podemos utilizar la aproximación normal.
# Usaremos esta última
varest<-var(distofcondexpest-condexpest)
# Construimos un IC del 95%
lower<-condexpest - qnorm(1-alpha/2)*sqrt(varest)
upper<-condexpest + qnorm(1-alpha/2)*sqrt(varest)
round(c(lower, condexpest, upper),2)
# sin NA: (-1.00, -0.69 -0.37)
# con NA: (-1.15, -0.67, -0.19)
Es notable que los valores obtenidos con NA son muy similares a los obtenidos en el primer análisis sin NA en el artículo anterior. Esto realmente es bastante sorprendente para mí, ya que este mecanismo faltante no es fácil de tratar. Es interesante observar que la varianza estimada del estimador también se duplica, de alrededor de 0.025 sin valores faltantes a aproximadamente 0.06 con valores faltantes.
La verdad se da como:
por lo que tenemos un ligero error, pero los intervalos de confianza contienen la verdad, como deberían.
El resultado es similar para un objetivo más complejo, como la varianza condicional:
# Estimamos la expectativa condicional en x:
condvarest<- sum(weights*Y^2) - condexpest^2
distofcondvarest<-unlist(lapply(DRF$weightsb, function(wb) { sum(wb[1,]*Y^2) - sum(wb[1,]*Y)^2} ))
# Podemos utilizar lo anterior directamente para construir intervalo de confianza, o podemos utilizar la aproximación normal.
# Usaremos esta última
varest<-var(distofcondvarest-condvarest)
# Construimos un IC del 95%
lower<-condvarest - qnorm(1-alpha/2)*sqrt(varest)
upper<-condvarest + qnorm(1-alpha/2)*sqrt(varest)
c(lower, condvarest, upper)
# sin NA: (1.89, 2.65, 3.42)
# con NA: (1.79, 2.74, 3.69)
Aquí, la diferencia en los valores estimados es un poco mayor. Como la verdad se da como
la estimación con NA es incluso ligeramente más precisa (aunque, por supuesto, esto probablemente se debe a la aleatoriedad). Nuevamente, la estimación de la varianza del estimador (varianza) aumenta con los valores faltantes, de 0.15 (sin valores faltantes) a 0.23.
Conclusión
En este artículo, discutimos MIA, que es una adaptación del método de división en Random Forest para tratar valores faltantes. Dado que está implementado en GRF y DRF, puede usarse ampliamente y el pequeño ejemplo que hemos visto indica que funciona notablemente bien.
Sin embargo, me gustaría señalar de nuevo que no hay garantía teórica de consistencia o de que los intervalos de confianza tengan sentido, incluso para un número muy grande de puntos de datos. Las razones de los valores faltantes son numerosas y uno debe tener mucho cuidado de no sesgar su análisis a través de un manejo descuidado de este problema. El método MIA no es de ninguna manera una solución bien entendida para este problema. Sin embargo, parece ser una solución rápida razonable por el momento, que parece ser capaz de hacer uso del patrón de los valores faltantes en los datos. Si alguien hace o tiene un análisis de simulación más extenso, estaría interesado en conocer los resultados.
Código
require(drf) drfown <- function(X, Y, num.trees = 500, splitting.rule = "FourierMMD", num.features = 10, bandwidth = NULL, response.scaling = TRUE, node.scaling = FALSE, sample.weights = NULL, sample.fraction = 0.5, mtry = min(ceiling(sqrt(ncol(X)) + 20), ncol(X)), min.node.size = 15, honesty = TRUE, honesty.fraction = 0.5, honesty.prune.leaves = TRUE, alpha = 0.05, imbalance.penalty = 0, compute.oob.predictions = TRUE, num.threads = NULL, seed = stats::runif(1, 0, .Machine$integer.max), compute.variable.importance = FALSE) { # comprobaciones iniciales de X e Y
if (is.data.frame(X)) {
if (is.null(names(X))) {
stop("el regresor debe ser nombrado si se proporciona en formato data.frame.")
}
if (any(apply(X, 2, class) %in% c("factor", "character"))) {
any.factor.or.character <- TRUE X.mat <- as.matrix(fastDummies::dummy_cols(X, remove_selected_columns = TRUE))
} else {
any.factor.or.character <- FALSE X.mat <- as.matrix(X)
}
mat.col.names.df <- names(X)
mat.col.names <- colnames(X.mat)
} else {
X.mat <- X
mat.col.names <- NULL
mat.col.names.df <- NULL
any.factor.or.character <- FALSE
}
if (is.data.frame(Y)) {
if (any(apply(Y, 2, class) %in% c("factor", "character"))) {
stop("Y debe contener solo variables numéricas.")
}
Y <- as.matrix(Y)
}
if (is.vector(Y)) {
Y <- matrix(Y,ncol=1)
}
#validar_X(X.mat)
if (inherits(X, "Matrix") && !(inherits(X, "dgCMatrix"))) {
stop("Actualmente solo se admite datos dispersos de clase 'dgCMatrix'.")
}
drf:::validate_sample_weights(sample.weights, X.mat)
#Y <- validate_observations(Y, X)
# establecer parámetros GRF heredados
clusters <- vector(mode = "numeric", length = 0)
samples.per.cluster <- 0
equalize.cluster.weights <- FALSE
ci.group.size <- 1
num.threads <- drf:::validate_num_threads(num.threads)
Citas
[1] Twala, B. E. T. H., M. C. Jones, y David J. Hand. Buenos métodos para lidiar con datos faltantes en árboles de decisión. Pattern Recognition Letters 29, 2008.