从多个因子列生成虚拟矩阵

Generate a dummy matrix from multiple factor columns

我已经在网上搜索过,但没有找到答案。我有一个包含多列的大 data.frame。每列都是一个因子变量。

我想转换 data.frame 使得因子变量的每个可能值都是一个变量,如果变量存在于因子列中则包含“1”,否则包含“0”。

这是我的意思的一个例子。

labels <- c("1", "2", "3", "4", "5", "6", "7") 

#create data frame (note, not all factor levels have to be in the columns,
#NA values are possible)
input <- data.frame(ID = c(1, 2, 3), 
Cat1 = factor(c( 4, 1, 1), levels = labels), 
Cat2 = factor(c(2, NA, 4), levels = labels),
Cat3 = factor(c(7, NA, NA), levels = labels))

#the seven factor levels now are the variables of the data.frame
desired_output <- data.frame(ID = c(1, 2, 3),
Dummy1 = c(0, 1, 1),
Dummy2 = c(1, 0, 0),
Dummy3 = c(0, 0, 0),
Dummy4 = c(1, 0, 1),
Dummy5 = c(0, 0, 0),
Dummy6 = c(0, 0, 0),
Dummy7 = c(1, 0, 0))

input
ID Cat1 Cat2 Cat3
1    4    2    7
2    1 <NA> <NA>
3    1    4 <NA>

desired_output
ID Dummy1 Dummy2 Dummy3 Dummy4 Dummy5 Dummy6 Dummy7
1      0      1      0      1      0      0      1
2      1      0      0      0      0      0      0
3      1      0      0      1      0      0      0

我的实际 data.frame 有 3000 多行和 100 多个级别的因素。 我希望你能帮我把输入转换成想要的输出。

您好 嘘

一种方法是使用矩阵索引。您有数据指定输出矩阵中的哪些位置应为 1(其余位置应为零),因此我们将制作一个零矩阵,然后根据您的数据填充 1。为此,您的数据需要位于双列矩阵中,第一列是输出的行 (ID),第二列是列。

以长格式输入数据,删除缺失值,将值转换为与标签匹配的整数,然后根据需要制作矩阵。

in2 <- reshape2::melt(input, id.vars="ID")
in2 <- subset(in2, !is.na(value))
in2$value <- match(in2$value, labels)
in2$variable <- NULL
in2 <- as.matrix(in2)

然后制作全为零的新输出矩阵,并使用该矩阵填充那些。

out <- matrix(0, nrow=nrow(input), ncol=length(labels))
colnames(out) <- labels
rownames(out) <- input$ID
out[in2] <- 1

out
##   1 2 3 4 5 6 7
## 1 0 1 0 1 0 0 1
## 2 1 0 0 0 0 0 0
## 3 1 0 0 1 0 0 0

这是使用 model.matrix 的方法。我们将缺失值转换为 0,并指定 0 作为因子对比的参考水平。然后我们只需将各个模型矩阵加在一起并贴上 ID:

new_lab = as.character(0:7)
for (i in 2:4) {
  temp = as.character(input[[i]])
  temp[is.na(temp)] = "0"
  input[[i]] = factor(temp, levels = new_lab)
}

mm = 
  model.matrix(~ Cat1, data = input) +
  model.matrix(~ Cat2, data = input) +
  model.matrix(~ Cat3, data = input)

mm[, 1] = input$ID
colnames(mm) = c("ID", paste0("Dummy", 1:(ncol(mm) - 1)))
mm
#   ID Dummy1 Dummy2 Dummy3 Dummy4 Dummy5 Dummy6 Dummy7
# 1  1      0      1      0      1      0      0      1
# 2  2      1      0      0      0      0      0      0
# 3  3      1      0      0      1      0      0      0
# attr(,"assign")
# [1] 0 1 1 1 1 1 1 1
# attr(,"contrasts")
# attr(,"contrasts")$Cat1
# [1] "contr.treatment"

您可以将结果保留为模型矩阵,将其改回数据框或其他任何形式。

这应该适用于您的数据框。我在 运行 ifelse 语句之前将值转换为数字。希望它有效:

# Make dummy df
Cat1 = factor(c( 4, 1, 1))
Cat2 = factor(c(2, NA, 4))
Cat3 = factor(c(7, NA, NA))

df <- data.frame(Cat1,Cat2,Cat3)

# Specify columns
cols <- c(1:length(df))

# Convert Values To Numeric 
df[,cols] %<>% lapply(function(x) as.numeric(as.character(x)))

# Perform ifelse. If its NA print 0, else print 1
df[,cols] %<>% lapply(function(x) ifelse(x == is.na(x) | (x) %in% NA, 0, 1))

基于输入:

  Cat1 Cat2 Cat3
1    4    2    7
2    1 <NA> <NA>
3    1    4 <NA>

输出如下所示:

 Cat1 Cat2 Cat3
1    1    1    1
2    1    0    0
3    1    1    0

几个方法,重复 Gregor 和 Aaron 的回答。

来自亚伦的。 factorsAsStrings=FALSE 在使用 dcast

时保留因子变量因此所有标签
library(reshape2)
dcast(melt(input, id="ID", factorsAsStrings=FALSE), ID ~ value, drop=FALSE) 
  ID 1 2 3 4 5 6 7 NA
1  1 0 1 0 1 0 0 1  0
2  2 1 0 0 0 0 0 0  2
3  3 1 0 0 1 0 0 0  1

然后你只需要删除最后一列。

来自格雷戈尔

na.replace <- function(x) replace(x, is.na(x), 0)
options(na.action='na.pass') # this keeps the NA's which are then converted to zero
Reduce("+", lapply(input[-1], function(x) na.replace(model.matrix(~ 0 + x))))
  x1 x2 x3 x4 x5 x6 x7
1  0  1  0  1  0  0  1
2  1  0  0  0  0  0  0
3  1  0  0  1  0  0  0

那么您只需要 cbind ID