212 views (last 30 days)

Show older comments

HI,

with r2020 there is finally a grouped boxplot called "boxchart". However. I don't seem to get the structure and options of it. Multiple questions.

How does one apply any property simultaneously to all boxes instead of looping? For instance like b.Notch = 'on' or a colormap to the ColorGroup? The standard colors especially their order is not what I'm looking for. however, assigning a colormap doesn't work, nor can I apply a b=boxchart()... b.BoxFaceColor = parula(size(b,1)) with n-colors for n-Groups. What am I not seeing here? All I can do is loop over all.

cols = parula(size(b,1));

for i = 1:size(b,1)

bb(i).BoxFaceColor = cols(i,:);

end

% this seems extremely cumbersome

here I have looped over each bb(i) as shown above in order to apply a colr map of my choice. .

Another question that pops up: How could I add separation lines? In the example image above I have added them with an image editor.. obviously not the choice!

Next: Why can't I seem to get a correct looking diagram when plotting when input xGroupData are unique numbers of discrete groups.. The diagram's x-axis or x-grouping is then entirely messed up.

xgroupdata = repmat([0, 0.0125, 0.0250, 0.0375], [1,100]);

ydata = rand(size(xgroupdata));

group = datasample(1:1:5,size(xgroupdata,2));

figure;

boxchart(xgroupdata,ydata, 'GroupByColor', group);

legend;

here is what that code produces. Only Group3 seems to be correctly placed at the proper xgroup-values. But the automatic box-width, doesn't work and the group spacing is also totally messed up, seems to be 0.2 but I can't even find a property in boxchart's documentation

I would like to be able to add a textlabel to any of the boxes with the sample size. For this I need to a) figure out the position of the boxes but I don't manage to understand the structure of the handle... there are zero Children for instance...

Adam Danz
on 21 Dec 2020

Edited: Adam Danz
on 5 Jan 2021

boxchart vs boxplot

>with r2020 there is finally a grouped boxplot called "boxchart".

The boxplot() function also has grouping options, has been around for much longer, and is often easier to customize than the newer standalone visualzation boxchart function.

ColorGroup & Notches

>How does one apply any property simultaneously to all boxes instead of looping? For instance like b.Notch = 'on' or a colormap to the ColorGroup?

To apply changes to a group of handles to existing graphics objects, follow this example, although the loop you mentioned is also fine and just as fast (faster?),

h = boxchart(___); % h is a vector of handles

set(h, {'BoxFaceColor'}, mat2cell(jet(numel(h)),ones(numel(h),1),3)) % jet is a colormap

Matlab documentation has a demo showing how to change colors in single bars or groups.

Separation lines

>How could I add separation lines?

For boxchart, this option currently doesn't exist (r2020b). If the x-axis is numeric, add divider lines using,

h = boxchart(__);

xline(x) % where x is the location of the line

If the x-axis is categorical it's a lot more complicated because divider lines cannot be added between categories. To add divider lines on a categorical x-axis you have to add additional categories to the x axis ticks, set their order, and then use xline() (or plot() or line()) with categorical x-values.

% Create boxchart showing 12 months of data

monthNum = randi(12,200,1);

data = rand(200,1)*100.*(normpdf(monthNum,6,1)+.25);

monthAbrv = categorical(monthNum,1:12,month(datetime(1, 1:12, 1), 's'));

figure()

h = boxchart(monthAbrv, data);

% Separate seasons by divider lines

% First add categories to the x-axis

ax = h.Parent; % axis handle

ax.XAxis.Categories' % view current categories.

ax.XAxis.Categories = {

'Jan' 'Feb' 'Spring',...

'Mar' 'Apr' 'May' 'Summer',...

'Jun' 'Jul' 'Aug' 'Fall',...

'Sep' 'Oct' 'Nov' 'Winter',...

'Dec'};

% Add xlines on seasonal categories

arrayfun(@(x)xline(x,'r-','LineWidth',1.5),categorical({'Spring','Summer','Fall','Winter'}))

% xline(__) for single lines

% if you want to label each line,

% arrayfun(@(x)xline(x,'r-',x,'LineWidth',1.5),categorical({'Spring','Summer','Fall','Winter'}))

% If needed, removed seasonal tick labels

ax.XTickLabel([3,7,11,15]) = {''};

Fix overlapping boxes in boxchart

>Why can't I seem to get a correct looking diagram when plotting [with grouped data]?

You can fix the overlapping boxes by making the grouping variable categorical

boxchart(categorical(xgroupdata),ydata, 'GroupByColor', group);

Adding text labels

> I would like to be able to add a textlabel to any of the boxes

Use the text() function. See the bottom of this answer to learn how to get the x value of the boxplot centers.

For numeric axes it's as easy as using the text() function but for categorical axes, you must use categorical coordinates. This demo below shows the number of samples in each category and plots that number above the median lines.

% Produce boxchart

figure()

h = boxchart(randi(5,100,1),rand(100,1));

% compute median and number of samples for each box

[groupID, groups] = findgroups(h.XData);

medians = splitapply(@median,h.YData,groupID);

nSamples = splitapply(@numel,h.YData,groupID);

% Plot text

text(groups, medians, compose('n=%d',nSamples),...

'HorizontalAlignment','Center','VerticalAlignment','bottom', ...

'FontSize',8)

Extracting location of boxs

>I need to a) figure out the position of the boxes but I don't manage to understand the structure of the handle

Extracting the location of the boxes from the graphics handle involves accessing each graphics element and examining its coordinates. It's not easy but achievable with boxplot (see this answer). I haven't figured out a way to achieve this with boxchart as of r2020b.

Of course many of the parameters can be computed using the raw data.

The median marker are easy to get using,

h = boxchart(__);

[groupID, groups] = findgroups(h.XData);

medians = splitapply(@median,h.YData,groupID);

The upper and lower edges of the boxes are the 25th and 75th quartiles,

h = boxchart(__);

[groupID, groups] = findgroups(h.XData);

bounds = splitapply(@(y)quantile(y,[.25,.75]),h.YData,groupID);

The whisker ends are a bit more difficult. They mark the max/min values that are not outliers so first you have to compute the outlier thresholds. Outliers are any values that are greater than 1.5*IQR from the medians. IQR is computed for each box below. See documentation for more info.

h = boxchart(__);

[groupID, groups] = findgroups(h.XData);

IQR = splitapply(@iqr,h.YData,groupID);

The box centers and Left/Right edges for numeric axes are calculated using

h = boxchart(__);

boxCenters = unique(h.XData);

boxEdges = boxCenters(:) + h.BoxWidth.*[-1,1]; % for numeric matrices only

Adam Danz
on 5 Jan 2021

Adding divider lines and text is tricky but I'll update the answer to show how.

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

Start Hunting!