1. ROC Curve란.
2. AUC 구하는 방법.


요약: 진단 방법의 효율성을 판단하는 방법 중 널리 사용되는 것이 ROC curve 이다. 민감도(sensitivity)와 특이도(specificity)가 어떤 관계를 갖고 변하는지를 이차원 평면 상에 표현한 것이 ROC curve인데, ROC curve 아래의 면적(AUC, area under curve) 이 넓을수록 좋은 진단 방법이라 할 수 있다. 이 글은 ROC curve의 AUC를 구하는 간단한 방법[각주:1]을 설명한다.


1. ROC Curve란.

   ROC Curve는 Receiver-Operating Characteristic curve의 줄임말로, 특정 진단 방법의 민감도와 특이도가 어떤 관계를 갖고 있는지를 표현한 그래프이다.

  진단 방법에 의한 환자의 분류는, 점수에 따라 사람을 환자/정상 으로 분류하는 것으로 결국 분류(classification)의 한 예이고, 따라서 아래 나오는 진단 방법은 classifier로 바꾸어 이해해도 큰 무리가 없겠다.

   우선 민감도와 특이도가 무엇인지 간단히 알아 보자. 보다 자세한 내용은 별도의 글을 참고하자. 민감도는 진짜 환자 중 검사 방법이 환자를 얼마나 잘 골라 내는가, 하는 것이고, 특이도는 진짜 정상 중 검사 방법이 정상을 얼마나 잘 골라 내는가를 의미한다. 예를 들어 보자. 내가 혈압으로 심근경색을 진단할 수 있다고 가정해 보자. '혈압이 얼마 이상이면 심근경색이다'라는 진단은 민감도 100%, 특이도 100%를 갖게 혈압의 기준을 설정할 수 있다. 가령, '혈압이 5 이상이면 심근경색' 이러면 전체 인구가 모두 환자로 판별되어, 누구나 환자로 판정이 나고, 따라서 환자를 100% 환자라고 진단할 수 있다. 문제는, 환자가 아닌 사람도 몽땅 환자라고 하기 때문에 민감도특이도가 거의 0에 가깝게 떨어진다는 것. 또는 반대로 혈압이 '1000 이상이면 심근경색' 이러면 모두 정상이라 판별되기 때문에 정상을 100% 골라낼 수 있기 때문에 특이도가 1 이 되지만 환자는 하나도 못 골라내기 때문에 민감도가 0 이 된다. 이렇게, 모든 분류 방법은 민감도와 특이도를 동시에 보아야 좋은 진단 방법인지 확인할 수 있다. 현재 적당한 기준은 민감도/특이도 모두 0.8에서 0.9 정도는 되어야 임상에서 사용할 수 있다고 한다.


   위의 예에서, 혈압을 얼마로 하는 것이 민감도/특이도를 높이게 될까? 어차피 민감도를 높이면 특이도는 줄어 들고, 특이도를 높이면 민감도는 감소하기 때문에 (즉, 하나가 다른 하나에 영향을 받기 때문에), 혈압에 대한 기준을 변경시켜 가면서 각각의 값을 그래프에 그려 보는 것이 좋겠다. (혈압, 심근경색여부)에 대한 데이터가 다음과 같다고 하자.

80/0
92/0
93/1
98/0
102/1
110/1
112/0
119/1

이 때 심근경색 여부가 1 이면 심근경색이 있는 것이다. 위의 경우 '혈압이 N이상이면 심근경색이다'라 한다면, N 을 80 으로 잡으면 민감도가 1 이 되나, 특이도가 0 이 된다. 따라서 N 을 변화시키면서 각각의 값을 구해 보면 다음과 같다. 

혈압 class TP TN FP FN P(=TP+FN) N(=TN+FP) 특이도 1-특이도 민감도
80 0 4 0 4 0 4 4 0 1 1
92 0 4 1 3 0 4 4 0.25 0.75 1
93 1 4 2 2 0 4 4 0.5 0.5 1
98 0 3 2 2 1 4 4 0.5 0.5 0.75
102 1 3 3 1 1 4 4 0.75 0.25 0.75
110 1 2 3 1 2 4 4 0.75 0.25 0.5
112 0 1 3 1 3 4 4 0.75 0.25 0.25
119 1 1 4 0 3 4 4 1 0 0.25

이 때, true positive란 환자로 진단된 사람 중 진짜 환자가 몇 명인가 에 대한 수이다. 또한 true negative란 정상으로 판단된 사람 중 진짜 정상은 몇 명인가에 대한 비율이다. 또한 이 때 '진짜' 정상/환자 구분은 기존에 잘 알려진 진단 기법을 기준으로 삼는다. 즉 '혈압'으로 판단하는 것이 새로운 것이므로 위 경우 다른, 기존에 잘 알려지고 많이 사용되는 방법으로 심근경색으로 판단된 것을 기준으로 삼아 '진짜' 환자인지 정상인지를 판별하는 것이다. 


