Source code for statsmodels.graphics.agreement

"""
Bland-Altman mean-difference plots

Author: Joses Ho
License: BSD-3
"""

import numpy as np

from . import utils


[docs] def mean_diff_plot( m1, m2, sd_limit=1.96, ax=None, scatter_kwds=None, mean_line_kwds=None, limit_lines_kwds=None, ): """ Construct a Tukey/Bland-Altman Mean Difference Plot. Tukey's Mean Difference Plot (also known as a Bland-Altman plot) is a graphical method to analyze the differences between two methods of measurement. The mean of the measures is plotted against their difference. For more information see https://en.wikipedia.org/wiki/Bland-Altman_plot Parameters ---------- m1 : array_like A 1-d array. m2 : array_like A 1-d array. sd_limit : float The limit of agreements expressed in terms of the standard deviation of the differences. If `md` is the mean of the differences, and `sd` is the standard deviation of those differences, then the limits of agreement that will be plotted are md +/- sd_limit * sd. The default of 1.96 will produce 95% confidence intervals for the means of the differences. If sd_limit = 0, no limits will be plotted, and the ylimit of the plot defaults to 3 standard deviations on either side of the mean. ax : AxesSubplot If `ax` is None, then a figure is created. If an axis instance is given, the mean difference plot is drawn on the axis. scatter_kwds : dict Options to to style the scatter plot. Accepts any keywords for the matplotlib Axes.scatter plotting method mean_line_kwds : dict Options to to style the scatter plot. Accepts any keywords for the matplotlib Axes.axhline plotting method limit_lines_kwds : dict Options to to style the scatter plot. Accepts any keywords for the matplotlib Axes.axhline plotting method Returns ------- Figure If `ax` is None, the created figure. Otherwise the figure to which `ax` is connected. References ---------- Bland JM, Altman DG (1986). "Statistical methods for assessing agreement between two methods of clinical measurement" Examples -------- Load relevant libraries. >>> import statsmodels.api as sm >>> import numpy as np >>> import matplotlib.pyplot as plt Making a mean difference plot. >>> # Seed the random number generator. >>> # This ensures that the results below are reproducible. >>> np.random.seed(9999) >>> m1 = np.random.random(20) >>> m2 = np.random.random(20) >>> f, ax = plt.subplots(1, figsize = (8,5)) >>> sm.graphics.mean_diff_plot(m1, m2, ax = ax) >>> plt.show() .. plot:: plots/graphics-mean_diff_plot.py """ fig, ax = utils.create_mpl_ax(ax) if len(m1) != len(m2): raise ValueError("m1 does not have the same length as m2.") if sd_limit < 0: raise ValueError(f"sd_limit ({sd_limit}) is less than 0.") means = np.mean([m1, m2], axis=0) diffs = m1 - m2 mean_diff = np.mean(diffs) std_diff = np.std(diffs, axis=0) scatter_kwds = scatter_kwds or {} if "s" not in scatter_kwds: scatter_kwds["s"] = 20 mean_line_kwds = mean_line_kwds or {} limit_lines_kwds = limit_lines_kwds or {} for kwds in [mean_line_kwds, limit_lines_kwds]: if "color" not in kwds: kwds["color"] = "gray" if "linewidth" not in kwds: kwds["linewidth"] = 1 if "linestyle" not in mean_line_kwds: kwds["linestyle"] = "--" if "linestyle" not in limit_lines_kwds: kwds["linestyle"] = ":" ax.scatter(means, diffs, **scatter_kwds) # Plot the means against the diffs. ax.axhline(mean_diff, **mean_line_kwds) # draw mean line. # Annotate mean line with mean difference. ax.annotate( f"mean diff:\n{mean_diff:0.3g}", xy=(0.99, 0.5), horizontalalignment="right", verticalalignment="center", fontsize=14, xycoords="axes fraction", ) if sd_limit > 0: half_ylim = (1.5 * sd_limit) * std_diff ax.set_ylim(mean_diff - half_ylim, mean_diff + half_ylim) limit_of_agreement = sd_limit * std_diff lower = mean_diff - limit_of_agreement upper = mean_diff + limit_of_agreement for j, lim in enumerate([lower, upper]): ax.axhline(lim, **limit_lines_kwds) ax.annotate( f"-{sd_limit} SD: {lower:0.2g}", xy=(0.99, 0.07), horizontalalignment="right", verticalalignment="bottom", fontsize=14, xycoords="axes fraction", ) ax.annotate( f"+{sd_limit} SD: {upper:0.2g}", xy=(0.99, 0.92), horizontalalignment="right", fontsize=14, xycoords="axes fraction", ) elif sd_limit == 0: half_ylim = 3 * std_diff ax.set_ylim(mean_diff - half_ylim, mean_diff + half_ylim) ax.set_ylabel("Difference", fontsize=15) ax.set_xlabel("Means", fontsize=15) ax.tick_params(labelsize=13) fig.tight_layout() return fig

Last update: Dec 16, 2024