【问题标题】:Extracting coefficient variable names from glmnet into a data.frame从 glmnet 中提取系数变量名称到 data.frame
【发布时间】:2015-03-04 06:22:51
【问题描述】:

我想提取 glmnet 生成的模型系数并从中创建 SQL 查询。函数coef(cv.glmnet.fit) 产生一个“dgCMatrix”对象。当我使用as.matrix 将其转换为矩阵时,变量名称会丢失,只留下系数值。

我知道可以在屏幕上打印系数,但是可以将名称写入数据框吗?

谁能帮忙提取这些名字?

【问题讨论】:

  • 您需要发布一个可重现的示例。发布一些数据,做一个例子告诉我们问题是什么以及您希望它是什么。使用glmnet 默认情况下我总是得到变量名,所以我不明白这个问题。

标签: r glmnet


【解决方案1】:

我在使用 tidymodels 框架中的 glmnet 时遇到了类似的问题,其中模型是在工作流中训练的,coef() 和上述解决方案都不起作用。

不过,对我有用的是 glmnet:::coef.glmnet 代码的一部分:

# taken from glmnet:::coef.glmnet
coefs <- predict(x, "lambda.min", type = "coefficients", exact = FALSE)

dd <- cbind(
  data.frame(var = rownames(coefs)),
  as.data.table(as.matrix(coefs))
)

