본문 바로가기
@ 통계 교양/통계 Tips

ROC curve 직접 그려보기

by bigpicture 2020. 6. 6.
반응형

ROC 곡선은 x축은 (1-specificity), y축은 sensitivity 인 곡선입니다. Receiver Operating Characteristic 의 약어입니다. 직역하면 수신자조작특성인데 신호탐지이론?에 나오는 용어라 와닿지 않네요. 

 

통계학의 입장에서 '진단(diagnosis)'이라는 관점으로 ROC curve 를 설명드릴 것입니다. 한가지 예시를 통해 자세히 설명드리겠습니다. 

 

체질량지수를 당뇨판정에 사용할 수 있을지 여부를 확인하고 싶습니다. 아래는 환자 10명의 체질량지수와 당뇨판정 여부에 대한 데이터입니다. 사실 10명은 너무 작은 크기이지만, 개념을 이해하는 것이 목적이므로 작게 설정했습니다. (bmi, 진단결과) 로 나타냈습니다.

 

(33.6,Yes)

(26.6,No)

(28.1,No)

(31.0,Yes)

(30.5,Yes)

(25.8,Yes)

(45.8,Yes)

(43.3,No)

(39.3,No)

(29.0,Yes)

 

bmi 크기가 작은 순으로 다시 배열해봅시다.

 

(25.8,Yes)

(26.6,No)

(28.1,No)

(29.0,Yes)

(30.5,Yes)

(31.0,Yes)

(33.6,Yes)

(39.3,No)

(43.3,No)

(45.8,Yes)

 

우리는 한가지 가정을 할 것입니다. bmi값이 A 이상일 때를 당뇨, 이하일 때를 정상으로 판정할 겁니다. 이제 가장 좋은 A를 결정해주어야 합니다. 어떤 기준으로 좋고 나쁨을 판단할지 알아보기 전에 아무 값이나 설정해봅시다. 일단 한번 해보면 좋은 A값에 대한 감이 올 것입니다.

 

한 35정도로 해봅시다. 35 이상이면 당뇨, 미만이면 정상으로 진단하겠습니다. 아래는 우리가 세운 bmi 기준으로 진단한 결과와 실제 결과입니다. 진단 결과를 표로도 나타낼 수 있습니다. 

 

 

 

35라는 값이 당뇨판정에 좋은 값인지 판단기준을 세워봅시다. 첫번째 생각해볼 수 있는 기준은 실제 당뇨환자를 당뇨환자라고 진단한 비율입니다. 위 경우는 1/6 입니다. 16.7% 정도입니다. 당뇨환자를 당뇨환자라고 진단한 비율을 'sensitivity'라고 합니다. 우리말로는 '민감도'입니다. 민감도가 16.7%면 너무 낮죠? 민감도를 높여봅시다. 당뇨판정을 더 많이 내리면 민감도가 올라가니까 bmi기준을 25로 확 낮춰봅시다. 기준을 바꾼 진단결과는 아래와 같습니다. 

 

 

당뇨환자를 당뇨환자라고 진단한 비율인 sensitivity는 100%입니다. 그런데 문제가 있습니다. 정상인을 정상인이라고 진단한 비율이 0%입니다. 진단 기준을 너무 낮추다 보니, 정상인을 모두 당뇨로 판정한 것입니다. 정상인을 정상인이라고 진단한 비율을 specificity 라고 합니다. 우리말로는 특이도라고 부릅니다. 

 

그렇다면 좋은 진단이 무엇일까요? 민감도도 높고 특이도도 높은 진단일 것입니다. 그런데 문제가 있습니다. 민감도를 높이면 특이도가 낮아지고, 특이도를 높이면 민감도가 낮아집니다. 서로 trade off 관계입니다. 따라서 우리는 '최적의 값'을 찾아야 합니다.

 

이 최적의 값을 찾도록 도와주는 그래프가 ROC 곡선 입니다. 다시 ROC 곡선의 정의로 돌아가봅시다. ROC 곡선은 x축은 (1-specificity), y축은 sensitivity 인 곡선입니다. 

 

 

 

 

