我正在阅读this Rnews document from June 2004,文章程序员的利基从33页提出了一种绘制接收器工作特性曲线和优化的方法。
第一个代码片段很简单,并且与定义一致
drawROC.A <- function(T, D) {
cutpoints <- c(-Inf, sort(unique(T)), Inf)
sens <- sapply(cutpoints,
function(c) sum(D[T>c])/sum(D))
spec <- sapply(cutpoints,
function(c) sum((1-D)[T<=c]/sum(1-D)))
plot(1-spec, sens, type = "l")
}
然后作者说(我做了些小修改)
有一个相对简单的函数优化
大幅提高速度,但代价是需要
T
作为一个数字,而不仅仅是一个
>
和<=
的对象定义
drawROC.B <- function(T, D){
DD <- table(-T, D)
sens <- cumsum(DD[ ,2]) / sum(DD[ ,2])
mspec <- cumsum(DD[ ,1]) / sum(DD[ ,1])
plot(mspec, sens, type="l")
}
我花了相当长的时间阅读优化版本,但被困在第一行:它看起来像是在
-
之前的负号T
被用来以相反的顺序执行累积和,但为什么?困惑的是,我把两个函数产生的ROC绘制在一起,检查结果是否相同。
左图由
drawROC.A
生成,右图是drawROC.B
的结果。乍一看,它们并不完全相同,但是如果你仔细观察,Y轴的范围是不同的,所以它们实际上是相同的情节。编辑:
现在我已经理解了
drawROC.B
的结果是正确的(见下面的答案),但是我仍然不知道实质性的性能提升来自哪里…… 最佳答案
我想我已经弄明白了。DD <- table(-T, D)
的意思是以相反的顺序执行累积和,这是因为我们正在计算Pr(T>c),而表的累积和正在计算T中小于或等于当前元素的元素数。
换句话说,这也会起作用,因为pr(t>c)=1-pr(t
drawROC.B <- function(T, D){
DD <- table(T, D)
sens <- 1 - cumsum(DD[ ,2])/sum(DD[ ,2])
mspec <- 1 - cumsum(DD[ ,1])/sum(DD[ ,1])
plot(mspec, sens, type="l")
}
顺便说一下,您可以使用它将两个点(0,0)和(1,1)添加到
drawROC.B
的结果中。drawROC.C <- function(T, D){
DD <- table(-T, D)
sens <- c(0, cumsum(DD[ ,2])/sum(DD[ ,2]), 1)
mspec <- c(0, cumsum(DD[ ,1])/sum(DD[ ,1]), 1)
plot(mspec, sens, type="l")
}
至于性能增益,请注意
drawROC.A
需要执行(渐近地)unique(T) * length(T)
比较,而drawROC.A
只需要length(T)
操作来构建表,并且所有后续操作都同样昂贵。