Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/statsmodels/graphics/agreement.py : 6%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1'''
2Bland-Altman mean-difference plots
4Author: Joses Ho
5License: BSD-3
6'''
8import numpy as np
9from . import utils
12def mean_diff_plot(m1, m2, sd_limit=1.96, ax=None, scatter_kwds=None,
13 mean_line_kwds=None, limit_lines_kwds=None):
14 """
15 Construct a Tukey/Bland-Altman Mean Difference Plot.
17 Tukey's Mean Difference Plot (also known as a Bland-Altman plot) is a
18 graphical method to analyze the differences between two methods of
19 measurement. The mean of the measures is plotted against their difference.
21 For more information see
22 https://en.wikipedia.org/wiki/Bland-Altman_plot
24 Parameters
25 ----------
26 m1 : array_like
27 A 1-d array.
28 m2 : array_like
29 A 1-d array.
30 sd_limit : float
31 The limit of agreements expressed in terms of the standard deviation of
32 the differences. If `md` is the mean of the differences, and `sd` is
33 the standard deviation of those differences, then the limits of
34 agreement that will be plotted are md +/- sd_limit * sd.
35 The default of 1.96 will produce 95% confidence intervals for the means
36 of the differences. If sd_limit = 0, no limits will be plotted, and
37 the ylimit of the plot defaults to 3 standard deviations on either
38 side of the mean.
39 ax : AxesSubplot
40 If `ax` is None, then a figure is created. If an axis instance is
41 given, the mean difference plot is drawn on the axis.
42 scatter_kwds : dict
43 Options to to style the scatter plot. Accepts any keywords for the
44 matplotlib Axes.scatter plotting method
45 mean_line_kwds : dict
46 Options to to style the scatter plot. Accepts any keywords for the
47 matplotlib Axes.axhline plotting method
48 limit_lines_kwds : dict
49 Options to to style the scatter plot. Accepts any keywords for the
50 matplotlib Axes.axhline plotting method
52 Returns
53 -------
54 Figure
55 If `ax` is None, the created figure. Otherwise the figure to which
56 `ax` is connected.
58 References
59 ----------
60 Bland JM, Altman DG (1986). "Statistical methods for assessing agreement
61 between two methods of clinical measurement"
63 Examples
64 --------
66 Load relevant libraries.
68 >>> import statsmodels.api as sm
69 >>> import numpy as np
70 >>> import matplotlib.pyplot as plt
72 Making a mean difference plot.
74 >>> # Seed the random number generator.
75 >>> # This ensures that the results below are reproducible.
76 >>> np.random.seed(9999)
77 >>> m1 = np.random.random(20)
78 >>> m2 = np.random.random(20)
79 >>> f, ax = plt.subplots(1, figsize = (8,5))
80 >>> sm.graphics.mean_diff_plot(m1, m2, ax = ax)
81 >>> plt.show()
83 .. plot:: plots/graphics-mean_diff_plot.py
84 """
85 fig, ax = utils.create_mpl_ax(ax)
87 if len(m1) != len(m2):
88 raise ValueError('m1 does not have the same length as m2.')
89 if sd_limit < 0:
90 raise ValueError('sd_limit ({}) is less than 0.'.format(sd_limit))
92 means = np.mean([m1, m2], axis=0)
93 diffs = m1 - m2
94 mean_diff = np.mean(diffs)
95 std_diff = np.std(diffs, axis=0)
97 scatter_kwds = scatter_kwds or {}
98 if 's' not in scatter_kwds:
99 scatter_kwds['s'] = 20
100 mean_line_kwds = mean_line_kwds or {}
101 limit_lines_kwds = limit_lines_kwds or {}
102 for kwds in [mean_line_kwds, limit_lines_kwds]:
103 if 'color' not in kwds:
104 kwds['color'] = 'gray'
105 if 'linewidth' not in kwds:
106 kwds['linewidth'] = 1
107 if 'linestyle' not in mean_line_kwds:
108 kwds['linestyle'] = '--'
109 if 'linestyle' not in limit_lines_kwds:
110 kwds['linestyle'] = ':'
112 ax.scatter(means, diffs, **scatter_kwds) # Plot the means against the diffs.
113 ax.axhline(mean_diff, **mean_line_kwds) # draw mean line.
115 # Annotate mean line with mean difference.
116 ax.annotate('mean diff:\n{}'.format(np.round(mean_diff, 2)),
117 xy=(0.99, 0.5),
118 horizontalalignment='right',
119 verticalalignment='center',
120 fontsize=14,
121 xycoords='axes fraction')
123 if sd_limit > 0:
124 half_ylim = (1.5 * sd_limit) * std_diff
125 ax.set_ylim(mean_diff - half_ylim,
126 mean_diff + half_ylim)
127 limit_of_agreement = sd_limit * std_diff
128 lower = mean_diff - limit_of_agreement
129 upper = mean_diff + limit_of_agreement
130 for j, lim in enumerate([lower, upper]):
131 ax.axhline(lim, **limit_lines_kwds)
132 ax.annotate('-SD{}: {}'.format(sd_limit, np.round(lower, 2)),
133 xy=(0.99, 0.07),
134 horizontalalignment='right',
135 verticalalignment='bottom',
136 fontsize=14,
137 xycoords='axes fraction')
138 ax.annotate('+SD{}: {}'.format(sd_limit, np.round(upper, 2)),
139 xy=(0.99, 0.92),
140 horizontalalignment='right',
141 fontsize=14,
142 xycoords='axes fraction')
144 elif sd_limit == 0:
145 half_ylim = 3 * std_diff
146 ax.set_ylim(mean_diff - half_ylim,
147 mean_diff + half_ylim)
149 ax.set_ylabel('Difference', fontsize=15)
150 ax.set_xlabel('Means', fontsize=15)
151 ax.tick_params(labelsize=13)
152 fig.tight_layout()
153 return fig