Cara menghitung Area Di Bawah Kurva (AUC), atau statistik-c, dengan tangan

78

Saya tertarik menghitung area di bawah kurva (AUC), atau c-statistik, dengan tangan untuk model regresi logistik biner.

Misalnya, dalam dataset validasi, saya memiliki nilai sebenarnya untuk variabel dependen, retensi (1 = dipertahankan; 0 = tidak dipertahankan), serta status retensi yang diprediksi untuk setiap pengamatan yang dihasilkan oleh analisis regresi saya menggunakan model yang dibangun menggunakan set pelatihan (ini akan berkisar dari 0 hingga 1).

Pikiran awal saya adalah untuk mengidentifikasi jumlah "benar" klasifikasi model dan hanya membagi jumlah pengamatan "benar" dengan jumlah total pengamatan untuk menghitung statistik-c. Dengan "benar", jika status retensi sebenarnya dari pengamatan = 1 dan status retensi yang diprediksi adalah> 0,5 maka itu adalah klasifikasi "benar". Selain itu, jika status retensi sebenarnya dari pengamatan = 0 dan status retensi yang diprediksi adalah <0,5 maka itu juga merupakan klasifikasi "benar". Saya berasumsi "dasi" akan terjadi ketika nilai prediksi = 0,5, tetapi fenomena itu tidak terjadi dalam dataset validasi saya. Di sisi lain, klasifikasi "salah" akan menjadi jika status retensi sebenarnya dari pengamatan = 1 dan status retensi yang diprediksi adalah <0. 5 atau jika status retensi sebenarnya untuk hasil = 0 dan status retensi yang diprediksi adalah> 0,5. Saya mengetahui TP, FP, FN, TN, tetapi tidak mengetahui bagaimana menghitung c-statistik yang diberikan informasi ini.

Matt Reichenbach
sumber

Jawaban:

115

Saya akan merekomendasikan makalah Hanley & McNeil 1982 ' Makna dan penggunaan area di bawah kurva karakteristik operasi penerima (ROC) '.

Contoh

Mereka memiliki tabel status penyakit dan hasil tes berikut (sesuai dengan, misalnya, perkiraan risiko dari model logistik). Angka pertama di sebelah kanan adalah jumlah pasien dengan status penyakit sebenarnya 'normal' dan nomor kedua adalah jumlah pasien dengan status penyakit sebenarnya 'tidak normal':

(1) Pasti normal: 33/3
(2) Mungkin normal: 6/2
(3) dipertanyakan: 6/2
(4) Mungkin tidak normal: 11/11
(5) Jelas tidak normal: 2/33

Jadi ada total 58 pasien 'normal' dan '51' yang abnormal. Kita melihat bahwa ketika prediktornya 1, 'Pasti normal', pasien biasanya normal (berlaku untuk 33 dari 36 pasien), dan ketika 5, 'Pasti abnormal' pasien biasanya abnormal (benar untuk 33 pasien). 35 pasien), sehingga prediktor masuk akal. Tetapi bagaimana kita menilai pasien dengan skor 2, 3, atau 4? Apa yang kami tetapkan untuk menilai pasien sebagai tidak normal atau normal untuk menentukan sensitivitas dan spesifisitas tes yang dihasilkan.

Sensitivitas dan spesifisitas

Kita dapat menghitung estimasi sensitivitas dan spesifisitas untuk cutoff yang berbeda. (Saya hanya akan menulis 'sensitivitas' dan 'spesifisitas' mulai sekarang, membiarkan sifat estimasi nilai menjadi tersirat.)

Jika kami memilih cutoff kami sehingga kami mengklasifikasikan semua pasien sebagai tidak normal, tidak peduli apa hasil tes mereka (yaitu, kami memilih cutoff 1+), kami akan mendapatkan sensitivitas 51/51 = 1. Spesifisitasnya adalah 0 / 58 = 0. Tidak terdengar bagus.

