R dummy/onehot-encoding 固定列结构
R dummy/onehot-encoding with fixed column structure
假设我的机器学习训练数据集包含 3 列,类别多达 50 个不同级别。我一次性对列进行编码。测试数据集只有一行。如何在对测试数据集进行编码时保持训练数据集的结构?
训练数据一切正常...
v1 <- factor(c("a","b","c","a"))
v2 <- factor(c("A","A","B","C"))
train <- data.frame(v1 = v1,v2 = v2)
train_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=train,
contrasts.arg=list(v1=contrasts(train$v1, contrasts=F),
v2=contrasts(train$v2, contrasts=F))))
print(train)
v1 v2
a A
b A
c B
a C
print(train_dummy )
v1a v1b v1c v2A v2B v2C
1 0 0 1 0 0
0 1 0 1 0 0
0 0 1 0 1 0
1 0 0 0 0 1
...但是对于测试数据它失败了。当我尝试将训练数据的因子水平应用于测试数据时,它不起作用:
test <- data.frame(v1 = factor("a"),v2 = factor("A"))
test_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=test,
contrasts.arg=list(v1=contrasts(train$v1, contrasts=F),
v2=contrasts(train$v2, contrasts=F))))
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) :
contrasts can be applied only to factors with 2 or more levels
当然我可以行绑定训练和测试数据,然后进行虚拟编码,但这是生产代码,我不能接受这是唯一的解决方案:
train_test <- rbind(train,test)
train_test_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=train_test,
contrasts.arg=list(v1=contrasts(train_test$v1, contrasts=F),
v2=contrasts(train_test$v2, contrasts=F))))
print(train_test_dummy)
v1a v1b v1c v2A v2B v2C
1 0 0 1 0 0
0 1 0 1 0 0
0 0 1 0 1 0
1 0 0 0 0 1
1 0 0 1 0 0
还有更好的吗?
这是一个 duplicate 但问题没有得到回答,所有其他问题仅涉及从一个数据集生成虚拟变量。
如果您另外添加
levels(test$v1) <- levels(train$v1)
levels(test$v2) <- levels(train$v2)
或者,如果所有列都是因子,则在一行中,
test[] <- Map(function(x, y) factor(x, level = levels(y)), test, train)
如果其中只有一部分是因素,
test[] <- Map(function(x, y) if(is.factor(x)) factor(x, level = levels(y)) else x, test, train)
那么最后的结果根据需要:
test_dummy
# v1a v1b v1c v2A v2B v2C
# 1 1 0 0 1 0 0
假设我的机器学习训练数据集包含 3 列,类别多达 50 个不同级别。我一次性对列进行编码。测试数据集只有一行。如何在对测试数据集进行编码时保持训练数据集的结构?
训练数据一切正常...
v1 <- factor(c("a","b","c","a"))
v2 <- factor(c("A","A","B","C"))
train <- data.frame(v1 = v1,v2 = v2)
train_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=train,
contrasts.arg=list(v1=contrasts(train$v1, contrasts=F),
v2=contrasts(train$v2, contrasts=F))))
print(train)
v1 v2
a A
b A
c B
a C
print(train_dummy )
v1a v1b v1c v2A v2B v2C
1 0 0 1 0 0
0 1 0 1 0 0
0 0 1 0 1 0
1 0 0 0 0 1
...但是对于测试数据它失败了。当我尝试将训练数据的因子水平应用于测试数据时,它不起作用:
test <- data.frame(v1 = factor("a"),v2 = factor("A"))
test_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=test,
contrasts.arg=list(v1=contrasts(train$v1, contrasts=F),
v2=contrasts(train$v2, contrasts=F))))
Error in `contrasts<-`(`*tmp*`, value = contr.funs[1 + isOF[nn]]) :
contrasts can be applied only to factors with 2 or more levels
当然我可以行绑定训练和测试数据,然后进行虚拟编码,但这是生产代码,我不能接受这是唯一的解决方案:
train_test <- rbind(train,test)
train_test_dummy <- as.data.frame(model.matrix(~ v1 + v2 -1 , data=train_test,
contrasts.arg=list(v1=contrasts(train_test$v1, contrasts=F),
v2=contrasts(train_test$v2, contrasts=F))))
print(train_test_dummy)
v1a v1b v1c v2A v2B v2C
1 0 0 1 0 0
0 1 0 1 0 0
0 0 1 0 1 0
1 0 0 0 0 1
1 0 0 1 0 0
还有更好的吗?
这是一个 duplicate 但问题没有得到回答,所有其他问题仅涉及从一个数据集生成虚拟变量。
如果您另外添加
levels(test$v1) <- levels(train$v1)
levels(test$v2) <- levels(train$v2)
或者,如果所有列都是因子,则在一行中,
test[] <- Map(function(x, y) factor(x, level = levels(y)), test, train)
如果其中只有一部分是因素,
test[] <- Map(function(x, y) if(is.factor(x)) factor(x, level = levels(y)) else x, test, train)
那么最后的结果根据需要:
test_dummy
# v1a v1b v1c v2A v2B v2C
# 1 1 0 0 1 0 0