Cách tính diện tích theo đường cong (AUC) hoặc thống kê c bằng tay


78

Tôi quan tâm đến việc tính diện tích dưới đường cong (AUC), hoặc thống kê c, bằng tay cho một mô hình hồi quy logistic nhị phân.

Ví dụ: trong tập dữ liệu xác thực, tôi có giá trị thực cho biến phụ thuộc, lưu giữ (1 = giữ lại; 0 = không giữ lại), cũng như trạng thái duy trì dự đoán cho mỗi quan sát được tạo bởi phân tích hồi quy của tôi bằng mô hình hồi quy được xây dựng bằng cách sử dụng tập huấn luyện (điều này sẽ nằm trong khoảng từ 0 đến 1).

Suy nghĩ ban đầu của tôi là xác định số lượng phân loại mô hình "chính xác" và chỉ cần chia số lượng quan sát "chính xác" cho số lượng quan sát tổng thể để tính toán thống kê c. Theo "chính xác", nếu trạng thái duy trì thực sự của một quan sát = 1 và trạng thái duy trì dự đoán là> 0,5 thì đó là phân loại "chính xác". Ngoài ra, nếu trạng thái duy trì thực sự của một quan sát = 0 và trạng thái duy trì dự đoán là <0,5 thì đó cũng là một phân loại "chính xác". Tôi giả sử "hòa" sẽ xảy ra khi giá trị dự đoán = 0,5, nhưng hiện tượng đó không xảy ra trong tập dữ liệu xác nhận của tôi. Mặt khác, phân loại "không chính xác" sẽ là nếu trạng thái duy trì thực sự của một quan sát = 1 và trạng thái duy trì dự đoán là <0. 5 hoặc nếu trạng thái duy trì thực sự cho kết quả = 0 và trạng thái duy trì dự đoán là> 0,5. Tôi biết về TP, FP, FN, TN, nhưng không biết làm thế nào để tính toán thống kê c đưa ra thông tin này.

Câu trả lời:


115

Tôi muốn giới thiệu bài viết năm 1982 của Hanley & McNeil ' Ý nghĩa và việc sử dụng khu vực dưới đường cong đặc tính vận hành máy thu (ROC) '.

Thí dụ

Họ có bảng tình trạng bệnh và kết quả xét nghiệm sau đây (tương ứng với, ví dụ, rủi ro ước tính từ một mô hình logistic). Số đầu tiên bên phải là số bệnh nhân có tình trạng bệnh thật 'bình thường' và số thứ hai là số bệnh nhân có tình trạng bệnh thật 'bất thường':

(1) Chắc chắn là bình thường: 33/3
(2) Có lẽ là bình thường: 6/2
(3) Câu hỏi: 6/2
(4) Có thể là bất thường: 11/11
(5) Chắc chắn là bất thường: 2/33

Vì vậy, có tổng số 58 bệnh nhân 'bình thường' và '51' bất thường. Chúng tôi thấy rằng khi dự đoán là 1, 'Chắc chắn là bình thường', bệnh nhân thường bình thường (đúng với 33 trong số 36 bệnh nhân) và khi đó là 5, 'Chắc chắn là bất thường', bệnh nhân thường bất thường (đúng với 33 35 bệnh nhân), vì vậy người dự đoán có ý nghĩa. Nhưng làm thế nào chúng ta nên đánh giá một bệnh nhân có điểm 2, 3 hoặc 4? Những gì chúng tôi đặt ra cho điểm cắt của chúng tôi để đánh giá một bệnh nhân là bất thường hoặc bình thường để xác định độ nhạy và độ đặc hiệu của xét nghiệm kết quả.

Độ nhạy và độ đặc hiệu

Chúng tôi có thể tính toán độ nhạy và độ đặc hiệu ước tính cho các điểm cắt khác nhau. (Tôi sẽ chỉ viết 'độ nhạy' và 'độ đặc hiệu' từ bây giờ, để cho tính chất ước tính của các giá trị được ẩn.)