OK, jadi mari kita pilih cutoff yang tidak terlalu ketat. Kami hanya mengklasifikasikan pasien sebagai abnormal jika mereka memiliki hasil tes 2 atau lebih tinggi. Kami kemudian kehilangan 3 pasien abnormal, dan memiliki sensitivitas 48/51 = 0,94. Tetapi kami memiliki spesifisitas yang jauh meningkat, dari 33/58 = 0,57.

Kita sekarang dapat melanjutkan ini, memilih berbagai cutoff (3, 4, 5,> 5). (Dalam kasus terakhir, kita tidak akan mengklasifikasikan setiap pasien sebagai abnormal, bahkan jika mereka memiliki kemungkinan nilai tes tertinggi 5.)

Kurva ROC

Jika kita melakukan ini untuk semua kemungkinan cutoff, dan plot sensitivitas terhadap 1 dikurangi spesifisitas, kita mendapatkan kurva ROC. Kita dapat menggunakan kode R berikut:

# 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)) )

Outputnya adalah:

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

Kami dapat menghitung berbagai statistik:

( 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

Dan menggunakan ini, kita dapat memplot kurva ROC (diperkirakan):

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)

Kurva AUC

Secara manual menghitung AUC

Kami dapat dengan mudah menghitung area di bawah kurva ROC, menggunakan rumus untuk area trapesium:

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

Hasilnya adalah 0,8931711.

Ukuran konkordansi

AUC juga dapat dilihat sebagai ukuran konkordansi. Jika kita mengambil semua pasangan pasien yang mungkin di mana satu normal dan yang lain abnormal, kita dapat menghitung seberapa sering itu abnormal yang memiliki hasil tes tertinggi (paling 'kelainan') (jika mereka memiliki nilai yang sama, kami hitung ini sebagai 'setengah kemenangan'):

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

Jawabannya adalah lagi 0,8931711, area di bawah kurva ROC. Ini akan selalu menjadi masalah.

Tampilan grafis konkordansi

Seperti yang ditunjukkan oleh Harrell dalam jawabannya, ini juga memiliki interpretasi grafis. Mari kita plot skor tes (estimasi risiko) pada y- sumbu dan status penyakit sebenarnya pada x- sumbu (di sini dengan beberapa jittering, untuk menunjukkan poin yang tumpang tindih):

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

Sebarkan skor skor risiko terhadap status penyakit sebenarnya.

Mari kita menggambar garis antara setiap titik di sebelah kiri (pasien 'normal') dan setiap titik di sebelah kanan (pasien 'abnormal'). Proporsi garis dengan kemiringan positif (yaitu proporsi pasangan konkordan ) adalah indeks konkordansi (garis datar dihitung sebagai 'konkordansi 50%').

Agak sulit untuk memvisualisasikan garis sebenarnya untuk contoh ini, karena jumlah ikatan (skor risiko yang sama), tetapi dengan sedikit jittering dan transparansi kita bisa mendapatkan plot yang masuk akal:

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))

Sebaran plot skor risiko terhadap status penyakit sebenarnya, dengan garis di antara semua pasangan pengamatan yang memungkinkan.

Kita melihat bahwa sebagian besar garis miring ke atas, sehingga indeks kesesuaian akan tinggi. Kami juga melihat kontribusi terhadap indeks dari setiap jenis pasangan pengamatan. Sebagian besar berasal dari pasien normal dengan skor risiko 1 berpasangan dengan pasien abnormal dengan skor risiko 5 (1-5 pasang), tetapi cukup banyak juga berasal dari 1-4 pasang dan 4-5 pasang. Dan sangat mudah untuk menghitung indeks konkordansi aktual berdasarkan definisi kemiringan:

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

Jawabannya lagi 0,8931711, yaitu, AUC.

Tes Wilcoxon – Mann-Whitney

Ada hubungan erat antara ukuran konkordansi dan uji Wilcoxon-Mann-Whitney. Sebenarnya, tes kedua jika probabilitas konkordansi (yaitu, bahwa itu pasien yang abnormal dalam random pasangan yang normal-normal yang akan memiliki paling 'normal-cari' hasil tes) adalah persis 0,5. Dan statistik pengujiannya hanyalah transformasi sederhana dari perkiraan probabilitas konkordansi:

