Як обчислити площу під кривою (AUC) або c-статистику вручну


78

Мене цікавить розрахунок площі під кривою (AUC) або c-статистика вручну для двійкової логістичної регресійної моделі.

Наприклад, у наборі даних перевірки я маю справжнє значення для залежної змінної, утримання (1 = збережено; 0 = не збережено), а також передбачуваний статус утримання для кожного спостереження, згенерованого моїм регресійним аналізом, використовуючи модель, яка була побудований за допомогою навчального набору (це коливатиметься від 0 до 1).

Мої початкові думки полягали в тому, щоб визначити «правильну» кількість класифікацій моделей і просто розділити кількість «правильних» спостережень на кількість загальних спостережень для обчислення c-статистики. За "правильним", якщо справжній статус збереження спостереження = 1 і передбачуваний статус утримання становить> 0,5, то це "правильна" класифікація. Крім того, якщо справжній статус збереження спостереження = 0 і передбачуваний статус утримання становить <0,5, то це також "правильна" класифікація. Я припускаю, що "прив'язка" відбудеться, коли передбачуване значення = 0,5, але це явище не відбувається в моєму наборі даних перевірки. З іншого боку, "неправильними" класифікаціями було б, якщо справжній статус збереження спостереження = 1, а передбачуваний статус збереження - <0. 5 або якщо справжній статус збереження для результату = 0 і передбачуваний статус утримання становить> 0,5. Мені відомо про TP, FP, FN, TN, але не знаю, як обчислити c-статистику за цією інформацією.

Відповіді:


115

Я рекомендував би документ Hanley & McNeil 1982 року « Значення та використання області під кривою експлуатаційної характеристики приймача (ROC) ».

Приклад

Вони мають таку таблицю стану захворювання та результату тесту (відповідає, наприклад, оціночному ризику за логістичною моделлю). Перше число праворуч - це кількість пацієнтів із справжнім статусом захворювання "нормальним", а друге - кількість пацієнтів із справжнім статусом захворювання "ненормальним":

(1) Безумовно нормально: 33/3
(2) Напевно нормально: 6/2
(3) Сумнівно: 6/2
(4) Можливо, ненормально: 11/11
(5) Виразно аномальне: 2/33

Таким чином, загалом є 58 «нормальних» пацієнтів та «51» аномальних. Ми бачимо, що коли предиктор становить 1, "безумовно нормально", пацієнт зазвичай є нормальним (вірно для 33 з 36 пацієнтів), а коли 5, "безумовно, аномальний", пацієнти зазвичай ненормальні (вірно для 33 з 35 пацієнтів), тому прогноз має сенс. Але як нам судити про пацієнта з оцінкою 2, 3 або 4? Те, що ми встановили для того, щоб оцінити пацієнтів як ненормальне або нормальне, визначає чутливість та специфіку отриманого тесту.

Чутливість і специфічність

Ми можемо розрахувати оцінену чутливість та специфічність для різних обрізів. (Я просто напишу "чутливість" та "специфічність" відтепер, дозволяючи передбачуваній природі значень неявно.)

Якщо ми оберемо наше обмеження, щоб класифікувати всіх пацієнтів як аномальних, незалежно від того, що говорять результати їх тестування (тобто, ми обираємо відрізок 1+), ми отримаємо чутливість 51/51 = 1. Специфіка буде 0 / 58 = 0. Це не так добре.

Добре, тож давайте виберемо менш суворе обмеження. Ми класифікуємо пацієнтів як аномальних, лише якщо у них результат тесту 2 або вище. Тоді ми пропускаємо 3 ненормальних пацієнтів і чутливість до них 48/51 = 0,94. Але ми маємо значно підвищену специфіку - 33/58 = 0,57.

Тепер ми можемо продовжувати це, вибираючи різні обрізи (3, 4, 5,> 5). (В останньому випадку ми не будемо класифікувати жодних пацієнтів як ненормальних, навіть якщо вони мають найвищий можливий тестовий бал 5.)