Nếu chúng tôi chọn mức cắt của chúng tôi để chúng tôi phân loại tất cả các bệnh nhân là bất thường, bất kể kết quả xét nghiệm của họ nói gì (nghĩa là chúng tôi chọn mức cắt 1+), chúng tôi sẽ có độ nhạy 51/51 = 1. Độ đặc hiệu sẽ là 0 / 58 = 0. Nghe không hay lắm.

OK, vì vậy hãy chọn một điểm cắt ít nghiêm ngặt hơn. Chúng tôi chỉ phân loại bệnh nhân là bất thường nếu họ có kết quả xét nghiệm từ 2 trở lên. Sau đó chúng tôi bỏ lỡ 3 bệnh nhân bất thường và có độ nhạy 48/51 = 0,94. Nhưng chúng tôi có độ đặc hiệu tăng lên nhiều, là 33/58 = 0,57.

Bây giờ chúng ta có thể tiếp tục điều này, chọn các điểm cắt khác nhau (3, 4, 5,> 5). (Trong trường hợp cuối cùng, chúng tôi sẽ không phân loại bất kỳ bệnh nhân nào là bất thường, ngay cả khi họ có điểm kiểm tra cao nhất có thể là 5.)

Đường cong ROC

Nếu chúng ta làm điều này cho tất cả các điểm cắt có thể và biểu đồ độ nhạy so với 1 trừ đi độ đặc hiệu, chúng ta sẽ có đường cong ROC. Chúng ta có thể sử dụng mã R sau:

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

Đầu ra là:

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

Chúng tôi có thể tính toán các số liệu thống kê khác nhau:

( 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

Và bằng cách sử dụng này, chúng ta có thể vẽ đường cong ROC (ước tính):

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)

Đường cong AUC

Tính toán thủ công AUC

Chúng ta có thể dễ dàng tính toán diện tích dưới đường cong ROC, sử dụng công thức cho diện tích của hình thang:

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

Kết quả là 0,8931711.

Một biện pháp phù hợp

AUC cũng có thể được coi là một biện pháp phù hợp. Nếu chúng tôi có tất cả các cặp bệnh nhân có thể có một bệnh nhân bình thường và một bệnh nhân khác thường, chúng tôi có thể tính toán mức độ thường xuyên của một bệnh nhân có kết quả xét nghiệm cao nhất (trông có vẻ bất thường nhất) (nếu chúng có cùng giá trị, chúng tôi tính rằng đây là 'một nửa chiến thắng'):

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

Câu trả lời là một lần nữa 0.8931711, khu vực dưới đường cong ROC. Điều này sẽ luôn luôn là trường hợp.

Một cái nhìn đồ họa của sự phù hợp

Như được chỉ ra bởi Mitchell trong câu trả lời của mình, điều này cũng có một cách giải thích đồ họa. Hãy cho điểm kiểm tra đồ thị (ước tính rủi ro) trên y -axis và tình trạng bệnh thực sự trên x -axis (ở đây với một số biến động, để hiển thị các điểm chồng chéo):

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

Phân tán âm mưu của điểm rủi ro chống lại tình trạng bệnh thực sự.

Bây giờ chúng ta hãy vẽ một đường giữa mỗi điểm bên trái (một bệnh nhân 'bình thường') và mỗi điểm ở bên phải (một bệnh nhân 'bất thường'). Tỷ lệ đường có độ dốc dương tính (ví dụ, tỷ lệ chỉnh hợp cặp) là chỉ số sự phù hợp (đường phẳng được tính là '50% sự phù hợp ').

Thật khó để hình dung các dòng thực tế cho ví dụ này, do số lượng mối quan hệ (điểm rủi ro bằng nhau), nhưng với một số sự lộn xộn và minh bạch, chúng ta có thể có được một âm mưu hợp lý:

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

Phân tán biểu đồ điểm rủi ro chống lại tình trạng bệnh thực sự, với các đường giữa tất cả các cặp quan sát có thể.