> ( 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

Statistik uji ( W = 2642) menghitung jumlah pasangan yang sesuai. Jika kita membaginya dengan jumlah pasangan yang memungkinkan, kita mendapatkan nomor familar:

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

Ya, ini 0,8931711, area di bawah kurva ROC.

Cara-cara yang lebih mudah untuk menghitung AUC (dalam R)

Tapi mari kita buat hidup lebih mudah untuk diri kita sendiri. Ada berbagai paket yang menghitung AUC untuk kita secara otomatis.

Paket Epi

The Epipaket menciptakan kurva ROC bagus dengan berbagai statistik (termasuk AUC) tertanam:

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

Kurva ROC dari paket Epi

Paket pROC

Saya juga suka pROCpaketnya, karena bisa memperlancar estimasi ROC (dan menghitung estimasi AUC berdasarkan ROC yang dihaluskan):

Kurva ROC (unsmoothed dan smoothed) dari paket pROC

(Garis merah adalah ROC asli, dan garis hitam adalah ROC yang diperhalus. Juga perhatikan rasio aspek 1: 1 standar. Masuk akal untuk menggunakan ini, karena baik sensitivitas dan spesifisitas memiliki rentang 0-1.)

Taksiran AUC dari ROC yang dihaluskan adalah 0,9107, mirip dengan, tetapi sedikit lebih besar dari, AUC dari ROC yang tidak digerakkan (jika Anda melihat gambar, Anda dapat dengan mudah melihat mengapa itu lebih besar). (Meskipun kami benar-benar memiliki terlalu sedikit kemungkinan nilai hasil tes yang berbeda untuk menghitung AUC yang lancar).

Paket rms

rmsPaket Harrell dapat menghitung berbagai statistik konkordansi terkait menggunakan rcorr.cens()fungsi. The C Indexoutput-nya adalah AUC:

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

Paket caTools

Akhirnya, kami memiliki caToolspaket dan colAUC()fungsinya. Ini memiliki beberapa keunggulan dibandingkan paket lain (terutama kecepatan dan kemampuan untuk bekerja dengan data multi-dimensi - lihat ?colAUC) yang terkadang dapat membantu. Tetapi tentu saja itu memberikan jawaban yang sama seperti yang telah kita hitung berulang-ulang:

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

Kurva ROC dari paket caTools

Kata-kata terakhir

Banyak orang tampaknya berpikir bahwa AUC memberi tahu kita seberapa 'baik' suatu tes. Dan beberapa orang berpikir bahwa AUC adalah probabilitas bahwa tes akan mengklasifikasikan pasien dengan benar. Hal ini tidak . Seperti yang dapat Anda lihat dari contoh dan perhitungan di atas, AUC memberi tahu kami sesuatu tentang keluarga tes, satu tes untuk setiap kemungkinan cutoff.

Dan AUC dihitung berdasarkan cutoff yang tidak akan pernah digunakan dalam praktik. Mengapa kita harus peduli dengan sensitivitas dan spesifisitas dari nilai batas 'tidak masuk akal'? Namun, itulah yang menjadi dasar AUC (sebagian). (Tentu saja, jika AUC sangat dekat dengan 1, hampir setiap tes yang memungkinkan akan memiliki kekuatan diskriminatif yang besar, dan kita semua akan sangat bahagia.)

Interpretasi pasangan 'acak normal-abnormal' dari AUC bagus (dan dapat diperpanjang, misalnya untuk model bertahan hidup, di mana kita melihat apakah itu orang dengan bahaya tertinggi (relatif) yang meninggal paling awal). Tetapi seseorang tidak akan pernah menggunakannya dalam praktik. Ini adalah kasus yang jarang terjadi di mana seseorang tahu seseorang memiliki satu orang sehat dan satu orang sakit, tidak tahu orang mana yang sakit, dan harus memutuskan yang mana dari mereka untuk dirawat. (Bagaimanapun, keputusan itu mudah; perlakukan orang yang dengan risiko tertinggi yang diperkirakan.)

Jadi saya pikir mempelajari kurva ROC yang sebenarnya akan lebih berguna daripada hanya melihat ukuran ringkasan AUC. Dan jika Anda menggunakan ROC bersama dengan (perkiraan) biaya positif palsu dan negatif palsu, bersama dengan tarif dasar dari apa yang Anda pelajari, Anda bisa mendapatkan tempat.

Perhatikan juga bahwa AUC hanya mengukur diskriminasi , bukan kalibrasi. Artinya, ini mengukur apakah Anda dapat membedakan antara dua orang (satu sakit dan satu sehat), berdasarkan skor risiko. Untuk ini, ini hanya melihat nilai risiko relatif (atau peringkat, jika Anda mau, lih. Interpretasi tes Wilcoxon-Mann-Whitney), bukan yang absolut, yang harus Anda minati. Misalnya, jika Anda membagi masing-masing risiko estimasi dari model logistik Anda sebesar 2, Anda akan mendapatkan AUC (dan ROC) yang persis sama.

Saat mengevaluasi model risiko, kalibrasi juga sangat penting. Untuk memeriksa ini, Anda akan melihat semua pasien dengan skor risiko sekitar, misalnya 0,7, dan melihat apakah sekitar 70% dari mereka benar-benar sakit. Lakukan ini untuk setiap kemungkinan skor risiko (mungkin menggunakan semacam perataan / regresi lokal). Plot hasilnya, dan Anda akan mendapatkan kalibrasi ukuran grafis .

Jika memiliki model dengan baik kalibrasi yang baik dan diskriminasi yang baik, maka Anda mulai memiliki model yang baik. :)

