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