90 votos

Un ejemplo: Regresión LASSO con glmnet para un resultado binario

Estoy empezando a incursionar en el uso de glmnet con Regresión LASSO donde mi resultado de interés es dicotómico. He creado un pequeño marco de datos simulado a continuación:

age     <- c(4, 8, 7, 12, 6, 9, 10, 14, 7) 
gender  <- c(1, 0, 1, 1, 1, 0, 1, 0, 0)
bmi_p   <- c(0.86, 0.45, 0.99, 0.84, 0.85, 0.67, 0.91, 0.29, 0.88)
m_edu   <- c(0, 1, 1, 2, 2, 3, 2, 0, 1)
p_edu   <- c(0, 2, 2, 2, 2, 3, 2, 0, 0)
f_color <- c("blue", "blue", "yellow", "red", "red", "yellow", "yellow", 
             "red", "yellow")
asthma  <- c(1, 1, 0, 1, 0, 0, 0, 1, 1)
# df is a data frame for further use!
df <- data.frame(age, gender, bmi_p, m_edu, p_edu, f_color, asthma)

Las columnas (variables) del conjunto de datos anterior son las siguientes:

  • age (edad del niño en años) - continuo
  • gender - binario (1 = hombre; 0 = mujer)
  • bmi_p (percentil del IMC) - continuo
  • m_edu (nivel de estudios más alto de la madre) - ordinal (0 = menos de estudios secundarios; 1 = diploma de estudios secundarios; 2 = licenciatura; 3 = título de postgrado)
  • p_edu (nivel educativo más alto del padre) - ordinal (igual que m_edu)
  • f_color (color primario favorito) - nominal ("azul", "rojo" o "amarillo")
  • asthma (estado del asma del niño) - binario (1 = asma; 0 = sin asma)

El objetivo de este ejemplo es hacer uso de LASSO para crear un modelo de predicción del estado del asma infantil a partir de la lista de 6 posibles variables predictoras ( age , gender , bmi_p , m_edu , p_edu y f_color ). Obviamente, el tamaño de la muestra es un problema, pero espero obtener más información sobre cómo manejar los diferentes tipos de variables (es decir, continuas, ordinales, nominales y binarias) dentro del glmnet cuando el resultado es binario (1 = asma; 0 = sin asma).

Como tal, ¿alguien estaría dispuesto a proporcionar una muestra R script junto con las explicaciones de este ejemplo simulado utilizando LASSO con los datos anteriores para predecir el estado del asma? ¡Aunque es muy básico, sé que yo, y probablemente muchos otros en CV, lo apreciaría mucho!

2 votos

Puede que tengas más suerte si publicas los datos como dput de un actual objeto de R; ¡no haga que los lectores le pongan glaseado encima además de hornearle un pastel! Si genera el marco de datos apropiado en R, digamos foo y luego editar en la pregunta la salida de dput(foo) .

0 votos

¡Gracias @GavinSimpson! ¡He actualizado el post con un marco de datos, así que espero poder comer algo de pastel sin glaseado! :)

2 votos

Al utilizar el percentil del IMC, se desafían en cierto modo las leyes de la física. La obesidad afecta a los individuos en función de las medidas físicas (longitudes, volúmenes, peso) y no en función de cuántos individuos son similares al sujeto actual, que es lo que hace el percentil.

124voto

Tiberia Puntos 121
library(glmnet)

age     <- c(4, 8, 7, 12, 6, 9, 10, 14, 7) 
gender  <- as.factor(c(1, 0, 1, 1, 1, 0, 1, 0, 0))
bmi_p   <- c(0.86, 0.45, 0.99, 0.84, 0.85, 0.67, 0.91, 0.29, 0.88) 
m_edu   <- as.factor(c(0, 1, 1, 2, 2, 3, 2, 0, 1))
p_edu   <- as.factor(c(0, 2, 2, 2, 2, 3, 2, 0, 0))
f_color <- as.factor(c("blue", "blue", "yellow", "red", "red", "yellow", 
                       "yellow", "red", "yellow"))
asthma <- c(1, 1, 0, 1, 0, 0, 0, 1, 1)

xfactors <- model.matrix(asthma ~ gender + m_edu + p_edu + f_color)[, -1]
x        <- as.matrix(data.frame(age, bmi_p, xfactors))

# Note alpha=1 for lasso only and can blend with ridge penalty down to
# alpha=0 ridge only.
glmmod <- glmnet(x, y=as.factor(asthma), alpha=1, family="binomial")

# Plot variable coefficients vs. shrinkage parameter lambda.
plot(glmmod, xvar="lambda")

enter image description here

Las variables categóricas suelen transformarse primero en factores, luego se crea una matriz de variables ficticias de predictores y, junto con los predictores continuos, se pasa al modelo. Tenga en cuenta que glmnet utiliza tanto las penalizaciones de cresta como las de lazo, pero puede establecerse cualquiera de ellas por separado.

Algunos resultados:

# Model shown for lambda up to first 3 selected variables.
# Lambda can have manual tuning grid for wider range.

glmmod
# Call:  glmnet(x = x, y = as.factor(asthma), family = "binomial", alpha = 1) 
# 
#        Df    %Dev   Lambda
#   [1,]  0 0.00000 0.273300
#   [2,]  1 0.01955 0.260900
#   [3,]  1 0.03737 0.249000
#   [4,]  1 0.05362 0.237700
#   [5,]  1 0.06847 0.226900
#   [6,]  1 0.08204 0.216600
#   [7,]  1 0.09445 0.206700
#   [8,]  1 0.10580 0.197300
#   [9,]  1 0.11620 0.188400
#  [10,]  3 0.13120 0.179800
#  [11,]  3 0.15390 0.171600
# ...