Karl Ove Hufthammer
sumber
8
Terima kasih, @Karl Ove Hufthammer, ini adalah jawaban paling teliti yang pernah saya terima. Saya sangat menghargai bagian "Kata-kata Terakhir" Anda. Kerja bagus! Terima kasih lagi!
Matt Reichenbach
Terima kasih banyak atas jawaban ini. Saya bekerja dengan dataset di mana Epi :: ROC () v2.2.6 yakin bahwa AUC adalah 1.62 (bukan itu bukan studi mentalis), tetapi menurut ROC, saya percaya lebih banyak pada 0,56 yang dihasilkan oleh kode di atas masuk.
BurninLeo
32

Lihat pertanyaan ini: Memahami kurva ROC

Berikut cara membuat kurva ROC (dari pertanyaan itu):

Menggambar kurva ROC

diberi set data yang diproses oleh classifier peringkat Anda

  • memberi peringkat contoh uji pada penurunan skor
  • mulai dari(0,0)
  • untuk setiap contoh (dalam urutan menurun) x
    • jika positif, pindahkan atas1 / posx1/pos
    • jika negatif, gerakkan kanan1 / negx1/neg

di mana dan masing-masing adalah pecahan dari contoh positif dan negatif.negposneg

Anda dapat menggunakan ide ini untuk menghitung AUC ROC secara manual menggunakan algoritma berikut:

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

Gambar animasi gif yang bagus ini harus menggambarkan proses ini dengan lebih jelas

membangun kurva

Alexey Grigorev
sumber
1
Terima kasih @Alexey Grigorev, ini adalah visual yang hebat dan kemungkinan akan terbukti berguna di masa depan! +1
Matt Reichenbach
1
Bisakah tolong jelaskan sedikit tentang "fraksi contoh positif dan negatif", maksud Anda nilai unit terkecil dari dua sumbu?
Allan Ruin
1
@ Allan Ruin: di possini berarti jumlah data positif. Katakanlah Anda memiliki 20 poin data, di mana 11 poin adalah 1. Jadi, saat menggambar grafik, kami memiliki persegi panjang 11x9 (tinggi x lebar). Alexey Grigorev melakukan skala tetapi biarkan seperti itu jika Anda suka. Sekarang, cukup gerakkan 1 pada grafik di setiap langkah.
Catbuilts
5

