rplotglmnet

How to select a single graph from multiple plot output


I'm attemtping to select a single grpah from multiple plot output with "glmnet" function (especially elastic net) in R.

I'm currently dealing with multinomial case; the response variable has 3 or more categories. So I fit the model with explanatory variable X and the response variable y with 3 types of categories. Assume that we already have alpha and lambda values from cross validation using cv.glmnet. I didn't use the argument type.multinomial = "grouped".

fit <- glmnet(X, y, alpha = alpha, lambda = lambda, standardize = TRUE, family = "multinomial")
plot(fit, xvar = "lambda")

fit is then the elastic net model. I know that plot(fit, xvar = "lambda") shows the coefficients path something like this:

Elastic net coefficients

However, when it is multinomial case, R generates multiple coefficient plots at the same time: for example when y has three categories, then R shows 3 coefficient plots. But I need to select only one graph out of them. I tried to find a way in google, but failed. Is there any way that I can try?


Solution

  • We could modify the glmnet:::plot.multnet method for this and add a which= option similar to that in stats:::plot.lm.

    plot.multnet <- function(x, xvar=c("norm", "lambda", "dev"), label=FALSE, 
              type.coef=c("coef", "2norm"), which=x$classnames, ...) {
      xvar <- match.arg(xvar)
      type.coef <- match.arg(type.coef)
      if (!all(which %in% x$classnames)) {
        warning(sprintf('`which="%s"` not in classnames, defaulting to all', which))
        which <- x$classnames
      }
      beta <- x$beta
      if (xvar == "norm") {
        cnorm1 <- function(beta) {
          whichnz <- nonzeroCoef(beta)
          beta <- as.matrix(beta[whichnz, ])
          apply(abs(beta), 2, sum)
        }
        norm <- apply(sapply(x$beta, cnorm1), 1, sum)
      } else {
        norm <- NULL
      }
      dfmat <- x$dfmat
      if (type.coef == "coef") {
        ncl <- nrow(dfmat)
        clnames <- rownames(dfmat)
        lapply(which, function(z) glmnet:::plotCoef(beta[[z]], norm, x$lambda, dfmat[z, ], x$dev.ratio, 
                                    label=label, xvar=xvar, ylab=paste("Coefficients: Response", 
                                                                       grep(z, clnames, value=TRUE))))
      } else {
        dfseq <- round(apply(dfmat, 2, mean), 1)
        glmnet:::plotCoef(coefnorm(beta, 2), norm, x$lambda, dfseq, x$dev.ratio, 
                 label=label, xvar=xvar, ylab="Coefficient 2Norms", 
                 ...)
      }
    }
    

    Usage

    fit1 <- glmnet::glmnet(x, y, family="multinomial")
    
    plot(fit1, xvar='lambda', which='c')
    

    enter image description here

    I implemented a warning if a certain layer is not present, then by default all layers are plotted as before.

    plot(fit1, xvar='lambda', which='foo')
    # Warning message:
    # In plot.multnet(fit1, xvar = "lambda", which = 1) :
    #   `which="foo"` not in classnames, defaulting to all
    

    Data:

    set.seed(42)
    x <- matrix(rnorm(100 * 20), 100, 20)
    y <- sample(letters[1:3], 100, replace=TRUE)