这种对 ROC 绘图的优化背后的基本原理是什么?

What's the rationale behind this optimization to ROC plotting?

我正在阅读 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")
}

然后作者说(我稍作修改),

There is a relatively simple optimization of the function that increases the speed substantially, though at the cost of requiring T to be a number, rather than just an object for which > and <= are defined

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轴的范围不一样,其实是同一个plot

编辑:

现在我明白了 drawROC.B 的结果是正确的(见下面我的回答),但我仍然不知道显着的性能提升来自哪里...

我想我已经弄明白了。 DD <- table(-T, D) 的意思是逆序求和,这是因为我们计算的是 Pr(T > c),而 table 的求和是计算 T 中元素的个数小于或等于当前元素。

换句话说,这也行得通,因为 Pr(T > c) = 1 - Pr(T <= c).

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) 操作来构建 table 并且所有后续操作的成本都一样高。