Finding x position of color-grouped boxchart boxes for overlaying jittered errorbar plot

22 views (last 30 days)
I'm writing a GUI that creates a boxchart from the results of a fitting operation. Each data point has an x and color category and a confidence interval associated with it. I'd like to create a boxchart that groups data points categorically on x and color, and then overlay an errorbar plot that shows each individual data point data point and its confidence interval. There could be any number of color and x categories.
My issue is that I can't figure out how to calculate or find the x position of each of the boxes, which I need to ensure the errorbar plots line up. It seems like this should be a property of the BoxChart object, but I can't find it.
For example, if I create the following data set, which uses either a two or three color categories:
% Generate some data
xcat = categorical({'a', 'b', 'c'});
ccat = categorical({'c1'; 'c2'; 'c3'}); % 3 color categories
%ccat = categorical({'c1'; 'c2'; 'c2'}); % 2 color categories
xdata = reshape(repmat(xcat, 12, 1), [], 1);
cdata = reshape(repmat(ccat, 4, 3), [], 1);
ydata = reshape([normrnd(10, 1, 12, 1), normrnd(11, 1, 12, 1), normrnd(8, 1, 12, 1)], [], 1);
cfdata = reshape(normrnd(0, 0.5, 12, 3), [], 1);
And I create a boxchart from it like this:
% Convert from categorical to numerical for plot
xcat_names = categories(xdata);
xdata_num = double(xdata);
% Create boxchart
figure
b = boxchart(xdata_num, ydata, 'GroupByColor', cdata);
set(gca, "XTick", unique(xdata_num), "XTickLabel", xcat_names);
legend
The following error bar code works perfectly when there's three colors, but doesn't align properly if there's two:
% Estimate errorbar positions
ccat_names = categories(cdata);
cgroupwidth = 1-1/length(ccat_names);
xspacing = cgroupwidth/length(ccat_names);
xcoloroffset = (0:length(ccat_names)-1) * xspacing - cgroupwidth/2 + xspacing/2;
% Add some jitter for readability
xjitteroffset = zeros(size(xdata)); % Zeros for testing alignment with no jitter
%xjitteroffset = randn(size(xdata))*cgroupwidth/length(ccat_names)/4;
% Overlay jittered errorbars on boxchart
cmap_c = lines(length(categories(cdata))); % match color of boxchart
hold on
for ix = 1:length(xcat_names)
for ic = 1:length(ccat_names)
ibox = xdata == xcat(ix) & cdata == ccat(ic); % find data that fits criteria
errorbar(xdata_num(ibox) + xcoloroffset(ic) + xjitteroffset(ibox),...
ydata(ibox), cfdata(ibox), '.', 'Color', cmap_c(ic,:),...
'HandleVisibility','off')
end
end
grid on
This code results in the following graphs depending on which ccat definition is commented out. You can see that the errorbars don't align in the 2 Color scenario.
So in summary, I'm interested if anyone knows:
  • A way to get the x position of the boxes from the BoxChart object
  • A way to calculate the x position of the boxes reliably with varying numbers of color and x categories.

Accepted Answer

Theo Husby
Theo Husby on 1 Oct 2025 at 15:23
I figured out a solution based on this answer given for bar graphs in this answer.
The box position can be calculated reliably, and I was close with my position estimate, but had the wrong definition of cgroupwidth. In my original question, the line should be:
cgroupwidth = min(0.8, length(ccat_names)/(length(ccat_names) + 1.5));
Here is the implementation written out as a function in case it's useful to anyone who encounters the same problem in the future:
function xboxpos = get_boxchart_x_position(xdata_num, nc)
% Return the x position of each individual box in a boxchart that uses 'GroupByColor'.
% xdata_num is a numerical vector. Same as the first input to boxchart.
% nc is the number of color categories
xgroup = unique(xdata_num);
nx = length(xgroup);
% Calculate offset for colors
cgroupwidth = min(0.8, nc/(nc + 1.5));
xspacing = cgroupwidth/nc;
xcoloroffset = (0:nc-1) * xspacing - cgroupwidth/2 + xspacing/2;
% Calculate x positions of each boxchart box assuming the same number
% of colors for each x position.
xboxpos = zeros(nx * nc, 1);
for ix = 1:nx
for ic = 1:nc
xboxpos((ix-1)*nc + ic) = xgroup(ix) + xcoloroffset(ic);
end
end
end
Example usage:
% Create a boxchart
boxchart(xdata_num, ydata, 'GroupByColor', cdata);
% Get number of color categories. cdata is categorical in my case.
nc = length(categories(cdata));
% Call function to get x position of each box in the chart.
xboxpos = get_boxchart_x_position(xdata_num, nc);
If anyone knows a cleaner solution, please let me know.

More Answers (0)

Products


Release

R2025a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!