Un esempio: regressione di LASSO utilizzando glmnet per il risultato binario


78

Sto iniziando a dilettarsi con l'uso di glmnetcon LASSO Regressione dove il mio risultato di interesse è dicotomica. Di seguito ho creato un piccolo frame di dati finti:

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)

Le colonne (variabili) nel set di dati sopra sono le seguenti:

  • age (età del bambino in anni) - continua
  • gender - binario (1 = maschio; 0 = femmina)
  • bmi_p (BMI percentile) - continuo
  • m_edu (livello di istruzione superiore della madre) - ordinale (0 = inferiore alla scuola superiore; 1 = diploma di scuola superiore; 2 = diploma di laurea; 3 = diploma post-diploma di maturità)
  • p_edu (livello di istruzione superiore del padre) - ordinale (uguale a m_edu)
  • f_color (colore primario preferito) - nominale ("blu", "rosso" o "giallo")
  • asthma (stato dell'asma infantile) - binario (1 = asma; 0 = nessun asma)

L'obiettivo di questo esempio è di fare uso di LASSO per creare un modello di predire lo stato del bambino asma dalla lista delle 6 variabili potenziali predittori ( age, gender, bmi_p, m_edu, p_edu, e f_color). Ovviamente la dimensione del campione è un problema qui, ma spero di ottenere maggiori informazioni su come gestire i diversi tipi di variabili (cioè, continuo, ordinale, nominale e binario) all'interno del glmnetframework quando il risultato è binario (1 = asma ; 0 = nessun asma).

Come tale, qualcuno sarebbe disposto a fornire uno Rscript di esempio insieme alle spiegazioni per questo esempio falso usando LASSO con i dati sopra per prevedere lo stato dell'asma? Anche se molto semplice, lo so, e probabilmente molti altri su CV, lo apprezzerei molto!


2
Potresti avere più fortuna se hai pubblicato i dati come dputdi un oggetto R reale ; non fare in modo che i lettori mettano la glassa sopra e ti cuociano una torta !. Se generi il frame di dati appropriato in R, ad esempio foo, modifica nella domanda l'output di dput(foo).
Gavin Simpson,

Grazie @GavinSimpson! Ho aggiornato il post con un frame di dati, quindi spero di poter mangiare un po 'di torta senza glassare! :)
Matt Reichenbach l'

2
Usando il percentile BMI stai in un certo senso sfidando le leggi della fisica. L'obesità colpisce gli individui in base a misurazioni fisiche (lunghezze, volumi, peso) e non in base a quanti individui sono simili al soggetto attuale, che è ciò che sta facendo il percentiling.
Frank Harrell,

3
Sono d'accordo, il percentile di BMI non è una metrica che preferisco usare; tuttavia, le linee guida del CDC raccomandano l'uso del percentile dell'IMC rispetto all'IMC (anche una metrica altamente discutibile!) per bambini e adolescenti di età inferiore ai 20 anni, poiché tiene conto dell'età e del sesso oltre all'altezza e al peso. Tutte queste variabili e valori di dati sono stati pensati interamente per questo esempio. Questo esempio non riflette nessuno dei miei lavori attuali mentre lavoro con i big data. Volevo solo vedere un esempio di glmnetin azione con un risultato binario.
Matt Reichenbach,

Inserisci qui un pacchetto di Patrick Breheny chiamato ncvreg che si adatta ai modelli di regressione lineare e logistica penalizzati da MCP, SCAD o LASSO. ( cran.r-project.org/web/packages/ncvreg/index.html )
bdeonovic

Risposte:


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

inserisci qui la descrizione dell'immagine

Le variabili categoriche di solito vengono prima trasformate in fattori, quindi viene creata una matrice variabile fittizia di predittori e insieme ai predittori continui viene passata al modello. Tieni presente che glmnet utilizza entrambe le penalità di cresta e lazo, ma può essere impostato su uno solo.

Alcuni risultati:

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

I coefficienti possono essere estratti dal glmmod. Qui mostrato con 3 variabili selezionate.

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 

Infine, la validazione incrociata può anche essere usata per selezionare lambda.

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

inserisci qui la descrizione dell'immagine

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

4
questo è esattamente quello che stavo cercando +1, le uniche domande che ho sono 1) cosa puoi fare con la validazione incrociata lambda di 0.2732972? e 2) Dal glmmod, le variabili selezionate sono il colore preferito (giallo), il genere e l'educazione del padre (laurea)? Grazie mille!
Matt Reichenbach,

