UseMethod("predict"):对于"class = train"的对象没有可适用的"predict"方法。

16

我有一个基于上个月的历史信息训练出来的模型(fit)。现在我想要使用这个模型预测本月的情况。当我尝试调用以下代码时:

predicted <- predict(fit, testData[-$Readmit])

我收到了以下错误:

Error in UseMethod("predict") : no applicable method for 'predict'
    applied to an object of class "train"

注:

  1. 拟合的模型是通过caret包中的train函数使用随机森林算法创建的。
  2. predict是一个通用函数,它将根据第一个输入参数调用特定的预测函数。在我的情况下,它将是:

    >fit$modelInfo$label

    [1] "Random Forest"

因此,调用的预测方法将是:predict.randomForest。有关更多信息,请参见[caret文档][3]。

这里是生成模型并调用模型的摘要源代码:

# Script-1: create a model:
fit <- train(testData[-$Readmit], testData$Readmit)
saveRDS(fit, modelFileName) # save the fit object into a file

# Script-2: predict
fit <- readRDS(modelFileName) # Load the model (generated previously)
predicted <- predict(fit, testData[-$Readmit])

注意: 生成模型的执行时间大约为3小时,这就是为什么我在那之后保存对象以便重复使用。

训练模型的数据集具有以下结构:

> str(fit$trainingData)
'data.frame':   29955 obs. of  27 variables:
$ Acuity                : Factor w/ 3 levels "Elective  ","Emergency ",..: 2 2 2 1 1 2 2 2 1 1 ...
$ AgeGroup              : Factor w/ 10 levels "100-105","65-70",..: 8 6 9 9 5 4 9 2 3 2 ...
$ IsPriority            : int  0 0 0 0 0 0 0 0 0 0 ...
$ QNXTReferToId         : int  115 1703712 115 3690 1948 115 109 512 481 1785596 ...
$ QNXTReferFromId       : int  1740397 1724801 1711465 1704170 1714272 1731911 1535 1712758 1740614 1760252 ...
$ iscasemanagement      : Factor w/ 2 levels "N","Y": 2 1 1 2 2 1 2 1 2 2 ...
$ iseligible            : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 2 2 2 2 ...
$ referralservicecode   : Factor w/ 11 levels "12345","278",..: 1 1 1 9 9 1 1 6 9 9 ...
$ IsHighlight           : Factor w/ 2 levels "N","Y": 1 1 1 1 1 1 1 1 1 1 ...
$ admittingdiagnosiscode: num  439 786 785 786 428 ...
$ dischargediagnosiscode: num  439 0 296 786 428 ...
$ RealLengthOfStay      : int  3 1 6 1 2 3 3 7 3 2 ...
$ QNXTPCPId             : int  1740397 1724801 1711465 1704170 1714272 1731911 1535 1712758 1740614 1760252 ...
$ QNXTProgramId         : Factor w/ 3 levels "QMXHPQ0839     ",..: 1 1 1 1 1 1 1 1 1 1 ...
$ physicalzipcode       : int  33054 33712 33010 33809 33010 33013 33142 33030 33161 33055 ...
$ gender                : Factor w/ 2 levels "F","M": 1 1 1 1 2 1 1 2 2 1 ...
$ ethnicitycode         : Factor w/ 4 levels "ETHN0001       ",..: 4 4 4 4 4 4 4 4 4 4 ...
$ dx1                   : num  439 786 296 786 428 ...
$ dx2                   : num  439 292 785 786 428 ...
$ dx3                   : num  402 0 250 0 0 ...
$ svc1                  : int  0 120 120 762 762 120 120 120 762 762 ...
$ svc2                  : int  120 0 0 0 0 0 0 0 0 0 ...
$ svc3                  : int  0 0 0 0 0 0 0 0 0 0 ...
$ Disposition           : Factor w/ 28 levels "0","APPEAL & GRIEVANCE REVIEW                                   ",..: 11 11 16 11 11 11 11 11 11 11 ...
$ AvgIncome             : Factor w/ 10 levels "-1",">100k","0-25k",..: 3 6 3 8 3 4 3 5 4 4 ...
$ CaseManagerNameID     : int  124 1 1 19 20 1 16 1 43 20 ...
$ .outcome              : Factor w/ 2 levels "NO","YES": 1 2 2 1 1 1 2 2 1 1    ...

