R模型矩阵中因子的所有层次


69

我有一个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为每个因子水平创建虚拟对象?

Answers:


52

您需要重置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))))

14
那完全可行,我会回答这个问题,但是如果我输入20个因素,是否有一种方法可以通用地对框架中的所有变量执行此操作,还是我注定要输入太多内容?
杰瑞德(Jared)2010年

66

(试图兑现自己...)为回应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))

21
+1。很好 您可以通过将4:5替换为sapply(testFrame,is.factor)来实现更高的自动化程度
fabians 2010年

自动化的绝佳解决方案。在你们两个人之间,我的问题已经得到了很好的回答,因此我不确定谁的回答应该被标记为“接受的答案”。我希望你们俩都能获得信誉。
杰瑞德(Jared)

6
@Jared:@fabians是您想要的答案,因此他应该得到荣誉-我的贡献只是一点点糖。
加文·辛普森

18

caret实现了一个不错的功能,dummyVars只需2行即可实现:

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/


11

dummyVarsfromcaret也可以使用。http://caret.r-forge.r-project.org/preprocess.html


看起来不错,但其中不包含拦截器,我似乎也无法强制这样做。
杰瑞德(Jared)2013年

2
@jared:对我有用。示例:require(caret); (df <- data.frame(x1=c('a','b'), x2=1:2)); dummies <- dummyVars(x2~ ., data = df); predict(dummies, newdata = df)
Andrew

1
如果您对每个因子水平都有一个虚拟变量,则@Jared无需拦截。
Will Townes

1
@Jared:此添加拦截列:require(caret); (df <- data.frame(x1=c('a','b'), x2=1:2)); dummies <- dummyVars(x2~ ., data = df); predict(dummies, newdata = df); cbind(1, predict(dummies, newdata = df))
MYaseen208 '17

3

一个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

2

好。只需阅读以上内容并将其放在一起即可。假设您想要一个矩阵,例如“ 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

我们要摆脱每个因素的**参考水平

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

1
顺便说一句,为什么这不是内置于R的基础?似乎每次运行模拟都需要它。
user36302 2014年

2

使用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

2

目前我正在学习套索模型和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:高级分析和图形(第273页)


感谢你的回答。有趣的是,该build.x函数是由我编写的,并且可以通过@fabiens和@gavin的答案来实现!那是我的书!太酷了,这圈满了。谢谢阅读!
贾里德

1

您可以用于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
...

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

要么

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

应该是最直接的


如果只有一个因素,这将很好地工作,但是如果有多个因素,则仍将省略参考水平。
格雷戈尔·托马斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.