Chúng tôi thấy rằng hầu hết các đường dốc lên, vì vậy chỉ số phù hợp sẽ cao. Chúng tôi cũng thấy sự đóng góp cho chỉ số từ mỗi loại cặp quan sát. Hầu hết đến từ những bệnh nhân bình thường có điểm rủi ro là 1 cặp với những bệnh nhân bất thường có điểm rủi ro là 5 (1 cặp5), nhưng khá nhiều cũng đến từ 1 cặp4 và 4 cặp5. Và thật dễ dàng để tính toán chỉ số phù hợp thực tế dựa trên định nghĩa độ dốc:

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

Câu trả lời là một lần nữa 0.8931711, tức là AUC.

Thử nghiệm Wilcoxonon MannTHER Whitney

Có một mối liên hệ chặt chẽ giữa biện pháp phù hợp và thử nghiệm WilcoxonTHER MannTHER Whitney. Trên thực tế, các xét nghiệm sau nếu xác suất phù hợp (nghĩa là bệnh nhân bất thường trong một cặp bất thường bình thường ngẫu nhiên bình thường sẽ có kết quả xét nghiệm 'bất thường' nhất) là chính xác 0,5. Và thống kê kiểm tra của nó chỉ là một biến đổi đơn giản của xác suất phù hợp ước tính:

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

Thống kê kiểm tra ( W = 2642) đếm số lượng các cặp tương ứng. Nếu chúng ta chia nó cho số lượng các cặp có thể, chúng ta sẽ nhận được một số gia đình:

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

Vâng, đó là 0,8931711, khu vực dưới đường cong ROC.

Các cách dễ dàng hơn để tính AUC (tính bằng R)

Nhưng hãy làm cho cuộc sống của chúng ta dễ dàng hơn. Có nhiều gói tính toán AUC cho chúng tôi tự động.

Gói Epi

Các Epigói tạo ra một đường cong ROC tốt đẹp với các thống kê khác nhau (bao gồm cả AUC) được nhúng:

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

Đường cong ROC từ gói Epi

Gói pROC

Tôi cũng thích pROCgói này, vì nó có thể làm mịn ước tính ROC (và tính toán ước tính AUC dựa trên ROC được làm mịn):

Đường cong ROC (không phẳng và được làm mịn) từ gói pROC

(Đường màu đỏ là ROC gốc và đường màu đen là ROC được làm mịn. Ngoài ra, hãy lưu ý tỷ lệ khung hình 1: 1 mặc định. Thật hợp lý khi sử dụng điều này, vì cả độ nhạy và độ đặc hiệu đều có phạm vi 01 .1).

AUC ước tính từ ROC được làm mịn là 0,9107, tương tự, nhưng lớn hơn một chút so với AUC từ ROC không phẳng (nếu bạn nhìn vào hình, bạn có thể dễ dàng thấy tại sao nó lớn hơn). (Mặc dù chúng tôi thực sự có quá ít giá trị kết quả kiểm tra khác biệt có thể để tính toán AUC trơn tru).

Gói rms

rmsGói củaellell có thể tính toán các số liệu thống kê phù hợp liên quan khác nhau bằng cách sử dụng rcorr.cens()hàm. Đầu C Indexra của nó là AUC:

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

Gói caTools

Cuối cùng, chúng ta có caToolsgói và colAUC()chức năng của nó . Nó có một vài lợi thế so với các gói khác (chủ yếu là tốc độ và khả năng làm việc với dữ liệu đa chiều - xem ?colAUC) đôi khi có thể hữu ích. Nhưng tất nhiên nó cho câu trả lời giống như chúng ta đã tính toán nhiều lần:

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

Đường cong ROC từ gói caTools

Từ cuối cùng

Nhiều người dường như nghĩ rằng AUC cho chúng ta biết bài kiểm tra 'tốt' như thế nào. Và một số người nghĩ rằng AUC là xác suất mà xét nghiệm sẽ phân loại chính xác một bệnh nhân. Nó không phải là . Như bạn có thể thấy từ ví dụ và tính toán ở trên, AUC cho chúng ta biết điều gì đó về một gia đình thử nghiệm, một thử nghiệm cho mỗi lần cắt có thể.