现在testData的结构将如下所示:

> str(testData[-$Readmit])
'data.frame':   610 obs. of  26 variables:
$ Acuity                : Factor w/ 4 levels "0","Elective  ",..: 3 2 4 2 2 2 4 3 3 3 ...
$ AgeGroup              : Factor w/ 9 levels "100-105","65-70",..: 4 3 5 4 2 9 4 2 4 6 ...
$ IsPriority            : int  0 0 0 0 0 0 1 1 1 1 ...
$ QNXTReferToId         : int  2140 482 1703785 1941 114 1714905 1703785 98 109 109 ...
$ QNXTReferFromId       : int  1791383 1729375 1718532 1746336 1718267 1718267 1718532 98 109 109 ...
$ iscasemanagement      : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 1 2 2 1 ...
$ iseligible            : Factor w/ 2 levels "N","Y": 2 2 2 2 2 2 2 2 2 2 ...
$ referralservicecode   : Factor w/ 7 levels "12345","IPMAT          ",..: 5 1 1 1 1 1 1 5 1 5 ...
$ IsHighlight           : Factor w/ 2 levels "N","Y": 1 1 1 1 1 1 1 1 1 1 ...
$ admittingdiagnosiscode: num  11440 11317 11420 11317 1361 ...
$ dischargediagnosiscode: num  11440 11317 11420 11317 1361 ...
$ RealLengthOfStay      : int  1 2 4 3 1 1 16 1 1 3 ...
$ QNXTPCPId             : int  3212 1713678 1738430 1713671 1720569 1791640 1725962 1148 1703290 1705009 ...
$ QNXTProgramId         : Factor w/ 2 levels "QMXHPQ0839     ",..: 1 1 1 1 1 1 1 1 1 1 ...
$ physicalzipcode       : int  34744 33175 33844 33178 33010 33010 33897 33126 33127 33125 ...
$ gender                : Factor w/ 2 levels "F","M": 2 1 2 1 2 2 2 1 1 2 ...
$ ethnicitycode         : Factor w/ 1 level "No Ethnicity   ": 1 1 1 1 1 1 1 1 1 1 ...
$ dx1                   : num  11440 11317 11420 11317 1361 ...
$ dx2                   : num  11440 11317 11420 11317 1361 ...
$ dx3                   : num  0 1465 0 11326 0 ...
$ svc1                  : int  52648 27447 50040 27447 55866 55866 51595 0 99221 300616 ...
$ svc2                  : int  76872 120 50391 120 120 38571 120 762 120 0 ...
$ svc3                  : int  762 0 120 0 0 51999 0 0 0 762 ...
$ Disposition           : Factor w/ 14 levels "0","DENIED- Not Medically Necessary                             ",..: 3 5 3 4 3 3 5 3 3 5 ...
$ AvgIncome             : Factor w/ 10 levels "-1",">100k","0-25k",..: 6 7 5 9 3 3 6 4 3 4 ...
$ CaseManagerNameID     : int  1 2 3 4 5 6 7 8 9 7 ...

变量结构相同,只是由于某些变量具有新值,因此某些因素变量的级别不同。例如:模型中的 Acuity 有3个级别,在测试数据中有4个级别。

我没有一种事先知道所有变量可能级别的方法。

请给予任何建议...

提前感谢,

大卫


1
train 不是 R 函数。您可以像这样阅读其文档 ?library_you_got_it_from::train。它们可能会在那里提到它是否具有 predict 方法。 - Frank
1
这是来自于 caret 包吗? - liori
summary(fit) 给你的结果是否合理? - abhiieor
我根据之前(@loiri @Frank和@abhiieor)的评论在原帖中添加了更多细节。@abhiieor,str(fit)的输出提供了太多信息,我通过fit$trainingData获取了训练数据结构。唯一不同的是,我保存了变量,然后加载它,测试集来自一个新文件(它不是训练集的一部分),但具有相同的数据结构(但不可能所有值或级别都相同)。我不知道这是否与我的问题有关。谢谢。 - David Leal
1
请注意,评论中只有第一个 @name 会被提醒。 - Frank
我忘记在源代码中添加caret库。这解决了原始错误并生成了以下内容:Error in predict.randomForest(modelFit, newdata, type = "prob") : New factor levels not present in the training data这指向我与级别相关的嫌疑。现在在Stackoverflow上查看有关此新错误的信息,我已经找到了这个链接作为可能的解决方案。 - David Leal
3个回答

