File Exchange

image thumbnail

Bland-Altman and Correlation Plot

version (13 KB) by Ran Klein
Plots a Bland-Altman and correlation plot for two data sets, including data grouping.


Updated 23 Aug 2019

View License

This customizable data analysis tools generates a Bland-Altman and correlation scatter plot. Data can be displayed using color and shape coding of groups using a 2D or 3D matrix notation. Data points can also be displayed using serial numbering, facilitating further interrogation of outliers.
Analysis results (e.g. correlation coefficients, line of best fit, reproducibility coefficient, etc.) can be displayed on the figure. Statistical results are returned as a structured output argument.
An example is included.

Cite As

Ran Klein (2020). Bland-Altman and Correlation Plot (, MATLAB Central File Exchange. Retrieved .

Comments and Ratings (40)

Joao Soares

Maybe would be nice to include a repeated measures option.

Hi Ran

Thank you for uploading this. I am new to MATLAB and so am unsure as to how to go about inserting my variables into this file ? Would you be able to direct me on how to best to this please?

Any help is greatly appreciated.

Kind regards,



Ran Klein

Smilla - thank you for your feedback. I have uploaded a fix as per your recommendation.



Thank you very much for the great function, very useful!
However, I don't understand why you use ranksum and not signrank for testing the differences between the two datasets in the non parametric case.
Should you not use the Wilcoxon Signed Rank test for paired observations ?
Thanks for clarifying.

Ran Klein

Jia Guo - thank you for proposing a solution to Michael Pan's problem. i hope that it applies. Conflicting filenames in the Matlab's search path seems like a probable explanation, but one which is beyond my control.
Michael Pan - please share with us, if you were able to resolve the issue.

Jia Guo

Re. Michael Pan and Ran Klein.
I got the same error when I included SPM12 and had the path near the top. This made Matlab using betapdf defined in SPM12, which does not have lgamma defined (seems it is part of Octave).
I resolved this by moving the SPM12 paths to the bottom, so Matlab loads the betaped defined in the Statistics Toolbox.

BTW, thanks to Ran for this tool!

Ran Klein

Michael Pan - I am unable to reproduce your error, and could use more information. Please confirm that you have the Statistics and Machine Learning Toolbox installed. What Matlab version are you using? Feel free to get in touch with me directly to help resolve these issues.

Michael Pan

Undefined function 'lgamma' for input arguments of type 'double'.

Error in betapdf (line 58)
pdf(k) = exp ((a - 1) * log (x(k))...

Error in betainv (line 87)
h = (betacdf (y_old, a, b) - x) ./ betapdf (y_old, a, b);

Error in finv (line 58)
inv(k) = ((1 ./ betainv (1 - x(k), n/2, m/2) - 1) * n / m);

Error in polyconf (line 99)
crit = sqrt(k * finv(1-alpha,k,S.df)); % Scheffe simultaneous value

Error in BlandAltman>DisplayCorrelationStats (line 467)
[y, delta] = polyconf(stats.polyCoefs, x, stats.polyFitStruct,'simopt','on');

Error in BlandAltman (line 131)
DisplayCorrelationStats(cAH, params, stats);

Error in BlandAltmanDemo (line 52)
[cr, fig, statsStruct] = BlandAltman(data1, data2,label,tit,gnames,'corrInfo',corrinfo,'baInfo',BAinfo,'axesLimits',limits,'colors',colors,
'showFitCI',' on');

Ran Klein

Shrikrishna Basude - Thank you for the positive feedback. The p-value appearing beside the mean line on the BA plot indicates the probability of bias (i.e. is the difference between the two data sets is not zero?).
- In Gaussian mode the one-sample, two-sided t-test of the differences between the paired data is used.
- In non-parametric mode the two-sided Wilcoxon rank sum test is used.
I hope this clarifies things.

Hello. I love the function. One question: What type of test is the P value from that appears on the BA plot mean line?

Hamid Fehri

Ran Klein

Complaints by Rik Wisselink and Cheryl Sital were addressed in version 1.7.


Really nice submission, except it doesn't seem to handle 'data1Mode' set to 'Truth' very well in the labels if you don't supply a unit as well. This can be resolved by replacing the current elseif the ResolveLabels subfunction with the one below.

elseif length(label)==2
if strcmpi(params.data1TreatmentMode,'Compare')
params.d1Label = label{1};
params.d2Label = label{2};
params.meanLabel = ['Mean ' label{1} ' & ' label{2}];
params.deltaLabel = [label{2} ' - ' label{1}];
params.meanLabel = label{1};
params.d1Label = label{1};
params.d2Label = label{2};
switch upper(params.diffValueMode)
diffUnits = units;
diffUnits = '';
params.baYLimMode = 'Auto';
case 'PERCENT'
diffUnits = '%';
params.baYLimMode = 'Auto';
error(['Unsupported diffValueMode ' params.diffValueMode])
params.deltaLabel = [label{2} ' - ' label{1}];

Should be added to the Stats Toolbox as a primitive, don't you think so ?


Thank you for sharing your script! It has helped me out a lot. I do, however, have an issue with the y-axes of my BA plots. I specified 'baYLimMode' to be 'auto' but some of my plots are still cut-off randomly (some points are not shown or in some plots one limit of agreement has been left out entirely). Could you please tell me how I can fix this?

Ran Klein

Andrew Stephens - Ignore my previous message, please. You are missing the labels input parameter (after 'Title' and before 'baStatsMode') in your example code snippet. Perhaps this is the cause of the error. Feel free to contact me for clarification.

Ran Klein

Liyun Yang – Please refer to and note that IQR ≈ 1.35σ for a normal distribution. Since RPC = 1.96σ by definition for a normal distribution (95% confidence interval), then RPCnp ≈ 1.96 / 1.35 · IQR = 1.45·IQR. I hope this addresses your concern.

Liyun Yang

It says that for nonparametric data, RPCnp = 1.45*IQR ~ RPC and uses 1.45*IQR for nonparametric data automatically, with a reference: Peck, Olsen and Devore, Introduction to Statistics and Data Analysis. Nelson Education, 2011. However I couldn't find the reference stating this. Anyone has more explaination for it?

Great function, some options don't seem to work (e.g 'baStatsMode','non-parametric').

"BlandAltman(Data1,Data2,{'Axis 1', 'Axis 2'},'Title','baStatsMode','Non-parametric');"

Ran Klein

Thank you all for the feedback. In the latest version these bugs are resolved.


To fix some issues mentioned below I use a dummy function:
function numstr = mynum2str(varargin)
numstr = num2str(varargin{1});
And you might need to get the "polyfitZero" function from Matlab File Exchange and paste it to the skript.


It would be nice if the function mynum2str would be defined or the code would rely on standard num2str function.
It is very hard to reconstruct mynum2str without further specifications.
Fixing this would make this contribution a great tool.

Nice code, works really well, I only have problems using the 'forceZeroIntercept' option. Could you elaborate on how to run it? I tried BlandAltman(data1,data2,'forceZeroIntercept','on'); and without the 'on' there but the code seems to use it as labels for the graphs and the slopes that come out are still the ones with intercepts.


I got the same error

Undefined function 'mynum2str' for input arguments of type 'double'

Any suggestion to fix this error?


I simply pass it two data sets and get:

Undefined function or variable 'mynum2str'.

Error in BlandAltman>DisplayCorrelationStats (line 454)
corrtext = [corrtext; ['y=' mynum2str(stats.slope,3,2) 'x']];

Error in BlandAltman (line 126)
DisplayCorrelationStats(cAH, params, stats);


Thanks a lot for sharing. However the 'mynum2str' function seems to be missing. Would appreciate it if you may fix this.

Ran Klein

Erik, thank you for your comments. I was just in the process of updating this tool based on other feedback. The SSE has been corrected, the usability improved (hopefully) and new functionality added. Please feel free to get in touch with me if you have additional feedback.


Quite functional, but not very easy to use and the plot is mediocre: the text is way too small and can be badly positioned, which makes it illegible. The SSE calculation is incorrect. It's actually the root mean squared error (RMSE = sqrt(MSE) = sqrt(SSE/(N-2)) in the case of the first order polynomial fit).

Also, the function always returns an output, which is not always desired.

Excellent piece of code. Could use it right out of the box. Perfect to show any correlation in the data if it's existing.

excellent contribution , good code thanks

Ran Klein

Botswana Serengeti, excellent comment. The input data does not imply whether they are correlated or not (regardless of the grouping). It is assumed that all data is independent. User beware!


Thanks for suptitle contribution!

small quick and dirty bugfix for figures with many subplots: pos array can get smaller zero.
add "pos(pos<0) = 0.05;" in line 97 of suptitle.m

Nice work, except there is a dangerous limitation in calculating limits of agreement.
If grouping is available, this usually also means that data is not independent and 1.96 sd limits do not apply in BA plots. See also Bland JM, Altman DG. Measuring agreement in method comparison studies. Stat Methods Med Res. 1999;8(2):135–60. for an example implementation.


Fixed non-parametric comparison to use signrank instead of ranked sum (credits to Smilla for bringing this issue to my attention).
Also includes minor comment improvements.

Changes include:
- Explicit support (including demo) for correlation analysis only with correlationPlot.m.
- Improved support for titles and legend with multiple analyses on a figure.
- Legend on/off.
- Text remains on plots when resized.

Corrected code typos as reported by Andrew Stephens. Thank you, Andrew.

Addressed bugs indicated by Rik Wisselink and Cheryl Sital in version 1.7.

Added support for data containing nan values.

Minor comment updates.

Corrected bug with wrong labelling of Bland-Altman x-axis when using data1Mode = 'Truth'.

Added mynum2str and fixed bug with equation option not showing intercept default. Thank you everyone for your comments.

New feature include:
- Parameter-value pair input arguments (incompatible with V1.1).
- Gaussian/non-parametric statistics (including warning for data that may violate this assumption)
- Absolute/relative differences
- Correction of SSE calculation

Added more default colours and the option to specify colours both as code characters and RGB table. See the demo file for and example.

MATLAB Release Compatibility
Created with R2012b
Compatible with any release
Platform Compatibility
Windows macOS Linux