Và AUC được tính toán dựa trên mức cắt mà người ta sẽ không bao giờ sử dụng trong thực tế. Tại sao chúng ta nên quan tâm đến độ nhạy và độ đặc hiệu của các giá trị ngưỡng 'vô nghĩa'? Tuy nhiên, đó là những gì AUC (một phần) dựa trên. (Tất nhiên, nếu AUC rất gần với 1, hầu hết mọi bài kiểm tra có thể sẽ có sức mạnh phân biệt đối xử lớn, và tất cả chúng ta sẽ rất hạnh phúc.)

Cách giải thích cặp 'AUC bình thường ngẫu nhiên' của AUC là tốt (ví dụ, có thể được mở rộng cho các mô hình sống sót, trong đó chúng ta thấy nếu đó là người có nguy cơ (tương đối) cao nhất chết sớm nhất). Nhưng người ta sẽ không bao giờ sử dụng nó trong thực tế. Đó là một trường hợp hiếm hoi khi người ta biết một người khỏe mạnh và một người bệnh, không biết người nào là người bệnh và phải quyết định điều trị cho ai. (Trong mọi trường hợp, quyết định là dễ dàng; đối xử với người có rủi ro ước tính cao nhất.)

Vì vậy, tôi nghĩ rằng nghiên cứu đường cong ROC thực tế sẽ hữu ích hơn là chỉ nhìn vào thước đo tóm tắt của AUC. Và nếu bạn sử dụng ROC cùng với (ước tính) chi phí của dương tính giả và âm tính giả, cùng với tỷ lệ cơ bản của những gì bạn đang học, bạn có thể nhận được ở đâu đó.

Cũng lưu ý rằng AUC chỉ đo lường sự phân biệt đối xử , không hiệu chuẩn. Đó là, nó đo lường xem bạn có thể phân biệt giữa hai người (một người ốm và một người khỏe mạnh) hay không, dựa trên điểm số rủi ro. Đối với điều này, nó chỉ xem xét các giá trị rủi ro tương đối (hoặc xếp hạng, nếu bạn muốn, xem diễn giải thử nghiệm WilcoxonTHER MannTHER Whitney), chứ không phải các giá trị tuyệt đối, mà bạn nên quan tâm. Ví dụ: nếu bạn chia từng rủi ro ước tính từ mô hình logistic của bạn bằng 2, bạn sẽ nhận được chính xác AUC (và ROC).

Khi đánh giá một mô hình rủi ro, hiệu chuẩn cũng rất quan trọng. Để kiểm tra điều này, bạn sẽ xem xét tất cả các bệnh nhân có điểm số rủi ro xung quanh, ví dụ 0,7 và xem liệu khoảng 70% trong số này thực sự bị bệnh. Làm điều này cho mỗi điểm rủi ro có thể (có thể sử dụng một số loại hồi quy làm mịn / hồi quy cục bộ). Vẽ các kết quả và bạn sẽ có được một phép đo hiệu chuẩn đồ họa .

Nếu có một mô hình với cả hiệu chuẩn tốt và phân biệt đối xử tốt, thì bạn bắt đầu có mô hình tốt. :)


8
Cảm ơn bạn, @Karl Ove Hufthammer, đây là câu trả lời thấu đáo nhất mà tôi từng nhận được. Tôi đặc biệt đánh giá cao phần "Lời cuối cùng" của bạn. Làm tốt lắm Cảm ơn một lần nữa!
Matt Reichenbach

Cảm ơn bạn rất nhiều cho câu trả lời bừa bãi này. Tôi đang làm việc với một bộ dữ liệu trong đó Epi :: ROC () v2.2.6 tin chắc rằng AUC là 1.62 (không phải là nghiên cứu về tinh thần), nhưng theo ROC, tôi tin nhiều hơn vào 0,56 rằng mã trên cho kết quả trong.
BurninLeo

32

Hãy xem câu hỏi này: Hiểu về đường cong ROC

Đây là cách xây dựng đường cong ROC (từ câu hỏi đó):

Vẽ đường cong ROC

đưa ra một tập dữ liệu được xử lý bởi phân loại xếp hạng của bạn

  • ví dụ kiểm tra xếp hạng về việc giảm điểm
  • (0,0)
  • x
    • x1/pos
    • x1/neg

posneg