【讨论】:

    【解决方案2】:

    更新: 我的答案的前两个 cmet 都是正确的。为了后代,我将答案保留在下面。

    以下答案很简短,它可以工作并且不需要任何其他包:

    tmp_coeffs <- coef(cv.glmnet.fit, s = "lambda.min")
    data.frame(name = tmp_coeffs@Dimnames[[1]][tmp_coeffs@i + 1], coefficient = tmp_coeffs@x)
    

    +1 的原因是 @i 方法的索引从 0 开始截取,但 @Dimnames[[1]] 从 1 开始。


    老答案:(仅供后代使用) 试试这些行:

    非零系数:

    coef(cv.glmnet.fit, s = "lambda.min")[which(coef(cv.glmnet.fit, s = "lambda.min") != 0)]
    

    选择的特征:

    colnames(regression_data)[which(coef(cv.glmnet.fit, s = "lambda.min") != 0)]
    

    然后将它们组合成一个数据框是直接的,但如果你也想要这部分代码,请告诉我。


    【讨论】:

    • 请注意,colnames(regression_data)[which(coef(cv.glmnet.fit, s = "lambda.min") != 0)] 不考虑截距(第一列),因此显示错误的名称
    • @x 对象方法将为您提供非零系数。
    • 感谢您的意见。我现在提供了一个更好的解决方案
    • 这仍然不正确。 tmp_coeffs@i 显示截距的索引为 0,而 tmp_coeffs@Dimnames[[1]] 在位置 1 截距。您需要添加 1 或使用下面的 Peter 解决方案。
    • @joel.wilson 也许您没有使用glmnet::cv.glmnet() 来拟合模型。是这样吗?
    【解决方案3】:

    假设您知道如何获取 lambda,我发现了两种不同的方法来显示特定 lambda 的所选模型中所需的预测变量。其中之一包括拦截。 lambda 可以通过“glmnet”库中的cv.glmnet 的平均值使用交叉验证获得。您可能只想查看每个方法的最后几行:

     myFittedLasso = glmnet(x=myXmatrix, y=myYresponse, family="binomial")
     myCrossValidated = cv.glmnet(x=myXmatrix, y=myYresponse, family="binomial")
     myLambda = myCrossValidated$lambda.1se  # can be simply lambda
    
     # Method 1 without the intercept
     myBetas = myFittedLasso$beta[, which(myFittedLasso$lambda == myLambda)]
     myBetas[myBetas != 0]
     ## myPredictor1    myPredictor2    myPredictor3
     ##   0.24289802      0.07561533      0.18299284
    
    
     # Method 2 with the intercept
     myCoefficients = coef(myFittedLasso, s=myLambda)
     dimnames(myCoefficients)[[1]][which(myCoefficients != 0)]
     ## [1] "(Intercept)"    "myPredictor1"    "M_myPredictor2"    "myPredictor3"
    
     myCoefficients[which(myCoefficients != 0)]
     ## [1] -4.07805560  0.24289802  0.07561533  0.18299284
    

    请注意,上面的示例暗示了二项分布,但这些步骤可以应用于任何其他类型。

    【讨论】:

      【解决方案4】:

      在这里,我编写了一个可重现的示例,并使用cv.glmnet 拟合了一个二进制(逻辑)示例。 glmnet 模型拟合也可以。在这个示例的最后,我将非零系数和相关特征组装到一个名为 myResults 的 data.frame 中:

      library(glmnet)
      X <- matrix(rnorm(100*10), 100, 10);
      X[51:100, ] <- X[51:100, ] + 0.5; #artificially introduce difference in control cases
      rownames(X) <- paste0("observation", 1:nrow(X));
      colnames(X) <- paste0("feature",     1:ncol(X));
      
      y <- factor( c(rep(1,50), rep(0,50)) ); #binary outcome class label
      y
      ## [1] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
      ## [51] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
      ## Levels: 0 1
      
      ## Perform logistic model fit:
      fit1 <- cv.glmnet(X, y, family="binomial", nfolds=5, type.measure="auc"); #with K-fold cross validation
      # fit1 <- glmnet(X, y, family="binomial") #without cross validation also works
      
      ## Adapted from @Mehrad Mahmoudian:
      myCoefs <- coef(fit1, s="lambda.min");
      myCoefs[which(myCoefs != 0 ) ]               #coefficients: intercept included
      ## [1]  1.4945869 -0.6907010 -0.7578129 -1.1451275 -0.7494350 -0.3418030 -0.8012926 -0.6597648 -0.5555719
      ## [10] -1.1269725 -0.4375461
      myCoefs@Dimnames[[1]][which(myCoefs != 0 ) ] #feature names: intercept included
      ## [1] "(Intercept)" "feature1"    "feature2"    "feature3"    "feature4"    "feature5"    "feature6"   
      ## [8] "feature7"    "feature8"    "feature9"    "feature10"  
      
      ## Asseble into a data.frame
      myResults <- data.frame(
        features = myCoefs@Dimnames[[1]][ which(myCoefs != 0 ) ], #intercept included
        coefs    = myCoefs              [ which(myCoefs != 0 ) ]  #intercept included
      )
      myResults
      ##       features      coefs
      ## 1  (Intercept)  1.4945869
      ## 2     feature1 -0.6907010
      ## 3     feature2 -0.7578129
      ## 4     feature3 -1.1451275
      ## 5     feature4 -0.7494350
      ## 6     feature5 -0.3418030
      ## 7     feature6 -0.8012926
      ## 8     feature7 -0.6597648
      ## 9     feature8 -0.5555719
      ## 10    feature9 -1.1269725
      ## 11   feature10 -0.4375461
      

      【讨论】:

      • s = 'lambda.min 给我一个错误。 Error in lambda[1] - s : non-numeric argument to binary operator 有什么想法吗?
      • 你使用函数glmnet还是cv.glmnet?它们生成的数据结构不一样。
      • "h(simpleError(msg, call)) 中的错误:在为函数“which”选择方法时评估参数“x”时出错:不能强制“list”对象键入“double” '"
      【解决方案5】:
      # requires tibble.
      tidy_coef <- function(x){
          coef(x) %>%
          matrix %>%   # Coerce from sparse matrix to regular matrix.
          data.frame %>%  # Then dataframes.
          rownames_to_column %>%  # Add rownames as explicit variables.
          setNames(c("term","estimate"))
      }
      

      没有小标题:

      tidy_coef2 <- function(x){
          x <- coef(x)
          data.frame(term=rownames(x),
                     estimate=matrix(x)[,1],
                     stringsAsFactors = FALSE)
      }
      

      【讨论】:

        【解决方案6】:

        有一种使用 coef()glmnet() 对象(您的模型)的方法。在索引 [[1]] 以下的情​​况下,表示多项逻辑回归中结果类的数量,也许对于其他模型,您应该删除它。

        coef_names_GLMnet <- coef(GLMnet, s = 0)[[1]]
        row.names(coef_names_GLMnet)[coef_names_GLMnet@i+1]
        
        在这种情况下,

        row.names() 索引需要递增 (+1),因为 coef() 对象中的变量(数据特征)的编号从 0 开始,但是变换后的字符向量记数从 1 开始。

        【讨论】:

          【解决方案7】:

          在上面 Mehrad 的解决方案的基础上,这里有一个简单的函数来打印一个只包含非零系数的表格:

          print_glmnet_coefs <- function(cvfit, s="lambda.min") {
              ind <- which(coef(cvfit, s=s) != 0)
              df <- data.frame(
                  feature=rownames(coef(cvfit, s=s))[ind],
                  coeficient=coef(cvfit, s=s)[ind]
              )
              kable(df)
          }
          

          上面的函数使用来自 knitr 的 kable() 函数来生成 Markdown-ready 表格。

          【讨论】:

          • s = 'lambda.min 给我一个错误。 Error in lambda[1] - s : non-numeric argument to binary operator 有什么想法吗?
          【解决方案8】:

          名称应该可以作为dimnames(coef(cv.glmnet.fit))[[1]] 访问,因此下面应该将系数名称和值都放入data.frame: data.frame(coef.name = dimnames(coef(GLMNET))[[1]], coef.value = matrix(coef(GLMNET)))

          【讨论】:

            【解决方案9】:

            检查broom 包。它具有tidy 函数,可将不同R 对象(包括glmnet)的输出转换为data.frames。

            【讨论】:

            • 这是最干净的答案。
            猜你喜欢
            • 2014-10-06
            • 1970-01-01
            • 2014-08-16
            • 2019-10-08
            • 2020-06-05
            • 2012-06-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多