我有一个公式和一个数据框,我想提取model.matrix()
。但是,我需要生成的矩阵包含在原始数据集中找到的NA。如果我使用model.frame()
来执行此操作,我只需将其传递给na.action=NULL
。但是,我需要的输出是model.matrix()
格式。具体来说,我只需要右侧变量,我需要输出为矩阵(不是数据帧),我需要将因子转换为一系列虚拟变量。
我确信我可以使用循环或其他东西一起破解某些东西,但我想知道是否有人可以建议更清洁,更有效的解决方法。非常感谢你的时间!
这是一个例子:
dat <- data.frame(matrix(rnorm(20),5,4), gl(5,2))
dat[3,5] <- NA
names(dat) <- c(letters[1:4], 'fact')
ff <- a ~ b + fact
# This omits the row with a missing observation on the factor
model.matrix(ff, dat)
# This keeps the NA, but it gives me a data frame and does not dichotomize the factor
model.frame(ff, dat, na.action=NULL)
这是我想要获得的:
(Intercept) b fact2 fact3 fact4 fact5
1 1 0.7266086 0 0 0 0
2 1 -0.6088697 0 0 0 0
3 NA 0.4643360 NA NA NA NA
4 1 -1.1666248 1 0 0 0
5 1 -0.7577394 0 1 0 0
6 1 0.7266086 0 1 0 0
7 1 -0.6088697 0 0 1 0
8 1 0.4643360 0 0 1 0
9 1 -1.1666248 0 0 0 1
10 1 -0.7577394 0 0 0 1
答案 0 :(得分:43)
Joris的建议有效,但更快更清洁的方法是通过全球na.action设置。 “通过”选项实现了我们从原始数据集中保留NA的目标。
结果矩阵将在与原始数据集对应的行中包含NA。
options(na.action='na.pass')
model.matrix(ff, dat)
结果矩阵将跳过包含NA的行。
options(na.action='na.omit')
model.matrix(ff, dat)
如果原始数据包含NA,则会发生错误。
options(na.action='na.fail')
model.matrix(ff, dat)
当然,在更改全局选项时要小心,因为它们可能会改变代码其他部分的行为。谨慎的人可能会将原始设置存储为类似current.na.action <- options('na.action')
的内容,然后在制作model.matrix后将其更改回来。
答案 1 :(得分:30)
另一种方法是使用带有参数model.frame
的{{1}}函数作为na.action=na.pass
的第二个参数:
model.matrix
> model.matrix(ff, model.frame(~ ., dat, na.action=na.pass))
(Intercept) b fact2 fact3 fact4 fact5
1 1 -1.3560754 0 0 0 0
2 1 2.5476965 0 0 0 0
3 1 0.4635628 NA NA NA NA
4 1 -0.2871379 1 0 0 0
5 1 2.2684958 0 1 0 0
6 1 -1.3560754 0 1 0 0
7 1 2.5476965 0 0 1 0
8 1 0.4635628 0 0 1 0
9 1 -0.2871379 0 0 0 1
10 1 2.2684958 0 0 0 1
可让您为调用model.frame
时维护的na.action
设置相应的操作。
答案 2 :(得分:16)
根据rownames:
,你可以使用model.matrix
对象搞乱一点
MM <- model.matrix(ff,dat)
MM <- MM[match(rownames(dat),rownames(MM)),]
MM[,"b"] <- dat$b
rownames(MM) <- rownames(dat)
给出:
> MM
(Intercept) b fact2 fact3 fact4 fact5
1 1 0.9583010 0 0 0 0
2 1 0.3266986 0 0 0 0
3 NA 1.4992358 NA NA NA NA
4 1 1.2867461 1 0 0 0
5 1 0.5024700 0 1 0 0
6 1 0.9583010 0 1 0 0
7 1 0.3266986 0 0 1 0
8 1 1.4992358 0 0 1 0
9 1 1.2867461 0 0 0 1
10 1 0.5024700 0 0 0 1
或者,您可以使用contrasts()
为您完成工作。手工构建矩阵将是:
cont <- contrasts(dat$fact)[as.numeric(dat$fact),]
colnames(cont) <- paste("fact",colnames(cont),sep="")
out <- cbind(1,dat$b,cont)
out[is.na(dat$fact),1] <- NA
colnames(out)[1:2]<- c("Intercept","b")
rownames(out) <- rownames(dat)
给出:
> out
Intercept b fact2 fact3 fact4 fact5
1 1 0.2534288 0 0 0 0
2 1 0.2697760 0 0 0 0
3 NA -0.8236879 NA NA NA NA
4 1 -0.6053445 1 0 0 0
5 1 0.4608907 0 1 0 0
6 1 0.2534288 0 1 0 0
7 1 0.2697760 0 0 1 0
8 1 -0.8236879 0 0 1 0
9 1 -0.6053445 0 0 0 1
10 1 0.4608907 0 0 0 1
在任何情况下,两种方法都可以合并到一个可以处理更复杂公式的函数中。我把这个练习留给了读者(当我在一篇论文中遇到它时,我对这句话感到厌恶;-))
答案 3 :(得分:8)
在查看mattdevlin和Nathan Gould的答案后,我偶然发现了一个更简单的解决方案:
model.matrix.lm(ff, dat, na.action = "na.pass")
model.matrix.default
可能不支持na.action
参数,但model.matrix.lm
会支持!
(我在Rstudio的自动完成建议中找到model.matrix.lm
- 如果你没有加载任何添加其他库的库,它似乎是model.matrix
唯一的非默认方法。然后我猜对了它可能支持na.action
参数。)