x축이 specificity가 아니라 1-specificity인데요, 제 생각에는 그래프를 더 알아보기 쉽게 만들기 위함인 것 같습니다. specificity는 0부터 1 사이 값이라서, 그래프를 좌우로 뒤집는 차이가 생길 뿐입니다. 

 

자 그럼 이제 ROC curve를 직접 그려봅시다. 방법은 간단합니다. 진단기준이 되는 bmi 값을 바꿔가면서 좌표평면에 점을 찍는 것입니다. bmi가 최소값인 25.8 보다 낮을 때 민감도와 특이도를 구해서 점을 찍으면 아래와 같습니다. 

 

 

 

기준을 25.8과 26.6 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

 

기준을 26.6과 28.1 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

 

 

기준을 28.1과 29.0 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

기준을 29.0과 30.5 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

 

기준을 30.5과 31.0 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

 

기준을 31.0과 33.6 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

기준을 33.6과 39.3 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

기준을 39.3과 43.3 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

기준을 43.3과 45.8 사이로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

기준을 45.8 이상으로 높이고 민감도와 (1-특이도)를 계산하여 점을 찍어봅시다.

 

 

이제 점들을 선으로 연결해주면 ROC 곡선이 완성됩니다. 

 

 

 

 

ROC 곡선을 왜 그렸는지 다시 상기해봅시다. 정상과 당뇨를 판단하는 BMI 기준을 바꿔가면서 ROC 곡선을 그렸습니다. 이 곡선을 이용하여 가장 진단을 잘 할 수 있는 BMI 기준을 찾아야합니다. 

 

민감도는 높을 수록 좋고, (1-특이도)는 낮을 수록 좋습니다. 따라서 y=x 직선에서 좌/상 방향으로 멀리 떨어진 점이 민감도와 특이도 둘다 높은 점입니다. 각각이 가장 높은 점은 아닙니다. (민감도+특이도)가 가장 높은 점입니다. 

 

 

선으로 부터 멀리 떨어진 점은 두 점입니다. 그 중 위에 있는 점이 선에서 더 멀리 떨어져 있습니다. 

 

이 점에서의 BMI 값을 기준으로 삼으면 됩니다. 얼마일까요? 이 점은 28.1과 29.0 사이에서 찍힌 점입니다. 둘의 평균값을 취합시다. 28.55 입니다. 

 

가장 좋은 기준이 되는 BMI를 선정했다고 해도, 진단능력이 떨어질 수도 있습니다. 내꺼중에 최고지만, 쓸만하지 못한 경우입니다. 쓸만한가의 판단 기준은 ROC curve의 아래쪽 면적을 이용합니다. AUC(Area Under the Curve) 라고 부릅니다. AUC는 다음글에서 설명드리도록 하겠습니다. 

 


[사용한 R코드]

 

1) 점 그릴 때 사용한 코드 

 

plot(0:1,0:1,type="n",ann=FALSE)

title(main="ROC curve",xlab="1-specificity",ylab="sensitivity",

      cex.main=1.6,cex.lab=1.3)

box("figure",col='gray')



points(1,1,pch=19,col='red')

points(1,5/6,pch=19,col='red')

points(3/4,5/6,pch=19,col='red')

points(2/4,5/6,pch=19,col='red')

points(2/4,4/6,pch=19,col='red')

points(2/4,3/6,pch=19,col='red')

points(2/4,2/6,pch=19,col='red')

points(2/4,1/6,pch=19,col='red')

points(1/4,1/6,pch=19,col='red')

points(0/4,1/6,pch=19,col='red')



points(0/4,0/6,pch=19,col='red')






2) ROC curve 코드



#curve 그리기

plot(0:1,0:1,type="n",ann=FALSE)

title(main="ROC curve",xlab="1-specificity",ylab="sensitivity",

      cex.main=1.6,cex.lab=1.3)

box("figure",col='gray')



x=c(1,1,3/4,2/4,2/4,2/4,2/4,2/4,1/4,0/4,0/4)

y=c(1,5/6,5/6,5/6,4/6,3/6,2/6,1/6,1/6,1/6,0/6)



lines(x,y)

lines(0:1,0:1,col="red")
반응형

댓글