Bạn có thể sử dụng ý tưởng này để tính toán thủ công AUC ROC bằng thuật toán sau:

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

Hình ảnh hoạt hình gif đẹp này sẽ minh họa quá trình này rõ ràng hơn

xây dựng đường cong


1
Cảm ơn @Alexey Grigorev, đây là một hình ảnh tuyệt vời và nó có thể sẽ chứng minh sự hữu ích trong tương lai! +1
Matt Reichenbach

1
Xin vui lòng giải thích một chút về "phân số của các ví dụ tích cực và tiêu cực", ý bạn là giá trị đơn vị nhỏ nhất của hai trục?
Allan Ruin

1
@ ALLan Ruin: posở đây có nghĩa là số lượng dữ liệu tích cực. Hãy nói rằng bạn có 20 điểm dữ liệu, trong đó 11 điểm là 1. Vì vậy, khi vẽ biểu đồ, chúng ta có một hình chữ nhật 11x9 (chiều cao x chiều rộng). Alexey Grigorev đã mở rộng quy mô nhưng cứ để nó như bạn muốn. Bây giờ, chỉ cần di chuyển 1 trên biểu đồ ở mỗi bước.
Catbuilts

5

Bài viết của Karl có rất nhiều thông tin tuyệt vời. Nhưng tôi chưa thấy trong 20 năm qua một ví dụ về đường cong ROC đã thay đổi suy nghĩ của bất kỳ ai theo hướng tốt. Giá trị duy nhất của một đường cong ROC theo ý kiến ​​khiêm tốn của tôi là diện tích của nó xảy ra tương đương với xác suất phù hợp rất hữu ích. Đường cong ROC tự cám dỗ người đọc sử dụng các điểm ngắt, đó là thực tiễn thống kê xấu.

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

n

Đối với chức năng Hmiscgói R rcorr.cens, in toàn bộ kết quả để xem thêm thông tin, đặc biệt là lỗi tiêu chuẩn.


Cảm ơn bạn, @Frank Harell, tôi đánh giá cao quan điểm của bạn. Tôi chỉ đơn giản sử dụng thống kê c như một xác suất phù hợp, vì tôi không thích cắt bỏ. Cảm ơn một lần nữa!
Matt Reichenbach

4

Dưới đây là một cách thay thế cho cách tính AUC tự nhiên bằng cách sử dụng quy tắc hình thang để có được diện tích dưới đường cong ROC.

AUC bằng với xác suất quan sát dương tính được lấy mẫu ngẫu nhiên có xác suất dự đoán (là dương tính) lớn hơn so với quan sát âm tính được lấy mẫu ngẫu nhiên. Bạn có thể sử dụng điều này để tính toán AUC khá dễ dàng trong bất kỳ ngôn ngữ lập trình nào bằng cách thực hiện tất cả các kết hợp theo chiều ngang của các quan sát tích cực và tiêu cực. Bạn cũng có thể quan sát mẫu ngẫu nhiên nếu kích thước mẫu quá lớn. Nếu bạn muốn tính toán AUC bằng bút và giấy, đây có thể không phải là cách tiếp cận tốt nhất trừ khi bạn có một mẫu rất nhỏ / rất nhiều thời gian. Ví dụ trong 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

Chúng tôi có thể xác minh bằng cách sử dụng pROCgói:

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

Sử dụng lấy mẫu ngẫu nhiên:

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

1
  1. Bạn có giá trị thực sự cho các quan sát.
  2. Tính xác suất sau và sau đó xếp hạng các quan sát theo xác suất này.
  3. PN
    Sum of true ranks0.5PN(PN+1)PN(NPN)

1
@ user73455 ... 1) Có, tôi có giá trị thực sự cho các quan sát. 2) Xác suất sau có đồng nghĩa với xác suất dự đoán cho mỗi quan sát không? 3) Hiểu; tuy nhiên, "Tổng thứ hạng thực" là gì và làm thế nào để tính giá trị này? Có lẽ một ví dụ sẽ giúp bạn giải thích câu trả lời này kỹ hơn? Cảm ơn bạn!
Matt Reichenbach
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.