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

Bland-Altman 플롯 쉽게 이해하기

by bigpicture 2025. 3. 21.
반응형

Bland-Altman 플롯은 두 가지 측정 방법 간의 일치 정도(agreement)를 평가하는 데 사용하는 그래프입니다. 

예를 하나 들어봅시다. 혈압을 측정하는 혈압계 A와 B가 있고 5명의 혈압을 아래와 같이 측정했다고 합시다. 

 

피험자 A 혈압계 B혈압계
1 120 122
2 130 128
3 125 127
4 140 146
5 150 159

 

두 혈압계의 결과가 잘 일치하는지 그래프로 나타내고 싶다고 합시다. 여러가지 방법이 있겠지만 두 방법의 일치 정도를 시각적으로 보기 위해서는 Blank-Altman plot이 좋습니다. 왜 좋은지는 Blank-Altman plot을 그려보면 알 수 있습니다.

Blank-Altman 플롯 그리는 방법

먼저 아래와 같이 두 값의 평균과 차이를 계산해줍니다. 

 

피험자 A 혈압계 B 혈압계 평균 차이 (A-B)
1 120 122 121 -2
2 130 128 129 2
3 125 127 126 -2
4 140 146 143 -6
5 150 159 154.5 -9

 

Blank-Altman 플롯은 X축을 평균으로, Y축을 차이로 그린 그래프입니다. 그려보면 아래와 같습니다. 

 

 

R코드 글 맨 뒤에 첨부하겠습니다. Blant-Altman 플롯을 그리면 단순히 산점도를 그리는 것보다 많은 정보를 확인할 수 있습니다. 

 

1. 차이평균을 확인할 수 있습니다. 이 값이 0에 가까울 수록 일치도가 높습니다. 

2. 차이평균을 기준으로 95% SD 구간을 확인할 수 있습니다. 이 구간의 범위가 너무 크면 일치도가 낮은 것입니다. 이 범위가 임상적으로 허용 가능한지 판단해야 합니다. 

3. 혈압계의 값에 따른 차이의 크기를 확인할 수 있습니다. 위 그래프의 경우 혈압이 높을 때 차이가 크게 발생한 것을 확인할 수 있습니다. 

4. Outlier 를 쉽게 확인할 수 있습니다. 

맺는말

두 가지 측정 방법 간의 일치도를 시각화하는 방법에는 여러 가지가 있습니다. 예전에는 단순히 두 결과를 산점도로 그려보거나, 차이 값을 막대그래프로 표시하는 방식이 주로 사용되었을 것입니다. 하지만 이러한 방법들은 한계가 있었고, 이런 시행착오 끝에 Bland-Altman 플롯이 고안되었습니다. 알고 보면 복잡한 내용은 아니지만, 직관적이고 해석이 쉽기 때문에 현재까지도 널리 사용되는 유용한 도구입니다.

첨부자료

#R코드

# 데이터 입력
A <- c(120, 130, 125, 140, 150)
B <- c(122, 128, 127, 146, 159)

# 평균과 차이 계산
mean_values <- (A + B) / 2
diff_values <- A - B

# 평균 차이와 표준편차, limits of agreement
mean_diff <- mean(diff_values)
std_diff <- sd(diff_values)
loa_upper <- mean_diff + 1.96 * std_diff
loa_lower <- mean_diff - 1.96 * std_diff

# 블랜드-알트만 플롯 그리기
plot(mean_values, diff_values,
     main = "Bland-Altman Plot",
     xlab = "두 혈압계의 평균값",
     ylab = "차이 (A - B)",
     pch = 19,   # 점 모양
     ylim = c(min(diff_values, loa_lower) - 2, max(diff_values, loa_upper) + 2)
)
abline(h = mean_diff, col = "red", lty = 2)       # 평균 차이 선
abline(h = loa_upper, col = "blue", lty = 3)      # 상한선
abline(h = loa_lower, col = "blue", lty = 3)      # 하한선

# 격자 추가
grid()

# 선 옆에 주석 추가
text(x = max(mean_values)-5, y = mean_diff, labels = paste0("mean_diff: ", round(mean_diff, 2)), pos = 3, col = "red")
text(x = max(mean_values)-5, y = loa_upper, labels = paste0("mean_diff+1.96SD: ", round(loa_upper, 2)), pos = 3, col = "blue")
text(x = max(mean_values)-5, y = loa_lower, labels = paste0("mean_diff-1.96SD: ", round(loa_lower, 2)), pos = 1, col = "blue")
반응형

댓글

bigpicture님의
글이 좋았다면 응원을 보내주세요!