function varargout = kruskalbar(data,groups,alpha)
% Provide data as a matrix (each column will be treated separately), or
% as grouped data with barwitherr(data, 'grouped'), in which case 'data'
% is a 2-column matrix with the first column containing the data adn the
% second column containing group number.
% Original by Martina F. Callaghan; I have changed it quite a bit to now
% compute means and errors by itself from the data provided, as well as to
% do tests for significance between groups and from zero for each group.
%
% This function does not assume normality and therefore plots medians,
% 75% confidence intervals, and performs non-parametric significance
% tests (signrank for each group to zero and kruskawallis between
% groups). For a parametric equivalent, use barwitherrn..
%
% Copyright (C) 2015 by Ralitsa Todorova
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.
if nargin<3,
alpha = 0.05;
end
grouped = false;
data = double(data);
if nargin==1,
if sum(sum(~isnan(data))==0) > 0, %if one of the vectors contains no real values
return
end
xOrder = 1:size(data,2);
values = nanmedian(data);
errors = semedian(data);
% errors = quantile(data,0.75)-values;
% values = nanmean(data);
% errors = nansem(data) - values;
else
grouped = true;
if isastring(groups,'groups','grouped','group'),
groups = data(:,end);
data = data(:,1:end-1);
end
xOrder = unique((groups))';
n = numel(xOrder);
values = nan(size(data,2),n);
errors = nan(size(data,2),n);
for i=1:n,
group = xOrder(i);
values(:,i) = nanmedian(data(groups==group,1:end));
errors(:,i) = semedian(data(groups==group,1:end));
% errors(:,i) = quantile(data(groups==group,1:end),0.75)' - values(:,i);
end
if size(data,2)>1,
xOrder = 1:size(data,2);
end
groups = double(groups);
end
[nRows nCols] = size(values);
if sum(size(xOrder)~=size(values))==0,
handles.bar = bar(xOrder, values); % standard implementation of bar fn
else
handles.bar = bar(values); % standard implementation of bar fn
end
hold on
hBar = handles.bar;
vers = version;
if num2str(vers(1))<8 % New code for Matlab 2010
if nRows > 1,
hErrorbar = zeros(1,nCols);
for col = 1:nCols
% Extract the x location data needed for the errorbar plots:
% x = get(get(handles.bar(col),'children'),'xdata');
% Use the mean x values to call the standard errorbar fn; the
% errorbars will now be centred on each bar; these are in ascending
% order so use xOrder to ensure y values and errors are too:
hErrorbar(col) = errorbar(mean(x,1), values(xOrder,col), errors(xOrder,col), errors(xOrder, col), '.k');
set(hErrorbar(col), 'marker', 'none')
end
else
x = get(get(handles.bar,'children'),'xdata');
hErrorbar = errorbar(mean(x,1), values, errors, errors, '.k');
set(hErrorbar, 'marker', 'none')
end
else % New code for Matlab 2016
if nRows > 1,
hErrorbar = zeros(1,nCols);
for col = 1:nCols
% Extract the x location data needed for the errorbar plots:
% x = get(get(handles.bar(col),'children'),'xdata');
% Use the mean x values to call the standard errorbar fn; the
% errorbars will now be centred on each bar; these are in ascending
% order so use xOrder to ensure y values and errors are too:
x = bsxfun(@plus, hBar(col).XData, [hBar(col).XOffset]');
hErrorbar(col) = errorbar(x, values(xOrder,col), errors(xOrder,col), errors(xOrder, col), '.k');
set(hErrorbar(col), 'marker', 'none')
end
else
x = handles.bar.XData;
hErrorbar = errorbar(x, values, errors, errors, '.k');
set(hErrorbar, 'marker', 'none')
end
end
%% Adding significance indication (stars etc)
if ~grouped || size(data,2)==1,
if grouped,
[~,~,stats] = kruskalwallis(data(:,1),groups,'off');
% For each group, check if it's different from zero
u = unique(groups)';
for i=1:length(u);
thesedata = data(groups==u(i),1);
if sum(~isnan(thesedata))>2,
p = signrank(thesedata);
if p2,
p = signrank(thesedata);
if p0)]; %if the upper and lower bound have the same sign
comparison2 = multcompare(stats,'display', 'off', 'alpha', 0.01);
comparison(:,3) = comparison(:,3) + double(comparison2(:,3).*comparison2(:,5)>0); % third column shows the number of stars to be included. 1 for 0.05, 1 more for 0.01, and another one for 0.001 if sum(double(comparison2(:,3).*comparison2(:,5)>0)),
comparison3 = multcompare(stats,'display', 'off', 'alpha', 0.001);
comparison(:,3) = comparison(:,3) + double(comparison3(:,3).*comparison3(:,5)>0);
% Now to plot them:
for i=1:size(comparison,1),
s = sign(median(values(:)));
smallnumber = nanmean(values(:))/10; %for display purposes, so things don't overlap
y1 = s*max(abs([errors(:)+values(:); -errors(:)+values(:)]))+(comparison(i,2)-comparison(i,1))*smallnumber;
y2 = y1+smallnumber/4;
x1 = xOrder(comparison(i,1)) - (xOrder(comparison(i,2))-xOrder(comparison(i,1)))*0.2 + 0.4;
x2 = xOrder(comparison(i,2)) + (xOrder(comparison(i,2))-xOrder(comparison(i,1)))*0.2 - 0.4; %lines between adjecent bars are shorter; the bigger the line, the farther it goes.
if comparison(i,3)==0,
% text(mean([comparison(i,1) comparison(i,2)]), y2, 'n.s.');
elseif comparison(i,3)==1,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([xOrder(comparison(i,1)) xOrder(comparison(i,2))]), y2, '*');
elseif comparison(i,3)==2,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([xOrder(comparison(i,1)) xOrder(comparison(i,2))]), y2, '**');
elseif comparison(i,3)==3,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([xOrder(comparison(i,1)) xOrder(comparison(i,2))]), y2, '***');
end
end
else
x = get(handles.bar,'children');
for i=1:n,
if num2str(vers(1))<8 % Code for Matlab 2010
xs(:,i) = nanmean(get(x{i},'xdata'));
else % New code for Matlab 2016
xs(:,i) = hBar(i).XData + hBar(i).XOffset;
end
end
u = unique(groups)';
for i=1:length(u);
for j=1:size(data,2),
thesedata = data(groups==u(i),j);
if sum(~isnan(thesedata))>2,
p = signrank(thesedata);
if p0)]; %if the upper and lower bound have the same sign
comparison2 = multcompare(stats,'display', 'off', 'alpha', 0.01);
comparison(:,3) = comparison(:,3) + double(comparison2(:,3).*comparison2(:,5)>0); % third column shows the number of stars to be included. 1 for 0.05, 1 more for 0.01, and another one for 0.001 if sum(double(comparison2(:,3).*comparison2(:,5)>0)),
comparison3 = multcompare(stats,'display', 'off', 'alpha', 0.001);
comparison(:,3) = comparison(:,3) + double(comparison3(:,3).*comparison3(:,5)>0);
sigFor2Groups(j,1) = comparison(1,3);
for i=1:size(comparison,1),
s = sign(median(data(:,j)));
smallnumber = nanmean(values(:))/10; %for display purposes, so things don't overlap
y1 = s*max(abs([errors(j,:)'+values(j,:)'; -errors(j,:)'+values(j,:)']))+(comparison(i,2)-comparison(i,1))*smallnumber;
y2 = y1+smallnumber/3;
x1 = xs(j,comparison(i,1));
x2 = xs(j,comparison(i,2));
if comparison(i,3)==1,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([x1 x2]), y2, '*','HorizontalAlignment','center');
elseif comparison(i,3)==2,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([x1 x2]), y2, '**','HorizontalAlignment','center');
elseif comparison(i,3)==3,
plot([x1 x2], [y1 y1], 'k');
plot([x1 x1], [y1 y1-smallnumber/10], 'k');
plot([x2 x2], [y1 y1-smallnumber/10], 'k');
text(mean([x1 x2]), y2, '***','HorizontalAlignment','center');
end
end
end
comparison = sigFor2Groups;
end
hold off
switch nargout
case 1
varargout{1} = hBar;
case 2
varargout{1} = hBar;
varargout{2} = hErrorbar;
case 3
varargout{1} = hBar;
varargout{2} = hErrorbar;
varargout{3} = comparison;
end