15

我想我找到了为什么这种情况发生的原因......predict是从stats包中的一个通用函数。我使用命名空间::来调用来自caret包的函数(这是创建用户包的推荐方式),而等价的caret包中的predict函数是predict.train,它是一个内部函数,不能被外部应用程序调用。唯一调用此函数的方法是使用stats包中的通用predict函数,然后根据第一个输入参数的类别:predicted <- predict(fit, testData[-$Readmit]),它会识别要调用的特定predict函数。

对于这个特定的函数,类别是train,所以实际上它将调用caret包中的train.predict函数。这个函数还处理基于使用的算法(方法)预测请求的特定函数,例如:predict.gbmpredict.glm等等。在caret文档的“5.7提取预测和类概率”部分中详细解释了这一点。

因此,::符号对于包中的其他函数(例如:caret.train)有效,但对于这个特定的函数(即predict)则无效。在这种情况下,需要显式加载库,以便它内部可以调用predict.train函数。

简而言之,在调用predict函数之前,解决方案就是添加以下行:

library(caret)

然后错误消失了。


1
在我使用caret但没有使用biglm包时,我也遇到了同样的问题,所以加1。 - YCR

8

根据@David Leal的回答,我尝试在调用预测函数之前加载library(caret),但没有帮助。

经过一番尝试,我意识到我必须加载包含模型本身的库。在我的情况下,我需要调用library(kenlab)来支持向量。


1
在解决类似问题时,我遇到了这个线程,并想要添加我的解决方案。特别是,上面的答案涉及在包内调用library(),这是强烈不建议的,因为它可能会无意中改变用户的库搜索路径并导致命名空间冲突。我的答案可能是针对特定情况的,但它们可以在不需要library()require()的情况下工作。
我的目标是创建一个包,允许用户使用我已经创建的模型从自己的数据中生成预测结果。这些模型将被硬编码到包中。它们包括基于tidymodels包创建的基于kknn的k最近邻模型和基于rpart的决策树模型。
从最简单的rpart树模型开始,当尝试运行stats::predict(hla_tree$model, new_data = df)时,我最初收到了与OP相同的错误。
Error in UseMethod("predict") : no applicable method for 'predict'
    applied to an object of class c("_rpart", "model_fit")

幸运的是,在parsnip(与tidymodels相关的部分)中,predict.model_fit是一个已导出的对象,所以只需将该行更改为parsnip::predict.model_fit(hla_tree$model, new_data = df)即可进行更正。


kknn k最近邻模型更具挑战性。在克服上述初始错误后,我会得到不同的错误。

Error in get(ctr, mode = "function", envir = parent.frame()): object 'contr.dummy' of mode 'function' was not found

这个错误似乎是与kknn包和如何在kknn()中引用contr.dummy函数作为引用对象有关。我最好的猜测是基于从包的kknn命名空间中加载函数的顺序。在包内部,对kknn()的调用将在contr.dummy()被加载到命名空间之前引用contr.dummy,导致调用kknn()时出现错误。在包外部,library(kknn)会在开始时将contr.dummy()添加到名称空间中,因此kknn()将成功。

幸运的是,这已经在待处理的kknn拉取请求中得到解决。因此,我接下来采取了以下步骤:

  • DESCRIPTION文件中添加了针对pull-request的特定软件包:usethis::use_dev_package("kknn", remote = "https://github.com/KlausVigo/kknn#24")
  • 在我的函数的roxygen注释中添加了@import kknn
  • 将相关的预测代码更改为stats::predict(knn_model$fit, df)

完成这些步骤后,软件包通过了所有的devtools::check()测试,并且似乎按照预期工作。


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