R中模型矩阵中因子的所有级别
Posted
技术标签:
【中文标题】R中模型矩阵中因子的所有级别【英文标题】:All Levels of a Factor in a Model Matrix in R 【发布时间】: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
为因子的每个级别创建虚拟对象?
【问题讨论】:
【参考方案1】:(试图赎回自己...)作为对 Jared 对 @F***s 关于自动化的回答的评论的回应,请注意,您需要提供的只是对比矩阵的命名列表。 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
@f***s 回答中哪个位置很好:
model.matrix(~ ., data=testFrame,
contrasts.arg = lapply(testFrame[,4:5], contrasts, contrasts=FALSE))
【讨论】:
+1。好的。您可以通过将 4:5 替换为 sapply(testFrame, is.factor) 来进一步自动化它 自动化的绝佳解决方案。在你们两个之间,我的问题已经得到了完美的回答,所以我不确定谁的答案应该被标记为“接受的答案”。我希望你们都获得荣誉。 @Jared:@f***s 是您正在寻找的答案,所以他应该得到赞扬 - 我的贡献只是加了一点糖。【参考方案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】:也可以使用来自caret
的dummyVars
。 http://caret.r-forge.r-project.org/preprocess.html
【讨论】:
看起来不错,但不包括拦截,我似乎无法强迫它。 @jared:它对我有用。示例:require(caret); (df <- data.frame(x1=c('a','b'), x2=1:2)); dummies <- dummyVars(x2~ ., data = df); predict(dummies, newdata = df)
@Jared 当您对因子的每个级别都有一个虚拟变量时,不需要拦截。
@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))
【参考方案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
的作者所建议的那样。)。
只是分享有一个整洁的编码,以获得与@f***s 和@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
【讨论】:
以上是关于R中模型矩阵中因子的所有级别的主要内容,如果未能解决你的问题,请参考以下文章
R语言droplevels函数删除因子变量(factor)没有用到的级别(level)实战
很想弄清楚 R 中的条形图上哪个因子级别已映射到哪个填充颜色?