在使用公式使用插入符号的 train() 训练的 randomForest 对象上使用 predict() 时出错

Error when using predict() on a randomForest object trained with caret's train() using formula

在 64 位 Linux 机器上使用带插入符 6.0-41 和 randomForest 4.6-10 的 R 3.2.0。

当尝试对使用公式从 caret 包中的 train() 函数训练的 randomForest 对象使用 predict() 方法时,函数 returns 一个错误。 当使用 x=y= 而不是公式通过 randomForest() and/or 进行训练时,一切运行顺利。

这是一个工作示例:

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) returns出现如下错误:

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 字段中的(因子)变量的名称.

插入符号 train() 函数的公式版本和非公式版本之间真的存在不一致吗?还是我遗漏了什么?

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

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

因此,当您使用 randomForest(y ~ ., data = dat) 时,randomForest 不会 创建虚拟变量,但 train (以及大多数其他人)将使用类似的调用train(y ~ ., data = dat).

错误发生是因为fuelType是一个因素。 train 创建的虚拟变量名称不同,因此 predict.randomForest 无法找到它们。

将非公式方法与 train 一起使用会将因子预测变量传递给 randomForest,一切都会起作用。

TL;DR

如果你想要相同的级别使用predict.train

,请使用train的非公式方法

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

1.训练集和测试集的分类变量类别不匹配。要检查这一点,您可以 运行 像下面这样的东西。

嗯,首先,将独立 variables/features 保留在列表中是一个很好的做法。假设该列表是 "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])))
  }  
}

一旦找到不匹配的分类变量,就可以返回,将测试数据的类别强加到训练数据上,然后重新构建模型。在类似于上面的循环中,对于每个 nonMatchingVar,您可以执行

levels(Test$nonMatchingVar) <- levels(Train$nonMatchingVar)

2. 一个傻瓜。如果您不小心将因变量留在自变量集中,您可能会 运行 进入此错误消息。我犯了那个错误。解决办法:小心点就好了。

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

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