π― Trigger: many predictors / transformations, or small n β cv.glmnet.f(y ~ β¦, df, family = "binomial") β coefficients(fit, s = "lambda.min") β predict.glmnet.f(fit, test, type = "response", s = "lambda.min").
β οΈ Key Constraint:glmnet takes a matrix, not a formula β use the .f wrappers; alpha = 1 (default) is lasso, alpha = 0 is ridge; forget s = "lambda.min" and you get the whole path.
π§ Minimal Working Example
library(glmnet); library(pROC) # install.packages("glmnet") oncesource("wrappers.R") # glmnet.f, cv.glmnet.f, predict.glmnet.f, my.make.formulasource("my.prediction.stats.R") # my.pred.stats(prob, target)pima.train = read.csv("pima.train.csv", stringsAsFactors = T) # 668 x 9pima.test = read.csv("pima.test.csv", stringsAsFactors = T) # 100 x 9# lambda = 0: no penalty = plain ML, matches glm()fit0 = glmnet.f(DIABETES ~ ., pima.train, family = "binomial", lambda = 0) # "binomial" IN QUOTES# hand-picked lambda: how sparse?fit = glmnet.f(DIABETES ~ ., pima.train, family = "binomial", lambda = 0.05)coefficients(fit) # "." = exactly zerosum(coefficients(fit)[-1, ] != 0) # non-zero predictors, intercept excluded -> 4# whole path, then CV for lambdapath = glmnet.f(DIABETES ~ ., pima.train, family = "binomial")plot(path, "lambda", label = T) # coefficient paths vs log(lambda)set.seed(1)cvfit = cv.glmnet.f(DIABETES ~ ., pima.train, family = "binomial") # 10 foldsplot(cvfit); min(cvfit$cvm); cvfit$lambda.mincoefficients(cvfit, s = "lambda.min")my.pred.stats(predict.glmnet.f(cvfit, pima.test, type = "response", s = "lambda.min"), pima.test$DIABETES)# ridge: same calls + alpha = 0ridge = cv.glmnet.f(DIABETES ~ ., pima.train, family = "binomial", alpha = 0)
Expected output (rerun on the studio files; CV rows use set.seed(1)):
Ξ» (lasso)
Non-zero of 8
Dropped
0.02
7
INS
0.03
6
BP, INS
0.05
4 (PREG, PLAS, BMI, AGE)
BP, SKIN, INS, PED
Model on Pima (n=668)
Terms
CA
Sens
Spec
AUC
Log-loss
lasso Ξ»=0 (=glm)
8
0.77
0.516
0.884
0.816
49.58
lasso CV, lambda.min=0.0027
8 (all, shrunk)
0.77
0.516
0.884
0.814
49.28
BIC stepwise
4 (PREG, PLAS, BMI, PED)
0.77
0.516
0.884
0.823
47.70
ridge CV, lambda.min=0.0236
8
0.76
0.484
0.884
0.810
50.43
Path β the default grid stopped at 60Ξ» values (0.00097β0.236), not 100 β glmnet ends early once the deviance stops changing.
Lasso vs BIC β with n=668 and only 8 predictors, CV barely penalises (all kept); BICβs 4-term model predicts slightly better and is far easier to read.
Ridge path β Ξ»=0.05β0.15β0.5: every coefficient shrinks smoothly (PED 0.633β0.472β0.278) and none reaches zero β unlike lasso at 0.05, which zeroed four.
Seed check β over seeds 1β5, lambda.min ranged 0.0014β0.0036 and always kept all 8 β the CV choice moves, the conclusion doesnβt.
π Variations
All transformations β f = my.make.formula("DIABETES", pima.train, use.interactions = T, use.logs = T, use.squares = T, use.cubics = T) β . + .*. + log() of each strictly positive predictor + I(x^2) + I(x^3) βΉ 59 columns (PREG has zeros βΉ no log).
n=668, 59 columns
Terms kept
Fit time
CA
AUC
Log-loss
lasso CV
17
2.2 s
0.77
0.840
45.78
BIC stepwise (both)
6
20.8 s
0.80
0.852
44.54
Reading β lasso is ~10Γ faster; with plenty of data it does not beat stepwise, but is not much worse.
Swap train/test β pima.train = read.csv("pima.test.csv", β¦) βΉ n=100 to fit, 668 to test, p=59:
n=100 model
Terms
CA
AUC
Log-loss
glm, 8 raw predictors
8
0.75
0.761
452.7
glm, all 59
59
0.62
0.618
5329.2
backward BIC from 59
18
0.67
0.623
5034.8
lasso CV (min CV dev. 0.910)
8
0.75
0.789
371.4
ridge CV, same folds (min CV dev. 0.945)
59
0.75
0.795
357.6
Reading β 59 terms on 100 rows overfits catastrophically; stepwise barely rescues it; lasso and ridge degrade gracefully and beat even the raw 8-predictor glm β the lassoβs real strength is small n relative to p. CV preferred lasso (0.910<0.945); the test set slightly favoured ridge β CV is an estimate, not a guarantee.
Same folds for a fair CV comparison β foldid = sample(rep(1:10, length.out = nrow(df))), then pass foldid = foldid to both cv.glmnet.f calls.
Continuous target β omit family (Gaussian default) β lasso/ridge linear regression.
βοΈ Practice
Practice 1: Fit a CV ridge logistic model of y on all predictors in df, print only the non-zero coefficients at the chosen Ξ», and the test AUC on newdf.
Reference solution
fit = cv.glmnet.f(y ~ ., df, family = "binomial", alpha = 0)b = coefficients(fit, s = "lambda.min"); b[b[, 1] != 0, , drop = F] # ridge: all of themp = predict.glmnet.f(fit, newdf, type = "response", s = "lambda.min")roc(response = newdf$y, as.numeric(p))$auc
Key move:alpha = 0 for ridge; s = "lambda.min" in bothcoefficients and predict.
Practice 2: Compare lasso and ridge fairly by minimum CV error on the full transformation formula.
Key move: identical foldid βΉ the CV difference reflects the penalty, not the random split.
β οΈ Common Mistakes
π‘ family = binomial unquoted β glm takes the object, glmnet needs the string"binomial".
π‘ Calling glmnet with a formula β it only accepts X, y; the wrappers build model.matrix and drop the intercept column for you.
π‘ Counting the intercept as a predictor β coefficients(fit)[-1, ] != 0 excludes row 1.
π‘ roc() warning βDeprecated use a matrix as predictorβ β harmless β predict.glmnet.f returns a 1-column matrix; wrap in as.numeric() to silence it.