Posting Karl memiliki banyak informasi bagus. Tapi dalam 20 tahun terakhir saya belum melihat contoh kurva ROC yang mengubah pemikiran siapa pun ke arah yang baik. Satu-satunya nilai kurva ROC menurut pendapat saya yang sederhana adalah bahwa luasnya kebetulan sama dengan probabilitas konkordansi yang sangat berguna. Kurva ROC sendiri menggoda pembaca untuk menggunakan cutoff, yang merupakan praktik statistik yang buruk.

Sejauh secara manual menghitung -index, buat plot dengan pada sumbu dan prediktor kontinu atau prediksi probabilitas bahwa pada sumbu. Jika Anda menghubungkan setiap titik dengan dengan setiap titik dengan , proporsi garis yang memiliki kemiringan positif adalah probabilitas konkordansi.Y = 0 , 1 x Y = 1 y Y = 0 Y = 1cY=0,1xY=1yY=0Y=1

Setiap tindakan yang memiliki penyebut dalam pengaturan ini adalah aturan penilaian akurasi yang tidak tepat dan harus dihindari. Ini termasuk proporsi yang diklasifikasikan dengan benar, sensitivitas, dan spesifisitas.n

Untuk fungsi Hmiscpaket R rcorr.cens, cetak seluruh hasil untuk melihat informasi lebih lanjut, terutama kesalahan standar.

Frank Harrell
sumber
Terima kasih, @ Frank Harell, saya menghargai sudut pandang Anda. Saya hanya menggunakan statistik c sebagai probabilitas konkordansi, karena saya tidak suka cutoff. Terima kasih lagi!
Matt Reichenbach
4

Berikut ini adalah alternatif cara alami menghitung AUC dengan hanya menggunakan aturan trapesium untuk mendapatkan area di bawah kurva ROC.

AUC sama dengan probabilitas bahwa pengamatan positif secara acak sampel memiliki probabilitas yang diprediksi (menjadi positif) lebih besar daripada pengamatan negatif secara acak. Anda dapat menggunakan ini untuk menghitung AUC dengan cukup mudah dalam bahasa pemrograman apa pun dengan melalui semua kombinasi berpasangan dari pengamatan positif dan negatif. Anda juga bisa secara acak sampel pengamatan jika ukuran sampel terlalu besar. Jika Anda ingin menghitung AUC menggunakan pena dan kertas, ini mungkin bukan pendekatan terbaik kecuali Anda memiliki sampel yang sangat kecil / banyak waktu. Misalnya dalam 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

Kami dapat memverifikasi menggunakan pROCpaket:

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

Menggunakan sampling acak:

mean(sample(probs[y == 1L], 100000L, TRUE) > sample(probs[y == 0L], 100000L, TRUE))
[1] 0.62896
Jeff
sumber
1
  1. Anda memiliki nilai sebenarnya untuk pengamatan.
  2. Hitung probabilitas posterior dan kemudian peringkatkan pengamatan dengan probabilitas ini.
  3. Dengan asumsi probabilitas cut-off dan jumlah pengamatan :N Jumlah dari peringkat sebenarnya - 0,5 P N ( P N + 1 )PN
    Sum of true ranks0.5PN(PN+1)PN(NPN)
pengguna73455
sumber
1
@ user73455 ... 1) Ya, saya memiliki nilai sebenarnya untuk pengamatan. 2) Apakah probabilitas posterior identik dengan probabilitas yang diprediksi untuk masing-masing pengamatan? 3) Dipahami; namun, apa itu "Jumlah peringkat sebenarnya" dan bagaimana cara menghitung nilai ini? Mungkin sebuah contoh akan membantu Anda menjelaskan jawaban ini dengan lebih teliti? Terima kasih!
Matt Reichenbach