В чем смысл этой оптимизации для построения ROC? - PullRequest
2 голосов
/ 15 марта 2019

Я читаю этот документ Rnews за июнь 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, созданный двумя функциями вместе, чтобы проверить, совпадают ли результаты.

enter image description here

Левый график получается drawROC.A, тогда как правый - результат drawROC.B.На первый взгляд, они не идентичны, но если присмотреться, диапазон оси Y отличается, поэтому они на самом деле представляют собой один и тот же график.

Редактировать:

Теперь я понял, что результат drawROC.B правильный (см. Мой ответ ниже), но я до сих пор не представляю, откуда происходит существенное повышение производительности ...

1 Ответ

0 голосов
/ 15 марта 2019

Я думаю, я понял это. DD <- table(-T, D) предназначен для выполнения кумулятивных сумм в обратном порядке, и это потому, что мы вычисляем Pr (T> c), тогда как кумулятивные суммы таблицы подсчитывают количество элементов в T, которые меньше или равны текущий элемент.

Другими словами, это также будет работать, потому что Pr (T> c) = 1 - Pr (T <= c). </p>

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) операций для построения таблицы, и все последующие операции являются такими же дорогостоящими.

...