创建和解释 Bland-Altman 情节

机器算法验证 数据可视化 解释 散点图 协议统计 平淡的奥特曼情节
2022-03-28 01:43:44

昨天我第一次听说布兰德-奥特曼阴谋我必须比较两种测量血压的方法,并且需要制作一个 Bland-Altman 图。我不确定我是否得到了正确的一切,所以这就是我认为我知道的:

我有两组数据。我计算它们的平均值(x 值)和它们的差值(y 值),并围绕轴 y = 差值平均值绘制它。然后,我计算差异的标准偏差,并将其绘制为“协议限制”。这是我不明白的- 什么协议的限制?通俗地说,95% 的同意是什么意思?这是否应该告诉我(假设散点图的所有点都在“协议限制”之间)方法有 95% 的匹配?

2个回答

你看过我在你的问题中链接的维基百科条目吗?

您没有绘制“数据的平均值”,但是对于以两种方式测量的每个数据点,您绘制两个测量值的差异 ( ) 与两个测量值的平均值 ( )。使用 R 和一些玩具数据:yx

> set.seed(1)
> measurements <- matrix(rnorm(20), ncol=2)
> measurements
            [,1]        [,2]
 [1,] -0.6264538  1.51178117
 [2,]  0.1836433  0.38984324
 [3,] -0.8356286 -0.62124058
 [4,]  1.5952808 -2.21469989
 [5,]  0.3295078  1.12493092
 [6,] -0.8204684 -0.04493361
 [7,]  0.4874291 -0.01619026
 [8,]  0.7383247  0.94383621
 [9,]  0.5757814  0.82122120
[10,] -0.3053884  0.59390132
> xx <- rowMeans(measurements)        # x coordinate: row-wise average
> yy <- apply(measurements, 1, diff)  # y coordinate: row-wise difference
> xx
 [1]  0.4426637  0.2867433 -0.7284346 -0.3097095  0.7272193 -0.4327010  0.2356194  
      0.8410805  0.6985013  0.1442565
> yy
 [1]  2.1382350  0.2061999  0.2143880 -3.8099807  0.7954231  0.7755348 -0.5036193  
      0.2055115  0.2454398  0.8992897
> plot(xx, yy, pch=19, xlab="Average", ylab="Difference")

无协议限制的 Bland-Altman 情节

要获得一致的限制(参见 Wikipedia 页面中的“应用程序”部分),您需要计算差异的平均值和标准偏差,即标准偏差处绘制水平线。y±1.96

> upper <- mean(yy) + 1.96*sd(yy)
> lower <- mean(yy) - 1.96*sd(yy)
> upper
[1] 3.141753
> lower
[1] -2.908468
> abline(h=c(upper,lower), lty=2)

具有协议限制的 Bland-Altman 图

(你看不到协议的上限,因为情节只上升到。)y2.1

至于剧情的解读和约定的界限,再看维基百科:

如果均值 ± 1.96 SD 内的差异在临床上不重要,则两种方法可以互换使用。

如果您想在 Python 中执行此操作,可以使用此代码

import matplotlib.pyplot as plt
import numpy as np
from numpy.random import random
%matplotlib inline
plt.style.use('ggplot')

我刚刚添加了最后一行,因为我喜欢 ggplot 样式。

def plotblandaltman(x,y,title,sd_limit):
    plt.figure(figsize=(20,8))
    plt.suptitle(title, fontsize="20")
    if len(x) != len(y):
        raise ValueError('x does not have the same length as y')
    else:
        for i in range(len(x)):
            a = np.asarray(x)

            b = np.asarray(x)+np.asarray(y)
            mean_diff = np.mean(b)
            std_diff = np.std(b, axis=0)
            limit_of_agreement = sd_limit * std_diff
            lower = mean_diff - limit_of_agreement
            upper = mean_diff + limit_of_agreement

            difference = upper - lower
            lowerplot = lower - (difference * 0.5)
            upperplot = upper + (difference * 0.5)
            plt.axhline(y=mean_diff, linestyle = "--", color = "red", label="mean diff")

            plt.axhline(y=lower, linestyle = "--", color = "grey", label="-1.96 SD")
            plt.axhline(y=upper, linestyle = "--", color = "grey", label="1.96 SD")

            plt.text(a.max()*0.85, upper * 1.1, " 1.96 SD", color = "grey", fontsize = "14")
            plt.text(a.max()*0.85, lower * 0.9, "-1.96 SD", color = "grey", fontsize = "14")
            plt.text(a.max()*0.85, mean_diff * 0.85, "Mean", color = "red", fontsize = "14")
            plt.ylim(lowerplot, upperplot)
            plt.scatter(x=a,y=b)

最后我只是做一些随机值并在这个函数中比较它们

x = np.random.rand(100)
y = np.random.rand(100)
plotblandaltman(x,y,"Bland-altman plot",1.96)

平淡无奇的奥特曼情节

通过一些小的修改,您可以轻松添加一个 for 循环并制作多个绘图