4
1) La validazione incrociata viene utilizzata per scegliere lambda e coefficienti (con errore minimo). In questo mockup, non esiste un min locale (c'era anche un avvertimento relativo a troppo pochi obs); Interpreterei che tutti i coefficienti erano ridotti a zero con le penalità di restringimento (il modello migliore ha solo intercettato) e ripetuto con più (reali) osservazioni e forse avrebbe aumentato l'intervallo lambda. 2) Sì, nell'esempio in cui ho scelto coef (glmmod) [, 10] ... scegli lambda per il modello tramite CV o interpretazione dei risultati. Potresti contrassegnarlo come risolto se ritieni che io abbia risolto la tua domanda? Grazie.
pat

2
Posso chiederti come gestisce la f_colorvariabile? I livelli di fattore da 1 a 4 sono considerati un passo più grande di 1 a 2 o sono tutti ugualmente ponderati, non direzionali e categorici? (Voglio applicarlo a un'analisi con tutti i predittori non ordinati.)
beroe

3
La riga xfactors <- model.matrix(asthma ~ gender + m_edu + p_edu + f_color)[,-1]codifica la variabile categoriale f_color (come dichiarato as.factornelle righe precedenti). Dovrebbe utilizzare la codifica variabile fittizia R predefinita, a meno che non contrasts.argvenga fornito l' argomento. Ciò significa che tutti i livelli di f_color sono equamente ponderati e non direzionali, ad eccezione del primo che viene utilizzato come classe di riferimento e assorbito nell'intercetta.
Alex,

1
@Alex non model.matrix(asthma ~ gender + m_edu + p_edu + f_color + age + bmi_p)[, -1]darebbe lo stesso risultato delle due righe sopra? Perché utilizzare un passaggio aggiuntivo per concatenare le variabili continue con data.frame?
jiggunjer,

6

Userò il pacchetto enet poiché quello è il mio metodo preferito. È un po 'più flessibile.

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)

4
grazie per la condivisione elasticnet; tuttavia, non so cosa fare dell'output dallo Rscript precedente . Potete per favore chiarire? Grazie in anticipo!
Matt Reichenbach,

4

Solo per espandere l'eccellente esempio fornito da pat. Il problema originale presentava variabili ordinali (m_edu, p_edu), con un ordine intrinseco tra i livelli (0 <1 <2 <3). Nella risposta originale di Pat penso che queste siano state trattate come variabili nominali categoriche senza alcun ordine tra loro. Potrei sbagliarmi, ma credo che queste variabili debbano essere codificate in modo tale che il modello rispetti il ​​loro ordine intrinseco. Se questi sono codificati come fattori ordinati (piuttosto che come fattori non ordinati come nella risposta di pat) allora glmnet dà risultati leggermente diversi ... Penso che il codice qui sotto includa correttamente le variabili ordinali come fattori ordinati, e dà risultati leggermente diversi:

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

inserisci qui la descrizione dell'immagine


1
sometimes_sci, buona cattura - questo sarebbe il modo più appropriato per modellare le variabili del livello di istruzione. Grazie per il tuo contributo.
Matt Reichenbach,

come si aggiungerebbe una legenda della trama per le variabili? Ad esempio qual è la linea rossa in questo esempio?
jiggunjer,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.