在使用caret的train()函数训练randomForest对象并使用公式进行预测时出现错误。

13

在一台64位Linux机器上,使用 R 3.2.0 版本、caret 6.0-41 版本和 randomForest 4.6-10 版本。当尝试对使用公式训练的randomForest对象应用caret包中的train()函数进行预测时,使用predict()方法会返回错误。但是,如果使用randomForest()进行训练并且使用x=和y=而不是公式,则一切都能够平稳运行。

以下是一个可工作的示例:

library(randomForest)
library(caret)

data(imports85)
imp85     <- imports85[, c("stroke", "price", "fuelType", "numOfDoors")]
imp85     <- imp85[complete.cases(imp85), ]
imp85[]   <- lapply(imp85, function(x) if (is.factor(x)) x[,drop=TRUE] else x) ## Drop empty levels for factors.

modRf1  <- randomForest(numOfDoors~., data=imp85)
caretRf <- train( numOfDoors~., data=imp85, method = "rf" )
modRf2  <- caretRf$finalModel
modRf3  <- randomForest(x=imp85[,c("stroke", "price", "fuelType")], y=imp85[, "numOfDoors"])
caretRf <- train(x=imp85[,c("stroke", "price", "fuelType")], y=imp85[, "numOfDoors"], method = "rf")
modRf4  <- caretRf$finalModel

p1      <- predict(modRf1, newdata=imp85)
p2      <- predict(modRf2, newdata=imp85)
p3      <- predict(modRf3, newdata=imp85)
p4      <- predict(modRf4, newdata=imp85)

在最后4行中,只有第二行p2 <- predict(modRf2, newdata=imp85)返回以下错误:

Error in predict.randomForest(modRf2, newdata = imp85) : 
variables in the training data missing in newdata

看起来这个错误的原因是predict.randomForest方法使用rownames(object$importance)来确定用于训练随机森林object的变量名称。当查看

rownames(modRf1$importance)
rownames(modRf2$importance)
rownames(modRf3$importance)
rownames(modRf4$importance)

我们看到:

[1] "stroke"   "price"    "fuelType"
[1] "stroke"   "price"    "fuelTypegas"
[1] "stroke"   "price"    "fuelType"
[1] "stroke"   "price"    "fuelType"

使用 caret 中的 train() 函数并带有公式,会导致 randomForest 对象的 importance 字段中的(因子)变量名称发生更改。

这是否意味着公式版本和非公式版本的 caret train() 函数之间存在不一致性?还是我漏掉了什么?


2
modRf3 <- randomForest(x=dataTrain[,c("stroke", "price", "fuelType")], y=dataTrain[, "numOfDoors"], data=imp85) Error in randomForest(x = dataTrain[, c("stroke", "price", "fuelType")], : object 'dataTrain' not found - user3710546
正如所指出的那样,在您的示例中未定义dataTrain,这意味着问题无法重现。如果我们无法运行代码并获得与您相同的结果,那么帮助您并不容易。 - MrFlick
我的错,dataTrain 应该是 imp85,我在原始问题的代码中进行了编辑。同时,在显式地提到 xy 的调用中,我删除了选项 data=imp85,因为它没有用处。 - Adrien Combaz
3个回答

37

首先,几乎从不使用$finalModel对象进行预测。使用predict.train。这是一个很好的例子。

某些函数(包括randomForesttrain)在处理虚拟变量时存在一些不一致性。在R中,大多数使用公式方法的函数将因子预测器转换为虚拟变量,因为它们的模型需要数据的数值表示。例外情况是树和基于规则的模型(可以在分类预测器上分裂)、朴素贝叶斯和其他一些模型。

所以,当你使用randomForest(y ~ ., data = dat)时,randomForest不会创建虚拟变量,但是使用train(y ~ ., data = dat)的话,train(以及大多数其他函数)会创建虚拟变量。

错误出现的原因是fuelType是一个因子。由train创建的虚拟变量没有相同的名称,因此predict.randomForest无法找到它们。

如果想要相同的水平,则使用train的非公式方法将因子预测器传递给randomForest即可使所有操作正常工作。

简而言之

如果你想要相同的水平,则使用train的非公式方法,或者使用predict.train


4
很不幸,我声望不够无法给你的回答点赞,但是你完美地回答了我的问题。我一直在想,所有那些允许使用公式的函数,在调用带公式和不带公式版本时,数据处理方式是否有所不同。现在我知道了!关于使用 $finalModel,我同意通常不建议使用它。这里我只是想比较 caretrandomForest 方法的结果。 - Adrien Combaz
“使用非公式方法”是什么意思? - canderson156

0

另一种方法是使用model.matrix显式编写测试数据,例如:

p2 <- predict(modRf2, newdata=model.matrix(~., imp85))

0

出现此错误可能有两个原因。

1. 训练集和测试集中分类变量的类别不匹配。您可以运行以下命令来检查:

首先,将独立变量/特征存储在列表中是一个好习惯。假设该列表为“vars”。并且您将“Data”分成了“Train”和“Test”。让我们看看:

for (v in vars){
  if (class(Data[,v]) == 'factor'){
    print(v)
    # print(levels(Train[,v])) 
    # print(levels(Test[,v]))
    print(all.equal(levels(Train[,v]) , levels(Test[,v])))
  }  
}
一旦找到不匹配的分类变量,您可以返回并将测试数据的类别强加到训练数据上,然后重新构建模型。在类似上述的循环中,对于每个非匹配变量,您可以执行以下操作:
levels(Test$nonMatchingVar) <- levels(Train$nonMatchingVar)

2. 一个愚蠢的错误。如果你不小心把因变量放在自变量集合中,你可能会遇到这个错误信息。我曾经犯过这个错误。解决方案:要更加小心。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接