私は、Hanley's&McNeilの1982年の論文「受信機動作特性(ROC)曲線下の領域の意味と使用」をお勧めします。
例
これらには、疾患の状態とテスト結果の次の表があります(たとえば、ロジスティックモデルからの推定リスクに対応)。右側の最初の数字は、真の疾患ステータスが「正常」である患者の数であり、2番目の数字は、真の疾患ステータスが「異常」である患者の数です。
(1)完全に正常:33/3
(2)おそらく正常:6/2
(3)疑わしい:6/2
(4)おそらく異常:11/11
(5)確実に異常:2/33
したがって、合計58人の「正常な」患者と「51」人の異常な患者がいます。予測変数が1、「完全に正常」の場合、患者は通常正常であり(36人の患者のうち33人に対して真)、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の手動計算
台形の面積の式を使用して、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軸にプロットします(ここでは、重なり合ったポイントを示すために、多少のジッターがあります)。
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
パッケージは、さまざまな統計(AUCを含む)が埋め込まれた素晴らしいROC曲線を作成します。
library(Epi)
ROC(testres, truestat) # also try adding plot="sp"
pROCパッケージ
またpROC
、ROC推定値を平滑化できる(および平滑化されたROCに基づいてAUC推定値を計算できる)パッケージも気に入っています。
(赤い線は元のROCで、黒い線は平滑化されたROCです。デフォルトの1:1アスペクト比にも注意してください。感度と特異度の両方に0〜1の範囲があるため、これを使用するのは理にかなっています)
平滑化された ROC からの推定AUC は0.9107で、平滑化されていない ROC からのAUC に似ていますが、わずかに大きくなっています(図を見ると、なぜ大きいのか簡単にわかります)。(スムーズなAUCを計算するには、実際のテスト結果値が少なすぎますが)。
rmsパッケージ
Harrellの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
最後の言葉
多くの人は、AUCがテストの「良い」ことを教えてくれると考えているようです。そして、一部の人々は、AUCがテストが患者を正しく分類する確率であると考えています。そうではありません。あなたは上記の例と計算からわかるように、AUCは私たちについて何か伝え家族のテストの、それぞれの可能なカットオフに1つのテストを。
そして、AUCは実際には決して使用しないカットオフに基づいて計算されます。「無意味な」カットオフ値の感度と特異性に注意する必要があるのはなぜですか?それでも、それはAUCが(部分的に)基づいているものです。(もちろん、AUCが1 に非常に近い場合、ほとんどすべての可能なテストに大きな差別力があり、私たちはすべて非常に満足しています。)
AUCの「ランダムな正常-異常」ペアの解釈は優れています(また、たとえば、生存モデルに拡張することができます。しかし、実際に使用することはありません。健康な人と病気の人が1人いることを知っており、どの人が病気であるかを知らず、どの人を治療するかを決定しなければならないまれなケースです。(いずれの場合でも、決定は簡単です。推定リスクが最も高いものを扱います。)
したがって、実際のROC曲線を調べることは、AUCの要約測定値を見るよりも役立つと思います。また、ROCを(推定の)偽陽性および偽陰性のコストと、学習しているものの基本レートと一緒に使用すると、どこかに到達できます。
また、AUCは差別ではなく、キャリブレーションのみを測定することに注意してください。つまり、リスクスコアに基づいて、2人(病気の人と健康な人)を区別できるかどうかを測定します。このために、それだけを見て、相対(あなたは、Wilcoxon-Mann-Whitney検定の解釈を参照されたいかどうか、ランク、)リスク値ではなく、あなたは絶対的なもの、必要があることに興味がある。たとえば、各リスクを分割する場合ロジスティックモデルから2で推定すると、まったく同じAUC(およびROC)が得られます。
リスクモデルを評価する場合、キャリブレーションも非常に重要です。これを調べるには、約0.7のリスクスコアを持つすべての患者を調べ、これらの約70%が実際に病気であったかどうかを確認します。可能性のある各リスクスコアに対してこれを行います(何らかの平滑化/ローカル回帰を使用する可能性があります)。結果をプロットすると、キャリブレーションのグラフィカルな測定値が得られます。
優れたキャリブレーションと優れた識別の両方を備えたモデルがある場合、優れたモデルができ始めます。:)