Un ejemplo: regresión LASSO usando glmnet para el resultado binario

78

Estoy empezando a incursionar con el uso de glmnetla LASSO regresión donde mi resultado de interés es dicotómica. He creado un pequeño marco de datos simulados 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) en el conjunto de datos anterior son las siguientes:

  • age (edad del niño en años) - continuo
  • gender - binario (1 = masculino; 0 = femenino)
  • bmi_p (Percentil de IMC) - continuo
  • m_edu (nivel de educación más alto de la madre) - ordinal (0 = menos que la escuela secundaria; 1 = diploma de escuela secundaria; 2 = título de bachiller; 3 = título de posgrado)
  • p_edu (nivel de educación más alto del padre) - ordinal (igual que m_edu)
  • f_color (color primario favorito) - nominal ("azul", "rojo" o "amarillo")
  • asthma (estado de asma infantil) - binario (1 = asma; 0 = sin asma)

El objetivo de este ejemplo es hacer uso de LASSO para crear un modelo de predicción de la condición de asma infantil de la lista de 6 posibles variables de predicción ( age, gender, bmi_p, m_edu, p_edu, y f_color). Obviamente, el tamaño de la muestra es un problema aquí, pero espero obtener más información sobre cómo manejar los diferentes tipos de variables (es decir, continua, ordinal, nominal y binaria) dentro del glmnetmarco cuando el resultado es binario (1 = asma ; 0 = sin asma).

Como tal, ¿estaría alguien dispuesto a proporcionar un Rscript de muestra junto con explicaciones para este ejemplo simulado usando LASSO con los datos anteriores para predecir el estado del asma? Aunque es muy básico, lo sé, y probablemente muchos otros en CV, ¡lo agradecería enormemente!

Matt Reichenbach
fuente
2
Puede que tenga más suerte si usted envió los datos como dputde un real objeto R; ¡no hagas que los lectores pongan glaseado encima ni te horneen un pastel! Si genera el marco de datos apropiado en R, por ejemplo foo, edite en la pregunta la salida de dput(foo).
Gavin Simpson
Gracias @GavinSimpson! Actualicé la publicación con un marco de datos, ¡así que espero poder comer un poco de pastel sin glaseado! :)
Matt Reichenbach
2
Al usar el percentil de IMC, en cierto sentido, está desafiando las leyes de la física. La obesidad afecta a los individuos según las medidas físicas (longitudes, volúmenes, peso), no según cuántos individuos son similares al sujeto actual, que es lo que está haciendo el percentil.
Frank Harrell
3
Estoy de acuerdo, el percentil de IMC no es una métrica que prefiero usar; sin embargo, las pautas de los CDC recomiendan usar el percentil de IMC sobre el IMC (¡también una métrica muy cuestionable!) para niños y adolescentes menores de 20 años, ya que tiene en cuenta la edad y el género además de la altura y el peso. Todas estas variables y valores de datos fueron pensados ​​completamente para este ejemplo. Este ejemplo no refleja ninguno de mis trabajos actuales, ya que trabajo con big data. Solo quería ver un ejemplo de glmneten acción con un resultado binario.
Matt Reichenbach
Conecte aquí un paquete de Patrick Breheny llamado ncvreg que se ajusta a modelos de regresión lineal y logística penalizados por MCP, SCAD o LASSO. ( cran.r-project.org/web/packages/ncvreg/index.html )
bdeonovic

Respuestas:

101
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")

ingrese la descripción de la imagen aquí

Las variables categóricas generalmente se transforman primero en factores, luego se crea una matriz variable variable de predictores y, junto con los predictores continuos, se pasa al modelo. Tenga en cuenta que glmnet usa penalizaciones de cresta y lazo, pero puede configurarse para que sea solo.

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 se pueden extraer 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 se puede utilizar para seleccionar lambda.

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

ingrese la descripción de la imagen aquí

(best.lambda <- cv.glmmod$lambda.min)
# [1] 0.2732972
palmadita
fuente
44
esto es exactamente lo que estaba buscando +1, las únicas preguntas que tengo son 1) ¿qué puede hacer con la validación cruzada lambda de 0.2732972? y 2) Desde el glmmod, ¿son las variables seleccionadas el color favorito (amarillo), el género y la educación del padre (licenciatura)? ¡Muchas gracias!
Matt Reichenbach
44
1) La validación cruzada se utiliza para elegir lambda y coeficientes (con un error mínimo). En esta maqueta, no hay min local (también hubo una advertencia relacionada con muy pocos obs); Interpretaría que todos los coeficientes se redujeron a cero con las penalizaciones por contracción (el mejor modelo solo intercepta) y volvería a ejecutar con más observaciones (reales) y tal vez aumente el rango lambda. 2) Sí, en el ejemplo donde elegí coef (glmmod) [, 10] ... eliges lambda para el modelo a través de CV o interpretación de resultados. ¿Podría marcar como resuelto si considera que resuelvo su pregunta? Gracias.
Pat
2
¿Puedo preguntar cómo maneja esto la f_colorvariable? ¿Se considera que el nivel de factor 1 a 4 es un paso mayor que 1 a 2, o son todos igualmente ponderados, no direccionales y categóricos? (Quiero aplicarlo a un análisis con todos los predictores desordenados)
Beroe
3
La línea xfactors <- model.matrix(asthma ~ gender + m_edu + p_edu + f_color)[,-1]codifica la variable categórica f_color (como se declara as.factoren las líneas anteriores). Debe usar la codificación de variable ficticia R predeterminada, a menos contrasts.argque se proporcione el argumento. Esto significa que todos los niveles de f_color son igualmente ponderados y no direccionales, excepto el primero que se utiliza como clase de referencia y se absorbe en la intersección.
Alex
1
¿@Alex no model.matrix(asthma ~ gender + m_edu + p_edu + f_color + age + bmi_p)[, -1]daría el mismo resultado que las dos líneas de arriba? ¿Por qué usar un paso adicional para concatenar las variables continuas con data.frame?
jiggunjer
6

Usaré el paquete enet ya que ese 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)
bdeonovic
fuente
44
gracias por compartir elasticnet; sin embargo, no sé qué hacer con el resultado del Rscript anterior . ¿Puedes por favor aclarar? ¡Gracias por adelantado!
Matt Reichenbach
4

Solo 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 estas fueron tratadas como variables categóricas nominales sin ningún orden entre ellas. Puedo estar equivocado, pero creo que estas variables deberían codificarse de tal manera que el modelo respete su orden inherente. Si se codifican como factores ordenados (en lugar de factores desordenados como en la respuesta de pat), glmnet da resultados ligeramente diferentes ... Creo que el siguiente código incluye correctamente las variables ordinales como factores ordenados, y proporciona 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")

ingrese la descripción de la imagen aquí

a veces_sci
fuente
1
sometimes_sci, buena captura: esta sería la forma más adecuada de modelar las variables de nivel educativo. Gracias por tu contribución.
Matt Reichenbach
¿Cómo agregaría una leyenda de la trama para las variables? Por ejemplo, ¿cuál es la línea roja en este ejemplo?
jiggunjer