Крива ROC

Якщо ми зробимо це для всіх можливих обрізів і побудуємо графік чутливості проти 1 мінус специфічності, отримаємо криву ROC. Ми можемо використовувати наступний код R:

# Data
norm     = rep(1:5, times=c(33,6,6,11,2))
abnorm   = rep(1:5, times=c(3,2,2,11,33))
testres  = c(abnorm,norm)
truestat = c(rep(1,length(abnorm)), rep(0,length(norm)))

# Summary table (Table I in the paper)
( tab=as.matrix(table(truestat, testres)) )

Вихід:

        testres
truestat  1  2  3  4  5
       0 33  6  6 11  2
       1  3  2  2 11 33

Ми можемо розрахувати різні статистичні дані:

( tot=colSums(tab) )                            # Number of patients w/ each test result
( truepos=unname(rev(cumsum(rev(tab[2,])))) )   # Number of true positives
( falsepos=unname(rev(cumsum(rev(tab[1,])))) )  # Number of false positives
( totpos=sum(tab[2,]) )                         # The total number of positives (one number)
( totneg=sum(tab[1,]) )                         # The total number of negatives (one number)
(sens=truepos/totpos)                           # Sensitivity (fraction true positives)
(omspec=falsepos/totneg)                        # 1 − specificity (false positives)
sens=c(sens,0); omspec=c(omspec,0)              # Numbers when we classify all as normal

І використовуючи це, ми можемо побудувати (оціночну) криву ROC:

plot(omspec, sens, type="b", xlim=c(0,1), ylim=c(0,1), lwd=2,
     xlab="1 − specificity", ylab="Sensitivity") # perhaps with xaxs="i"
grid()
abline(0,1, col="red", lty=2)

Крива AUC

Вручну обчислення AUC

Ми можемо дуже легко обчислити площу під кривою ROC, використовуючи формулу для площі трапеції:

height = (sens[-1]+sens[-length(sens)])/2
width = -diff(omspec) # = diff(rev(omspec))
sum(height*width)

Результат 0,8931711.

Погоджений захід

AUC також може розглядатися як відповідність. Якщо ми беремо всіх можливих пар пацієнтів, у яких один є нормальним, а другий - ненормальним, ми можемо обчислити, наскільки часто це аномальний, який має найвищий (найбільш "ненормальний вигляд") тест (якщо вони мають однакове значення, ми порахуйте, що це як «половина перемоги»):

o = outer(abnorm, norm, "-")
mean((o>0) + .5*(o==0))

Відповідь знову 0,8931711, площа під кривою ROC. Це завжди буде так.

Графічний вигляд узгодження

Як вказував Гаррелл у своїй відповіді, це також має графічну інтерпретацію. Давайте побудуємо тестовий бал (оцінка ризику) на y- вісі та справжньому статусі захворювання на x- ox (тут з деяким тремтінням, щоб показати точки перекриття):

plot(jitter(truestat,.2), jitter(testres,.8), las=1,
     xlab="True disease status", ylab="Test score")

Скетерний графік оцінки ризику щодо справжнього стану хвороби.

Давайте тепер проведемо лінію між кожною точкою зліва («нормальний» пацієнт) і кожною точкою праворуч («ненормальний» пацієнт). Частка ліній із позитивним нахилом (тобто частка співзвучних пар) є індексом конкордансу (плоскі лінії вважаються "50% конкордації").

Виявити фактичні лінії для цього прикладу трохи важко, через кількість зв’язків (однаковий показник ризику), але з деяким тремтінням та прозорістю ми можемо отримати розумний сюжет:

d = cbind(x_norm=0, x_abnorm=1, expand.grid(y_norm=norm, y_abnorm=abnorm))
library(ggplot2)
ggplot(d, aes(x=x_norm, xend=x_abnorm, y=y_norm, yend=y_abnorm)) +
  geom_segment(colour="#ff000006",
               position=position_jitter(width=0, height=.1)) +
  xlab("True disease status") + ylab("Test\nscore") +
  theme_light()  + theme(axis.title.y=element_text(angle=0))

