我一直很难理解S3方法的调用文档,这次它又咬了我一口。
我要提前道歉,因为我有超过一个问题,但它们都密切相关。在一组复杂的函数中,我创建了许多glmnet
拟合,特别是逻辑回归。现在,glmnet
文档指定返回值具有两个类别:“glmnet”和(对于逻辑回归)“lognet”。实际上,它们按照此顺序指定。
然而,在查看glmnet
实现末尾时,就在调用(内部函数)lognet
后面,将fit
的类设置为“lognet”,我在返回之前看到了这行代码:
class(fit) = c(class(fit), "glmnet")
根据这个,我可以得出结论,类的顺序实际上是"lognet"、"glmnet"。
不幸的是,我的拟合结果(如文档所建议):
> class(myfit)
[1] "glmnet" "lognet"
这种方法的问题在于S3方法的分派,特别是
predict
。这是predict.lognet
的代码:function (object, newx, s = NULL, type = c("link", "response",
"coefficients", "class", "nonzero"), exact = FALSE, offset,
...)
{
type = match.arg(type)
nfit = NextMethod("predict") #<- supposed to call predict.glmnet, I think
switch(type, response = {
pp = exp(-nfit)
1/(1 + pp)
}, class = ifelse(nfit > 0, 2, 1), nfit)
}
我已经添加了一条评论来解释我的推理。现在,当我使用新的datamatrix mydata
和type="response"
调用此myfit
的预测时,就像这样:
predict(myfit, newx=mydata, type="response")
根据文档,我没有得到预测的概率值,而是得到了线性组合,这正是立即调用 predict.glmnet
的结果。
我尝试过颠倒类别的顺序,像这样:
orgclass<-class(myfit)
class(myfit)<-rev(orgclass)
然后再次执行预测调用:喜闻乐见:它可以工作!我得到了概率。
所以,这里有一些问题:
- 我对“学习”S3方法会按类的出现顺序分配的理解正确吗?
- 如果我假设
glmnet
中的代码会导致predict
的正确派发顺序错误,那么我是正确的吗? - 在我的代码中,没有明显/可见地操作类。 什么可能导致顺序改变?
library(glmnet)
y<-factor(sample(2, 100, replace=TRUE))
xs<-matrix(runif(100), ncol=1)
colnames(xs)<-"x"
myfit<-glmnet(xs, y, family="binomial")
mydata<-matrix(runif(10), ncol=1)
colnames(mydata)<-"x"
class(myfit)
predict(myfit, newx=mydata, type="response")
class(myfit)<-rev(class(myfit))
class(myfit)
predict(myfit, newx=mydata, type="response")
class(myfit)<-rev(class(myfit))#set it back
class(myfit)
根据生成的数据,差异或多或少明显(在我的真实数据集中,我注意到所谓概率值为负数,这就是我发现问题的方式),但确实应该看到差异。感谢任何意见。
编辑:我刚刚发现可怕的真相:glmnet 1.5.2 中的任何顺序都可以使用(该版本存在于我运行实际代码的服务器上,导致分类顺序颠倒的拟合),但是从1.6开始需要按照“lognet”、“glmnet”的顺序进行排序。我还没有检查在1.7中会发生什么。
感谢@Aaron提醒我信息学基础知识(除了“如果一切失败,请重新启动”之外,“检查您的版本”),我错误地认为由统计学习之神创建的程序包将受到此类错误的保护,以及感谢@Gavin确认了我对S3工作方式的重建。