Code covered by the BSD License

boxplotstack

Laurent S (view profile)

08 Aug 2011 (Updated )

Displays stacked box plots with labels.

boxplotstack(X,xtick,labels,opacity,bgcolor)
```function boxplotstack(X,xtick,labels,opacity,bgcolor)
%BOXPLOTSTACK Displays stacked box plots with labels.
%   boxplotstack(X) displays size(X,2) stacks of box plots, each stack
%   consisting of size(X,1) box plots if X is a cell array, or just one box
%   plot per stack if X is an matrix.
%
%   boxplotstack(X,xtick) sets the x-tick label underneath the n-th stack
%   to xtick{n}.
%
%   boxplotstack(X,xtick,labels) prints the text labels{i,j} next to the
%   box plot corresponding to X{i,j}. If labels{i,j} is a number, it will
%   be printed as a percentage and the opacity values will be taken from
%   labels.
%
%   boxplotstack(X,xtick,labels,opacity) prints the text labels{i,j} next
%   to the box plot corresponding to X{i,j}, with background colour black
%   and opacity equal to opacity{i,j}.
%
%   boxplotstack(X,xtick,labels,opacity,bgcolor) prints the text
%   labels{i,j} next to the box plot corresponding to X{i,j}, with
%   background colour bgcolor{i,j} and opacity equal to opacity{i,j}. The
%   colour should be specified as an RGB triplet [r g b].

%   Version: 2013-05-21
%   Authors: Laurent Sorber (Laurent.Sorber@cs.kuleuven.be)

% Parse input.
if ~iscell(X), X = mat2cell(X,size(X,1),ones(1,size(X,2))); end
if nargin < 2, xtick = {}; end
if nargin < 3, labels = cell(size(X)); end
if nargin < 4, opacity = labels; end
if nargin < 5, bgcolor = cell(size(X)); end

% Compute each box plot's statistics.
stats = cell(size(X));
for j = 1:size(X,2)
q2 = -inf(size(X,1),1);
for i = 1:size(X,1)

% Skip this box plot if there is no X associated to it.
if isempty(X{i,j}), continue; end

% Compute the first, second and third quartiles, and whiskers.
x = sort(X{i,j});
n = length(x);
stat.q2 = median(x);
stat.q1 = median(x(x<stat.q2));
stat.q3 = median(x(x>stat.q2));
stat.w1 = x(find(x >= stat.q1-1.5*(stat.q3-stat.q1),1,'first'));
stat.w3 = x(find(x <= stat.q3+1.5*(stat.q3-stat.q1),1,'last'));
stats{i,j} = stat;
q2(i) = stat.q2;

end

% Sort this column's boxplots in drawing order.
[tmp,idx] = sort(q2,'descend');
stats(:,j) = stats(idx,j);
labels(:,j) = labels(idx,j);
opacity(:,j) = opacity(idx,j);
bgcolor(:,j) = bgcolor(idx,j);

end

% Draw each box plot.
for j = 1:size(stats,2)
prevpos = 'r';
for i = 1:size(stats,1)

% Skip this box plot if there is no X associated to it.
if isempty(stats{i,j}), continue; end

% Retrieve the previous, current and next statistics.
statcur = stats{i,j};
hasprev = i > 1 && ~isempty(stats{i-1,j});
hasnext = size(stats,1) > 1 && ...
i < size(stats,1) && ...
~isempty(stats{i+1,j});
if hasprev, statprev = stats{i-1,j}; end
if hasnext, statnext = stats{i+1,j}; end

% Determine the offset of this box plot, to prevent overlap.
pos = 'c';
if hasnext && statnext.w3 >= statcur.w1 || ...
hasprev && statprev.w1 <= statcur.w3
if strcmp(prevpos,'l'), pos = 'r'; else pos = 'l'; end
end

% Draw the box plot.
drawboxplot(statcur,labels{i,j},opacity{i,j},bgcolor{i,j},j,pos);
prevpos = pos;

end
end

% Set up the axes.
xlim([0.5 size(X,2)+.5]);
set(gca,'XTick',1:size(X,2),'YGrid','on');
if ~isempty(xtick), set(gca,'XTickLabel',xtick); end
hold off;

end

function drawboxplot(stat,label,opac,bgclr,x,pos)

% Define parameters.
obox = 0.13; % The offset of the box if the position is 'l' or 'r'.
wbox = 0.22; % The box width.
wwsk = 0.11; % The whisker width.
mtxt = 0.05; % The margin between the labels and boxes.
ptxt = 1;    % The backgroundcolor margin of the labels in pixels.
if isempty(bgclr)
ctxt = [0 0 0]; % The fade-to label background colour (fade-from is w).
else
ctxt = bgclr;
end

% Get box statistics.
q1 = stat.q1;
q2 = stat.q2;
q3 = stat.q3;
w1 = stat.w1;
w3 = stat.w3;

% Draw box.
switch pos
case 'l', x = x-obox;
case 'r', x = x+obox;
end
plot([x;x],[q3 w3],'--k'); hold on;
plot([x-wwsk/2;x+wwsk/2],[w3;w3],'k');
plot([x;x],[w1 q1],'--k');
plot([x-wwsk/2;x+wwsk/2],[w1;w1],'k');
plot([x-wbox/2;x-wbox/2;x+wbox/2;x+wbox/2;x-wbox/2],[q1;q3;q3;q1;q1],'b');
plot([x-wbox/2;x+wbox/2],[q2;q2],'r');

% Draw labels.
style = {'Margin',ptxt};
switch pos
case 'l',
x = x-wbox/2-mtxt;
style(end+1:end+2) = {'HorizontalAlignment','right'};
case {'r','c'}
x = x+wbox/2+mtxt;
end
if isnumeric(label) && ~isempty(label)
label = sprintf('%i%%',round(label*100));
end
if isnumeric(opac) && ~isempty(opac)
style(end+1:end+2) = {'BackgroundColor', ...
min(1,(1-opac)*[1 1 1]+opac*ctxt)};
%if round(100*opac) > 50, style(end+1:end+2) = {'Color','w'}; end
end
if ~isempty(label)
text(x,q2,label,style{:});
end

end
```