Графік розсіювання оцінки ризику щодо справжнього стану хвороби з лініями між усіма можливими парами спостереження.

Ми бачимо, що більшість ліній нахиляються вгору, тому індекс узгодженості буде високим. Ми також бачимо внесок в індекс від кожного типу спостережної пари. Більшість припадає на нормальних пацієнтів з оцінкою ризику 1 у парі з аномальними пацієнтами з оцінкою ризику 5 (1–5 пар), але досить багато також від 1–4 пар та 4–5 пар. І дуже легко обчислити фактичний індекс узгодження на основі визначення схилу:

d = transform(d, slope=(y_norm-y_abnorm)/(x_norm-x_abnorm))
mean((d$slope > 0) + .5*(d$slope==0))

Відповідь знову 0,8931711, тобто AUC.

Тест Вілкоксона – Манна – Вітні

Існує тісний зв’язок між мірою узгодження і тестом Вілкоксона – Манна – Вітні. Насправді останній тестує, якщо ймовірність узгодження (тобто, що аномальний пацієнт у випадковій нормально-аномальній парі матиме найбільш тестовий результат "ненормального вигляду") рівно 0,5. А його тестова статистика - це просто просте перетворення оціненої ймовірності узгодження:

> ( wi = wilcox.test(abnorm,norm) )
    Wilcoxon rank sum test with continuity correction

data:  abnorm and norm
W = 2642, p-value = 1.944e-13
alternative hypothesis: true location shift is not equal to 0

Тестова статистика ( W = 2642) підраховує кількість супутніх пар. Якщо поділити його на кількість можливих пар, отримаємо фамільне число:

w = wi$statistic
w/(length(abnorm)*length(norm))

Так, це 0,8931711, площа під кривою ROC.

Найпростіші способи розрахунку AUC (в R)

Але давайте полегшимо собі життя. Існують різні пакети, які обчислюють AUC для нас автоматично.

Пакет Epi

EpiПакет створює хороший ROC кривої з різними статистичними даними (включаючи АУК) вбудовані:

library(Epi)
ROC(testres, truestat) # also try adding plot="sp"

Крива ROC з пакету Epi

Пакет pROC

Мені також подобається pROCпакет, оскільки він може згладити оцінку ROC (і обчислити оцінку AUC на основі згладженої ROC):

Крива ROC (не згладжена і згладжена) з пакету pROC

(Червона лінія є початковою ROC, а чорна лінія - згладженою ROC. Також врахуйте за замовчуванням співвідношення сторін 1: 1. Це має сенс використовувати це, оскільки і чутливість, і специфічність мають діапазон 0–1.)

Орієнтовний показник AUC від згладженого ROC становить 0,9107, схожий на, але трохи більший, ніж AUC від негладного ROC (якщо подивитися на рисунок, ви легко зрозумієте, чому він більший). (Хоча насправді маємо занадто мало можливих чітких значень результатів тесту, щоб обчислити рівний AUC).

Пакет rms

rmsПакет Харрелла може обчислювати різні пов'язані статистичні дані про узгодження за допомогою rcorr.cens()функції. У C Indexйого виході є AUC:

> library(rms)
> rcorr.cens(testres,truestat)[1]
  C Index 
0.8931711

Пакет caTools

Нарешті, ми маємо caToolsпакет та його colAUC()функції. Він має кілька переваг перед іншими пакетами (в основному швидкість і можливість роботи з багатовимірними даними - див. ?colAUC), Які іноді можуть бути корисними. Але, звичайно, це дає таку ж відповідь, як ми підраховували знову і знову:

library(caTools)
colAUC(testres, truestat, plotROC=TRUE)
             [,1]
0 vs. 1 0.8931711