위의 테이블을 그래프로 그려 보면 다음과 같다. 



x 축을 1-특이도로 한 이유는 저렇게 해야 우리가 익히 보아 오던 그래프 형태와 비슷하기 때문이다. 위 표와 그래프에서 알 수 있듯이, 진단 기준을 '혈압 80 이상이면 모두 환자'라 하면 민감도는 1 이지만 특이도가 0 이 되서 그래프 상에는 제일 오른쪽 상위 점으로 찍히게 된다.

위와 같은 그래프의 아래 면적이 1/2에서 멀먼 멀수록 좋은 진단 기준이라 할 수 있겠다. 즉 (민감도,1-특이도) 가 딱 두 점 (0,0), (1,1)에 찍히게 하는 진단 방법은 정확히 AUC가 1 이어서 가장 완벽한 방법이라 할 수 있다.



2. AUC 구하는 방법.

   진단값을 오름차순으로 정렬한 후, 1-특이도가 변하는 지점에서의 sensitivity 합계를 전체 정상(negative) 수로 나누어 주면 된다. 이 때 낮은 진단값이 정상이라 가정한다. 반대일 경우 그에 맞게 적당히 변경해서 사용하면 된다. 작은 진단값에서부터 시작하여 큰 값으로 이동하면서 negative (즉, 이 경우 정상(0)) 을 만나는 점에서의 민감도값만 남겨 준다. 

혈압 class TP TN FP FN P(=TP+FN) N(=TN+FP) 특이도 1-특이도 민감도
80 0 4 0 4 0 4 4 0 1 1
92 0 4 1 3 0 4 4 0.25 0.75 1
93 1 4 2 2 0 4 4 0.5 0.5 1
98 0 3 2 2 1 4 4 0.5 0.5 0.75
102 1 3 3 1 1 4 4 0.75 0.25 0.75
110 1 2 3 1 2 4 4 0.75 0.25 0.5
112 0 1 3 1 3 4 4 0.75 0.25 0.25
119 1 1 4 0 3 4 4 1 0 0.25

위의 경우 민감도 중 노랑색으로 칠한 값들의 합인 3 이 되겠다. 이 값을 전체 negative 수 (정상 수, 이 경우 4명) 로 나누어 준다. 그러면 AUC가 0.75 가 나온다. 



여기까지가 기본적인 방법이고, 실제로 구현을 할 때는 계산의 용이함과 약간(?)의 속도를 위하여 살짝 계산을 줄여서 다음과 같이 계산한다.

1 - 1/(N*P)*(sum_{i=1}^{N} Pi),
where
Pi = the number of passed positive sample at ith negative point,
P = total positive number,
N = total negative number.


실제 C++ 코드로 보면 다음과 같다. 중요: 기존의 코드는 값에 같은 값(80, 80 처럼)이 있는 경우 오류가 있음이 밝혀 졌다. 

bool sort_by_class(const std::pair<double,bool>& op1, const std::pair<double,bool>& op2) {

return op1.first < op2.first; 

}

// yul code. 

double get_AUC(std::vector<std::pair<double,double> >& xy_pair) {

double AUC = 0;

std::sort(xy_pair.begin(), xy_pair.end(), sort_by_class);

std::vector<std::pair<double, double> >::const_iterator roc_pos = xy_pair.begin();

for (roc_pos; roc_pos != xy_pair.end(); roc_pos++){

if (roc_pos + 1 == xy_pair.end()){

continue;

}

if (roc_pos->first < (roc_pos+1)->first){

double temp_area = 0;

double del_x = (roc_pos+1)->first - roc_pos->first;

double del_y = (roc_pos+1)->second - roc_pos->second;

temp_area = (del_x * roc_pos->second) + (del_x * del_y * 0.5);

AUC = AUC + temp_area;

}

else continue;

}

return AUC;

}

double get_ROC_AUC(std::vector<std::pair<double,bool> >& value)

{

std::sort(value.begin(), value.end(), sort_by_class); 

std::vector<std::pair<double, double> > roc_pair;

roc_pair.push_back(std::pair<double, double>(0, 0));

int negative_total = 0; 

int positive_total = 0; 

std::vector<std::pair<double,bool> >::const_iterator pos; 

for (pos = value.begin(); pos != value.end(); pos++){

if(pos->second == true) positive_total++;

if(pos->second == false) negative_total++; 

}

for (pos = value.begin(); pos != value.end(); pos++){

double classifier = -1;

if (pos + 1 == value.end()) classifier = (pos->first) + 1;

else classifier = (pos + 1)->first;


if (classifier - (pos->first) < 0.00000001) continue;


double positive_count = 0;

double negative_count = 0;

std::vector<std::pair<double, bool> >::const_iterator snsp_pos = value.begin();

for (snsp_pos; snsp_pos <= pos; snsp_pos++){

if (snsp_pos->second == true) positive_count++;

else if (snsp_pos->second == false) negative_count++;

}

roc_pair.push_back(std::pair<double, double>((negative_count/static_cast<double>(negative_total)), (positive_count/static_cast<double>(positive_total))));

}

double AUC = get_AUC(roc_pair);

return AUC; 

}

