【问题标题】:All Levels of a Factor in a Model Matrix in RR中模型矩阵中因子的所有级别
【发布时间】:2011-06-01 10:05:49
【问题描述】:

我有一个data.frame,由数字和因子变量组成,如下所示。

testFrame <- data.frame(First=sample(1:10, 20, replace=T),
           Second=sample(1:20, 20, replace=T), Third=sample(1:10, 20, replace=T),
           Fourth=rep(c("Alice","Bob","Charlie","David"), 5),
           Fifth=rep(c("Edward","Frank","Georgia","Hank","Isaac"),4))

我想构建一个matrix,将虚拟变量分配给因子,而单独保留数字变量。

model.matrix(~ First + Second + Third + Fourth + Fifth, data=testFrame)

正如运行lm 时所预期的那样,这会忽略每个因素的一个水平作为参考水平。但是,我想为所有因素的每个级别构建一个带有虚拟/指标变量的matrix。我正在为glmnet 构建这个矩阵,所以我不担心多重共线性。

有没有办法让model.matrix 为因子的每个级别创建虚拟对象?

【问题讨论】:

    标签: r matrix model indicator


    【解决方案1】:

    (试图赎回自己...)作为对 Jared 对 @Fabians 关于自动化的回答的评论的回应,请注意,您需要提供的只是对比矩阵的命名列表。 contrasts() 采用向量/因子并从中生成对比矩阵。为此,我们可以使用lapply() 对我们数据集中的每个因素运行contrasts(),例如对于提供的testFrame 示例:

    > lapply(testFrame[,4:5], contrasts, contrasts = FALSE)
    $Fourth
            Alice Bob Charlie David
    Alice       1   0       0     0
    Bob         0   1       0     0
    Charlie     0   0       1     0
    David       0   0       0     1
    
    $Fifth
            Edward Frank Georgia Hank Isaac
    Edward       1     0       0    0     0
    Frank        0     1       0    0     0
    Georgia      0     0       1    0     0
    Hank         0     0       0    1     0
    Isaac        0     0       0    0     1
    

    @fabians 回答中哪个位置很好:

    model.matrix(~ ., data=testFrame, 
                 contrasts.arg = lapply(testFrame[,4:5], contrasts, contrasts=FALSE))
    

    【讨论】:

    • +1。好的。您可以通过将 4:5 替换为 sapply(testFrame, is.factor) 来进一步自动化它
    • 自动化的绝佳解决方案。在你们两个之间,我的问题已经得到了完美的回答,所以我不确定谁的答案应该被标记为“接受的答案”。我希望你们都获得荣誉。
    • @Jared:@fabians 是您正在寻找的答案,所以他应该得到赞扬 - 我的贡献只是加了一点糖。
    【解决方案2】:

    您需要为因子变量重置contrasts

    model.matrix(~ Fourth + Fifth, data=testFrame, 
            contrasts.arg=list(Fourth=contrasts(testFrame$Fourth, contrasts=F), 
                    Fifth=contrasts(testFrame$Fifth, contrasts=F)))
    

    或者,打字少一点,没有正确的名字:

    model.matrix(~ Fourth + Fifth, data=testFrame, 
        contrasts.arg=list(Fourth=diag(nlevels(testFrame$Fourth)), 
                Fifth=diag(nlevels(testFrame$Fifth))))
    

    【讨论】:

    • 这完全有效,我会接受这个答案,但如果我输入 20 个因素,是否有办法对框架中的所有变量普遍做到这一点,或者我也注定要打字多少?
    【解决方案3】:

    caret 实现了一个很好的函数dummyVars 用两行代码来实现:

    library(caret) dmy <- dummyVars(" ~ .", data = testFrame) testFrame2 <- data.frame(predict(dmy, newdata = testFrame))

    检查最后一列:

    colnames(testFrame2)
    
    "First"  "Second"         "Third"          "Fourth.Alice"   "Fourth.Bob"     "Fourth.Charlie" "Fourth.David"   "Fifth.Edward"   "Fifth.Frank"   "Fifth.Georgia"  "Fifth.Hank"     "Fifth.Isaac"   
    

    这里最好的一点是你得到了原始数据框,加上排除了用于转换的原始变量的虚拟变量。

    更多信息:http://amunategui.github.io/dummyVar-Walkthrough/

    【讨论】:

      【解决方案4】:

      也可以使用来自caretdummyVarshttp://caret.r-forge.r-project.org/preprocess.html

      【讨论】:

      • 看起来不错,但不包括拦截,我似乎无法强迫它。
      • @jared:它对我有用。示例:require(caret); (df &lt;- data.frame(x1=c('a','b'), x2=1:2)); dummies &lt;- dummyVars(x2~ ., data = df); predict(dummies, newdata = df)
      • @Jared 当您对因子的每个级别都有一个虚拟变量时,不需要拦截。
      • @Jared:这个添加拦截栏:require(caret); (df &lt;- data.frame(x1=c('a','b'), x2=1:2)); dummies &lt;- dummyVars(x2~ ., data = df); predict(dummies, newdata = df); cbind(1, predict(dummies, newdata = df))
      【解决方案5】:

      好的。只需阅读以上内容并将其放在一起。假设您想要矩阵,例如'X.factors' 乘以你的系数向量来得到你的线性预测器。还有几个额外的步骤:

      X.factors = 
        model.matrix( ~ ., data=X, contrasts.arg = 
          lapply(data.frame(X[,sapply(data.frame(X), is.factor)]),
                                                   contrasts, contrasts = FALSE))
      

      (请注意,如果您只有一个因子列,则需要将 X[*] 转回数据框。)

      然后说你得到这样的东西:

      attr(X.factors,"assign")
      [1]  0  1  **2**  2  **3**  3  3  **4**  4  4  5  6  7  8  9 10 #emphasis added
      

      我们希望摆脱每个因素的 **'d 参考水平

      att = attr(X.factors,"assign")
      factor.columns = unique(att[duplicated(att)])
      unwanted.columns = match(factor.columns,att)
      X.factors = X.factors[,-unwanted.columns]
      X.factors = (data.matrix(X.factors))
      

      【讨论】:

      • 顺便说一句,为什么这不是内置到基础 R 中?似乎每次运行模拟时都需要它。
      【解决方案6】:

      tidyverse 回答:

      library(dplyr)
      library(tidyr)
      result <- testFrame %>% 
          mutate(one = 1) %>% spread(Fourth, one, fill = 0, sep = "") %>% 
          mutate(one = 1) %>% spread(Fifth, one, fill = 0, sep = "")
      

      产生预期的结果(与@Gavin Simpson 的回答相同):

      > head(result, 6)
        First Second Third FourthAlice FourthBob FourthCharlie FourthDavid FifthEdward FifthFrank FifthGeorgia FifthHank FifthIsaac
      1     1      5     4           0         0             1           0           0          1            0         0          0
      2     1     14    10           0         0             0           1           0          0            1         0          0
      3     2      2     9           0         1             0           0           1          0            0         0          0
      4     2      5     4           0         0             0           1           0          1            0         0          0
      5     2     13     5           0         0             1           0           1          0            0         0          0
      6     2     15     7           1         0             0           0           1          0            0         0          0
      

      【讨论】:

        【解决方案7】:

        使用 R 包“CatEncoders”

        library(CatEncoders)
        testFrame <- data.frame(First=sample(1:10, 20, replace=T),
                   Second=sample(1:20, 20, replace=T), Third=sample(1:10, 20, replace=T),
                   Fourth=rep(c("Alice","Bob","Charlie","David"), 5),
                   Fifth=rep(c("Edward","Frank","Georgia","Hank","Isaac"),4))
        
        fit <- OneHotEncoder.fit(testFrame)
        
        z <- transform(fit,testFrame,sparse=TRUE) # give the sparse output
        z <- transform(fit,testFrame,sparse=FALSE) # give the dense output
        

        【讨论】:

          【解决方案8】:

          我目前正在学习 Lasso 模型和 glmnet::cv.glmnet()model.matrix()Matrix::sparse.model.matrix()(对于高维矩阵,使用 model.matrix 会浪费我们的时间,正如 glmnet 的作者所建议的那样。)。

          只是分享有一个整洁的编码,以获得与@fabians 和@Gavin 的答案相同的答案。同时,@asdf123 还引入了另一个包library('CatEncoders')

          > require('useful')
          > # always use all levels
          > build.x(First ~ Second + Fourth + Fifth, data = testFrame, contrasts = FALSE)
          > 
          > # just use all levels for Fourth
          > build.x(First ~ Second + Fourth + Fifth, data = testFrame, contrasts = c(Fourth = FALSE, Fifth = TRUE))
          

          来源:R for Everyone: Advanced Analytics and Graphics (page273)

          【讨论】:

          • 感谢您的回答。有趣的是,build.x 函数是我编写的,并通过@fabiens 和@gavin 的回答实现了!那是我的书!太酷了,这绕了一圈。感谢阅读!
          【解决方案9】:

          您可以使用tidyverse 来实现此目的,而无需手动指定每一列。

          诀窍是制作一个“长”数据框。

          然后,整理一些东西,然后将其展开,以创建指标/虚拟变量。

          代码:

          library(tidyverse)
          
          ## add index variable for pivoting
          testFrame$id <- 1:nrow(testFrame)
          
          testFrame %>%
              ## pivot to "long" format
              gather(feature, value, -id) %>%
              ## add indicator value
              mutate(indicator=1) %>%
              ## create feature name that unites a feature and its value
              unite(feature, value, col="feature_value", sep="_") %>%
              ## convert to wide format, filling missing values with zero
              spread(feature_value, indicator, fill=0)
          
          

          输出:

             id Fifth_Edward Fifth_Frank Fifth_Georgia Fifth_Hank Fifth_Isaac First_2 First_3 First_4 ...
          1   1            1           0             0          0           0       0       0       0
          2   2            0           1             0          0           0       0       0       0
          3   3            0           0             1          0           0       0       0       0
          4   4            0           0             0          1           0       0       0       0
          5   5            0           0             0          0           1       0       0       0
          6   6            1           0             0          0           0       0       0       0
          7   7            0           1             0          0           0       0       1       0
          8   8            0           0             1          0           0       1       0       0
          9   9            0           0             0          1           0       0       0       0
          10 10            0           0             0          0           1       0       0       0
          11 11            1           0             0          0           0       0       0       0
          12 12            0           1             0          0           0       0       0       0
          ...
          

          【讨论】:

            【解决方案10】:
            model.matrix(~ First + Second + Third + Fourth + Fifth - 1, data=testFrame)
            

            model.matrix(~ First + Second + Third + Fourth + Fifth + 0, data=testFrame)
            

            应该是最直接的

            【讨论】:

            • 如果只有一个因素,这会很好,但如果有多个因素,仍然会省略参考水平。
            【解决方案11】:

            我编写了一个名为ModelMatrixModel 的包来改进model.matrix() 的功能。包中的 ModelMatrixModel() 函数默认返回一个包含稀疏矩阵的类,该类具有所有级别的虚拟变量,适合在 glmnet 包中的 cv.glmnet() 中输入。重要的是,返回 该类还存储转换参数,例如因子级别信息,然后可以将其应用于新数据。该函数可以处理 r 公式中的大多数项目,例如 poly() 和交互。它还提供了其他几个选项,例如处理无效因子级别和缩放输出。

            #devtools::install_github("xinyongtian/R_ModelMatrixModel")
            library(ModelMatrixModel)
            testFrame <- data.frame(First=sample(1:10, 20, replace=T),
                                    Second=sample(1:20, 20, replace=T), Third=sample(1:10, 20, replace=T),
                                    Fourth=rep(c("Alice","Bob","Charlie","David"), 5))
            newdata=data.frame(First=sample(1:10, 2, replace=T),
                               Second=sample(1:20, 2, replace=T), Third=sample(1:10, 2, replace=T),
                               Fourth=c("Bob","Charlie"))
            mm=ModelMatrixModel(~First+Second+Fourth, data = testFrame)
            class(mm)
            ## [1] "ModelMatrixModel"
            class(mm$x) #default output is sparse matrix
            ## [1] "dgCMatrix"
            ## attr(,"package")
            ## [1] "Matrix"
            data.frame(as.matrix(head(mm$x,2)))
            ##   First Second FourthAlice FourthBob FourthCharlie FourthDavid
            ## 1     7     17           1         0             0           0
            ## 2     9      7           0         1             0           0
            
            #apply the same transformation to new data, note the dummy variables for 'Fourth' includes the levels not appearing in new data     
            mm_new=predict(mm,newdata)
            data.frame(as.matrix(head(mm_new$x,2))) 
            ##   First Second FourthAlice FourthBob FourthCharlie FourthDavid
            ## 1     6      3           0         1             0           0
            ## 2     2     12           0         0             1           0
            

            【讨论】:

              猜你喜欢
              • 2015-02-16
              • 2015-10-13
              • 2021-12-01
              • 2016-03-26
              • 1970-01-01
              • 1970-01-01
              • 2021-02-20
              • 2018-10-20
              • 2018-05-14
              相关资源
              最近更新 更多