Крива ROC з пакету caTools

Заключні слова

Багато людей, здається, думають, що AUC говорить нам, наскільки хорошим є тест. І деякі люди думають, що AUC - це ймовірність того, що тест правильно класифікує пацієнта. Це не так . Як видно з наведеного вище прикладу та розрахунків, AUC повідомляє нам щось про сімейство тестів, один тест на кожне можливе відсічення.

А AUC розраховується на основі скорочень, які ніколи не використовували б на практиці. Чому ми повинні дбати про чутливість та специфіку «безглуздих» значень обрізання? Все-таки саме на цьому AUC (частково) базується. (Звичайно, якщо AUC дуже близький до 1, майже кожен можливий тест матиме велику дискримінаційну силу, і всі ми будемо дуже раді.)

Паралельна інтерпретація AUC "випадкової нормально-ненормальної" є приємною (і може бути розширена, наприклад, до моделей виживання, де ми бачимо, чи є людина з найбільшою (відносною) небезпекою, яка вмирає найперше). Але ніхто ніколи не використовував би це на практиці. Це рідкісний випадок, коли хтось знає, що має одну здорову та одну хвору людину, не знає, яка людина хвора, і повинен вирішити, хто з них лікувати. (У будь-якому випадку, рішення є простим; ставитися до того, з яким найбільше оцінюється ризик.)

Тому я думаю, що вивчення фактичної кривої ROC буде кориснішим, ніж просто перегляд підсумкової міри AUC. І якщо ви використовуєте ROC разом із (оцінками) витрат на помилкові позитиви та помилкові негативи, а також базові ставки того, що ви вивчаєте, ви можете десь дістатися.

Також зауважте, що AUC вимірює лише дискримінацію , а не калібрування. Тобто він вимірює, чи можна розрізняти двох осіб (одну хвору та одну здорову), грунтуючись на оцінці ризику. З цього приводу він розглядає лише відносні величини ризику (або ранжирує, якщо ви хочете, пор. Тестування тесту Вілкоксона – Манна – Вітні), а не абсолютні, які вас повинні зацікавити. Наприклад, якщо розділити кожен ризик Оцініть з вашої логістичної моделі на 2, ви отримаєте абсолютно таку ж AUC (і ROC).

Оцінюючи модель ризику, калібрування також є дуже важливим. Щоб вивчити це, ви подивитесь на всіх пацієнтів з оцінкою ризику приблизно, наприклад, 0,7, і побачите, чи приблизно 70% з них насправді захворіли. Зробіть це для кожного можливого показника ризику (можливо, використовуючи якесь згладжування / локальну регресію). Накресліть результати, і ви отримаєте графічну міру калібрування .

Якщо є модель з обома хорошою калібруванням і хорошою дискримінацією, то ви починаєте мати хорошу модель. :)


8
Дякую, @Karl Ove Hufthammer, це найповніша відповідь, яку я коли-небудь отримував. Я особливо ціную ваш розділ "Заключні слова". Відмінна робота! Знову дякую!
Метт Райхенбах

Дуже дякую за детальну відповідь. Я працюю з набором даних, де Epi :: ROC () v2.2.6 переконаний, що AUC становить 1,62 (ні, це не менталістське дослідження), але згідно з ROC, я вважаю, що набагато більше, ніж у 0,56, що наведений вище код в.
BurninLeo

32

Погляньте на це питання: Розуміння кривої ROC

Ось як побудувати криву ROC (з цього питання):

Малювання кривої ROC

дано набір даних, оброблений вашим класифікатором класифікації

  • прикладайте рейтингові тестові приклади за зменшенням балів
  • (0,0)
  • x
    • x1/pos
    • x1/neg

posneg

Ви можете використовувати цю ідею для ручного обчислення AUC ROC, використовуючи наступний алгоритм:

auc = 0.0
height = 0.0

for each training example x_i, y_i
  if y_i = 1.0:
    height = height + tpr
  else 
    auc = auc + height * fpr

