Yesterday I heard of the [Bland-Altman plot](https://en.wikipedia.org/wiki/Bland%E2%80%93Altman_plot) for the first time. I have to compare two methods of measuring blood pressure, and I need to produce a Bland-Altman plot. I am not sure if I get everything about it right, so here's what I think I know:

I have two sets of data. I calculate their mean (x value) and their difference (y value) and plot it around the axis y = mean of difference. Then, I calculate the standard deviation of the difference, and plot it as "limits of agreement". This is what I do not understand - limits of what agreement? What means 95% agreement in layman's terms? Is that supposed to tell me (provided that all points of the scatter graph are between the "limits of agreement") that the methods have 95% match?
['https://stats.stackexchange.com/questions/99835', 'https://stats.stackexchange.com', 'https://stats.stackexchange.com/users/46069/']
Have you looked at [the Wikipedia entry](https://en.wikipedia.org/wiki/Bland%E2%80%93Altman_plot) I linked in your question?

You don't plot "the mean of the data", but for each data point measured in two ways, you plot the difference in the two measurements ($y$) against the average of the two measurements ($x$). Using R and some toy data:

```r
> 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 plot without limits of agreement](https://i.stack.imgur.com/vdzoe.png)

To get the limits of agreement (see under "Application" in the Wikipedia page), you calculate the mean and the standard deviation of the differences, i.e., the $y$ values, and plot horizontal lines at the mean $\pm 1.96$ standard deviations.

```r
> 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 plot with limits of agreement](https://i.stack.imgur.com/piesA.png)

(You can't see the upper limit of agreement because the plot only goes up to $y\approx 2.1$.)

As to the interpretation of the plot and the limits of agreement, again look to Wikipedia: 

> 
> If the differences within mean ± 1.96 SD are not clinically important, the two methods may be used interchangeably.
> 
> 
>
If you would like to do this in Python you can use this code

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

```

I just added the last line because I like the ggplot style.

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

```

And finaly I just make some random values and compare them in this function

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

```

[![Bland-altman plot](https://i.stack.imgur.com/OYo2f.png)](https://i.stack.imgur.com/OYo2f.png)

With some minor modification, you can easily add a for-loop and make several plots