(위 코드는 연구실 후배가 작성한 코드임) 


문제가 있던 기존 코드는 다음에 있다. 

more..


위에서는 일부러 처리하지 않았는데, AUC는 0.5 이하가 될 수 없다. 그런데 만약 위의 계산 결과가 0.5 보다 작다면 질병과 정상군을 반대로 입력한 것이다. 따라서 반환값을 보고 잘 판단하면 된다.


위의 코드를 갖고 실제로 구한 값은 0.823508 이 나오고 GraphPad Prism 으로 구한면 0.8235 가 나온다. 입력 파일은 다음에 있다.

data.txt


이에 대한 ROC 를 그려 보면 다음과 같다.




  1. 원래 복잡한 것을 간단히 설명한다기보다는, 방법 자체가 간단하다는 뜻. ㅋ [본문으로]

'연구관련 > Bioinfo류' 카테고리의 다른 글

정규화(normalization)  (17) 2010.04.19
drug 관련 싸이트  (0) 2010.04.06
생물정보학(bioinformatics)에 대하여  (2) 2010.01.12
outlier 빼고 상관계수 구하기 : Mahalanobis 거리  (0) 2010.01.04
ROC의 AUC 구하기  (16) 2010.01.04
cytoscape: network 연구 프로그램  (0) 2009.03.06
Posted by adnoctum