return auc

Ця приємна анімована картинка повинна зображати цей процес чіткіше

побудова кривої


1
Дякую @Alexey Grigorev, це чудовий візуальний і, ймовірно, виявиться корисним у майбутньому! +1
Метт Рейхенбах

1
Скажіть, будь ласка, трохи про "частки позитивних та негативних прикладів", ви маєте на увазі найменше значення одиниці двох осей?
Аллан Руїн

1
@Allan Ruin: posтут мається на увазі кількість позитивних даних. Скажімо, у вас є 20 точок даних, у яких 11 балів - 1. Отже, при малюванні діаграми у нас є прямокутник 11х9 (висота х ширина). Олексій Григорьов зробив масштаб, але просто нехай так, як вам подобається. Тепер просто перемістіть 1 на графіку на кожному кроці.
Catbuilts

5

Пост Карла має багато чудової інформації. Але я ще не бачив за останні 20 років прикладу кривої ROC, яка змінила чиєсь мислення в хорошому напрямку. На мою скромну думку, єдине значення кривої ROC полягає в тому, що її площа дорівнює дуже корисній вірогідності узгодження. Крива ROC сама спокушує читача використовувати обрізи, що є поганою статистичною практикою.

cY=0,1xY=1yY=0Y=1

n

Для функції Hmiscпакету R rcorr.censнадрукуйте весь результат, щоб побачити більше інформації, особливо стандартну помилку.


Дякую, @Frank Harell, я ціную твою точку зору. Я просто використовую c-статистику як імовірність узгодження, оскільки мені не подобаються відсічки. Знову дякую!
Метт Райхенбах

4

Ось альтернатива природному способу обчислення AUC шляхом простого використання трапецієподібного правила для отримання площі під кривою ROC.

AUC дорівнює ймовірності того, що випадкове вибіркове позитивне спостереження має прогнозовану ймовірність (бути позитивною) більшою, ніж випадкове вибіркове негативне спостереження. Ви можете використовувати це для обчислення AUC досить легко в будь-якій мові програмування, провівши всі парні комбінації позитивних і негативних спостережень. Ви також можете випадково відібрати спостереження, якщо розмір вибірки був занадто великим. Якщо ви хочете обчислити AUC за допомогою ручки та паперу, це може бути не найкращим підходом, якщо у вас дуже невеликий зразок / багато часу. Наприклад в R:

n <- 100L

x1 <- rnorm(n, 2.0, 0.5)
x2 <- rnorm(n, -1.0, 2)
y <- rbinom(n, 1L, plogis(-0.4 + 0.5 * x1 + 0.1 * x2))

mod <- glm(y ~ x1 + x2, "binomial")

probs <- predict(mod, type = "response")

combinations <- expand.grid(positiveProbs = probs[y == 1L], 
        negativeProbs = probs[y == 0L])

mean(combinations$positiveProbs > combinations$negativeProbs)
[1] 0.628723

Ми можемо підтвердити, використовуючи pROCпакет:

library(pROC)
auc(y, probs)
Area under the curve: 0.6287

Використання випадкової вибірки:

mean(sample(probs[y == 1L], 100000L, TRUE) > sample(probs[y == 0L], 100000L, TRUE))
[1] 0.62896

1
  1. Ви маєте справжнє значення для спостережень.
  2. Обчисліть задню ймовірність, а потім оцініть спостереження за цією ймовірністю.
  3. PN
    Sum of true ranks0.5PN(PN+1)PN(NPN)

1
@ user73455 ... 1) Так, я маю справжнє значення для спостережень. 2) Чи задня ймовірність є синонімом прогнозованих ймовірностей для кожного із спостережень? 3) зрозуміла; однак, що таке "сума справжніх рангів" і як можна обчислити це значення? Можливо, приклад допоможе вам пояснити цю відповідь більш ретельно? Дякую!
Метт Райхенбах
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.