Los coeficientes pueden extraerse del glmmod. Aquí se muestra con 3 variables seleccionadas.

coef(glmmod)[, 10]
#   (Intercept)           age         bmi_p       gender1        m_edu1 
#    0.59445647    0.00000000    0.00000000   -0.01893607    0.00000000 
#        m_edu2        m_edu3        p_edu2        p_edu3    f_colorred 
#    0.00000000    0.00000000   -0.01882883    0.00000000    0.00000000 
# f_coloryellow 
#   -0.77207831 

Por último, la validación cruzada también puede utilizarse para seleccionar lambda.

cv.glmmod <- cv.glmnet(x, y=asthma, alpha=1)
plot(cv.glmmod)

enter image description here

(best.lambda <- cv.glmmod$lambda.min)
# [1] 0.2732972

4 votos

Esto es exactamente lo que estaba buscando +1, las únicas preguntas que tengo son 1) ¿qué se puede hacer con la lambda de validación cruzada de 0,2732972? y 2) desde el glmmod, ¿las variables seleccionadas son el color favorito (amarillo), el género y la educación del padre (licenciatura)? ¡Muchas gracias!

4 votos

1) La validación cruzada se utiliza para elegir lambda y los coeficientes (con un error mínimo). En esta maqueta, no hay un mínimo local (también había una advertencia relacionada con muy pocas observaciones); yo interpretaría que todos los coeficientes se redujeron a cero con las penalizaciones por contracción (el mejor modelo sólo tiene intercepción) y volver a ejecutar con más observaciones (reales) y tal vez aumentar el rango de lambda. 2) Sí, en el ejemplo en el que elegí coef(glmmod)[,10]... se elige lambda para el modelo a través del CV o la interpretación de los resultados. ¿Podría marcar como resuelto si considera que he resuelto su pregunta? gracias.

2 votos

¿Puedo preguntar cómo se maneja el f_color ¿variable? ¿Se considera que el nivel del factor 1 al 4 es un paso mayor que el 1 al 2, o son todos igualmente ponderados, no direccionales y categóricos? (Quiero aplicarlo a un análisis con todos los predictores no ordenados).

6voto

bdeonovic Puntos 2807

Utilizaré el paquete enet ya que es mi método preferido. Es un poco más flexible.

install.packages('elasticnet')
library(elasticnet)

age <- c(4,8,7,12,6,9,10,14,7) 
gender <- c(1,0,1,1,1,0,1,0,0)
bmi_p <- c(0.86,0.45,0.99,0.84,0.85,0.67,0.91,0.29,0.88)
m_edu <- c(0,1,1,2,2,3,2,0,1)
p_edu <- c(0,2,2,2,2,3,2,0,0)
#f_color <- c("blue", "blue", "yellow", "red", "red", "yellow", "yellow", "red", "yellow")
f_color <- c(0, 0, 1, 2, 2, 1, 1, 2, 1)
asthma <- c(1,1,0,1,0,0,0,1,1)
pred <- cbind(age, gender, bmi_p, m_edu, p_edu, f_color)

enet(x=pred, y=asthma, lambda=0)

5 votos

Gracias por compartirlo elasticnet Sin embargo, no sé qué hacer con el resultado de lo anterior R script. ¿Puedes aclararlo, por favor? ¡Gracias de antemano!

5voto

CSPea Puntos 66

Sólo para ampliar el excelente ejemplo proporcionado por pat. El problema original planteaba variables ordinales (m_edu, p_edu), con un orden inherente entre niveles (0 < 1 < 2 < 3). En la respuesta original de pat creo que se trataron como variables categóricas nominales sin orden entre ellas. Puede que me equivoque, pero creo que estas variables deben codificarse de forma que el modelo respete su orden inherente. Si se codifican como factores ordenados (en lugar de factores no ordenados como en la respuesta de Pat), glmnet da resultados ligeramente diferentes... Creo que el código siguiente incluye correctamente las variables ordinales como factores ordenados, y da resultados ligeramente diferentes:

library(glmnet)

age     <- c(4, 8, 7, 12, 6, 9, 10, 14, 7) 
gender  <- as.factor(c(1, 0, 1, 1, 1, 0, 1, 0, 0))
bmi_p   <- c(0.86, 0.45, 0.99, 0.84, 0.85, 0.67, 0.91, 0.29, 0.88) 
m_edu   <- factor(c(0, 1, 1, 2, 2, 3, 2, 0, 1), 
                  ordered = TRUE)
p_edu   <- factor(c(0, 2, 2, 2, 2, 3, 2, 0, 0), 
                  levels = c(0, 1, 2, 3), 
                  ordered = TRUE)
f_color <- as.factor(c("blue", "blue", "yellow", "red", "red", 
                       "yellow", "yellow", "red", "yellow"))
asthma <- c(1, 1, 0, 1, 0, 0, 0, 1, 1)

xfactors <- model.matrix(asthma ~ gender + m_edu + p_edu + f_color)[, -1]
x        <- as.matrix(data.frame(age, bmi_p, xfactors))

# Note alpha=1 for lasso only and can blend with ridge penalty down to
# alpha=0 ridge only.
glmmod <- glmnet(x, y=as.factor(asthma), alpha=1, family="binomial")

# Plot variable coefficients vs. shrinkage parameter lambda.
plot(glmmod, xvar="lambda")

enter image description here

1 votos

Sometimes_sci, good catch - this would be the more appropriate way to model the education level variables. Gracias por tu aportación.

0 votos

¿Cómo se puede añadir una leyenda gráfica para las variables? Por ejemplo, ¿qué es la línea roja en este ejemplo?

0 votos

Se utilizaría la función de leyenda, como legend("bottomright", lwd = 1, col = 1:11, legend = colnames(x), cex = .7).

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X