댓글을 달아 주세요

  1. 가든 2010.12.15 13:42 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 논문 검색중에 ROC가 뭔지 몰라 들어왔습니다. 자세한 설명 감사드립니다.

  2. 인표 2011.06.14 21:04 신고  댓글주소  수정/삭제  댓글쓰기

    자세한 설명 감사합니다.
    이 내용 담아가겠습니다.
    감사합니다.

  3. 신희 2011.10.13 01:59 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요 먼저 좋은 정보 감사드립니다..^^
    죄송합니다만,, 여쭤볼 게 있는데요, ROC 커브를 보면 항상 Sensitivity(TPR = TP/TP+FN)가 1-특이도(FPR = FP/FP+TN) 보다 큰 걸 알 수 있는데요,, 그 이유가 궁금합니다,,(x축 FPR, y축 TPR이라고 할 때 항상 x=y 직선 윗부분에 점이 플로팅되네요..)

    • Favicon of http://adnoctum.tistory.com adnoctum 2011.10.13 08:47 신고  댓글주소  수정/삭제

      만약 말씀하신 그 값이 작다면, 0과 1을 나누는 기준이 반대로 되어 있는 거죠. 예를 들면 어떤 값 이하면 0(정상)으로 하게 해 놓았었다면 그 기준이 '반대'로 되야 하는 것입니다. 따라서, sensitivity 가 1-specificity 보다 작게 나오는 그래프였다면 기준을 반대로 해서 그리면 그래프가 위쪽으로 볼록하게 나오겠죠.

      요약하면, 물론 sentivity 값이 1-specificity 보다 작게 나오는 것도 가능한데 그 경우엔 기준을 반대로 적용하면 더 좋은 결과가 나와서 그렇게 한 후 그림을 그리기 때문에 항상 ROC curve 는 위로 볼록하게 나오는 것입니다.

  4. 독돌이 2011.11.10 15:12 신고  댓글주소  수정/삭제  댓글쓰기

    ROC가 뭔가 궁금해서 들어와봤습니다.
    아...어렵네요.
    좋은정보 감사합니다~

  5. Favicon of http://aging.pharm.pusan.ac.kr 박대의 2012.03.01 21:04 신고  댓글주소  수정/삭제  댓글쓰기

    R이나 S+ 사용하시는 분들은 ROC 커브 그릴때 pROC 패키지 괜찮네요. smooth 기능이 있어서 그래프가 이쁘게 그려지네요. 정설 빨리 감기 낳길 바래요...

  6. 댄디릴라 2012.08.10 19:45 신고  댓글주소  수정/삭제  댓글쓰기

    오류 수정 요청이요~! ^^
    "문제는, 환자가 아닌 사람도 몽땅 환자라고 하기 때문에 민감도가 거의 0에 가깝게 떨어진다는 것."
    이부분에서 민감도가 0에 가깝게 떨어지는 것이 아니라 실제 환자가 아닌 사람 중에서 환자가 아니라고 판단한 비율이 0에 가깝기 때문에 특이도가 0에 가깝에 떨어진다고 해야 맞는 말인 것 같습니다. 반대로 민감도는 1에 가깝겠지요. ^^

    • Favicon of http://adnoctum.tistory.com adnoctum 2012.08.10 19:55 신고  댓글주소  수정/삭제

      오류 수정 요청이요~! ^^
      "실제 환자가 아닌 사람 중에서 환자가 아니라고"
      이 부분에서 "환자가 아니라고" 가 아니라 "환자라고"요.

      ㅎㅎ, 댄디릴라 님을 한 번 따라 해 봤습니다, 기분 언짢지 않으셨으면 합니다.

      지적하신 사항은 무슨 말씀인지 알겠습니다, 적절한 지적이군요. 제가 실수했습니다. 감사합니다.

  7. SSI 2012.08.19 16:56 신고  댓글주소  수정/삭제  댓글쓰기

    궁금한게 있는데요 ROC curve에서 A,B 검사를 비교할때 두검사를 합친 민감도, 특이도를 알고싶으면 SPSS에서 어떻게입력해야하는지요??

    • Favicon of http://adnoctum.tistory.com adnoctum 2012.08.20 11:59 신고  댓글주소  수정/삭제

      무슨 말씀이신지 잘 모르겠습니다. 비교를 하는데 왜 합치는 일을 해야 하는지... 단순히 두 검사를 합해서 새로운 검사를 만들고, 그것의 민감도와 특이도를 알고 싶으신 게 아닐까, 하는 생각이 드는데요. 만약 그 경우라면 fisher discriminant analysis 같은 방법을 적용해 볼 수 있겠지요.

      질문을 좀 더 명확히 해주시면 좀 더 얘기해 볼 수 있을 것 같습니다.

  8. SSI 2012.08.20 13:47 신고  댓글주소  수정/삭제  댓글쓰기

    답변 감사합니다. 그러니까 간암 진단할때 A검사와 B검사를 합친 경우 민감도가 상승하고 특이도는 감소하게 되는데 두개를 합친 ROC curve를 그리고싶은거거든요.. 논문에 A, B곡선말고 A+B의 곡선이 있어서요. 예를들면 두검사를비교하고 그렇지만 진단율을 높이는데 두검사를 합친게 더 좋을수있잖아요

    • Favicon of http://adnoctum.tistory.com adnoctum 2012.08.20 23:17 신고  댓글주소  수정/삭제

      제일 간단하게는 다음과 같이 해볼 수 있습니다.
      a*A + b*B 로 새로운 검사 C 를 만들고, a와 b를 변형시켜 가면서 C의 AUC를 구해서 가장 높은 (a,b)를 찾은 후 그것으로 ROC curve를 그리면 되겠죠. 그런데, 이 때, C를 a로 나눠줘도 무방하니

      A + b*B = C

      로 C를 정하고, b값을, 가령 -100 부터 100 처럼 특정 구간을 정해준 후 값을 모두 훑으면서 C의 auc 를 구해서 가장 높은 b 값을 찾은 후, 그 b 값에 해당하는 ROC curve 를 구하면 될 것 같습니다.

      A와 B가 선형으로 결합하지 않는다면 좀 더 복잡해질 수 있고, 그 때는 왜 선형이 아닌(가령 parabola) 다른 형태의 결합을 사용했는지에 대한 근거(rationale)을 제공해야 할 것으로 보입니다.

      SPSS에서 위 작업을 어떻게 하는지는 잘 모르겠습니다. 통계처리까지 전 전부 코드로 작성해서 하는지라...

  9. Favicon of http://quantic.tistory.com 호쫑 2014.02.18 20:36 신고  댓글주소  수정/삭제  댓글쓰기

    ROC, AUC를 검색하다 우연히 들리게되었습니다. 알기 쉽게 설명해 주셔서 감사합니다.

  10. Favicon of http://cafe.naver.com/iphonediy 길똥고 2014.06.24 19:58 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 내용 잘 읽었습니다.

    그런데 그림이 안보이네요 ...
    맨 마지막 그림 밖에 안보입니다. --;

    그림 좀 링크복구 해주시면 안될까요?

    미리 감사드리며~
    좀 퍼갈게요.

    http://cafe.naver.com/iphonediy

  11. 열이사랑 2014.08.22 09:46 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 내용 감사합니다. 제가 찾던 정보입니다. *^^*

    혹시 그림을 보이게 할 수는 없을까요??

  12. 구독자 2015.04.02 04:50 신고  댓글주소  수정/삭제  댓글쓰기

    좋은 정보 너무 감사드립니다.
    아쉽게도 제일 마지막 그림만 보여서 설명을 충분히 이해하기 어렵네요.
    혹시 그림을 보이게 해주실수 있으신지요.