function varargout = anovabar(data,groups,alpha,correction) % 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 assumes normality and therefore plots means and standard % errors of the mean, and performs parametric significance tests (ttest % for each group to zero and anova between groups). % For a non-parametric equivalent, use barwitherr. % % Copyright (C) 2017 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 if nargin<4, correction = 'tukey-kramer'; 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 = nanmean(data); errors = nansem(data); else grouped = true; if isastring(groups,'groups','grouped','group'), groups = data(:,end); data = data(:,1:end-1); end ok = ~isnan(groups); data = data(ok,:); groups = groups(ok); 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) = nanmean(data(groups==group,1:end),1); errors(:,i) = nansem(data(groups==group,1:end))'; 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, hbar = bar(xOrder, values); % standard implementation of bar fn else hbar = bar(values); % standard implementation of bar fn end hold on vers = version; if num2str(vers(1))<8 % 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(hbar(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(hbar,'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(hbar(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 = hbar.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, % one way anova if grouped, [~,~,stats] = anova1(data(:,1),groups,'off'); u = unique(groups)'; else [~,~,stats] = anova1(data,[],'off'); u = 1:size(data,2); end % For each group, check if it's different from zero for i=1:length(u); if grouped, thesedata = data(groups==u(i),1); else thesedata = data(:,i); end if sum(~isnan(thesedata))>2, [~,p] = ttest(thesedata); if p<0.05, % Put a little star above it to show it's significantly different from zero plot(xOrder(i), sign(values(i))*(max([abs(values(i)+errors(i)) abs(values(i)-errors(i))])+abs(values(i)/7)), '*', 'markersize', 5, 'MarkerEdgeColor', [0 0 0.5]); end end end comparison = multcompare(stats,'display', 'off','ctype',correction); comparison = [comparison(:,1:2) double(comparison(:,3).*comparison(:,5)>0)]; %if the upper and lower bound have the same sign comparison2 = multcompare(stats,'display', 'off', 'alpha', 0.01,'ctype',correction); % ** 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,'ctype',correction); % *** comparison(:,3) = comparison(:,3) + double(comparison3(:,3).*comparison3(:,5)>0); % Now to plot them: for i=1:size(comparison,1), s = sign(Portion(values(:)>0)./(Portion(values(:)>0 | values(:)<0))-0.5); if s==0,s=sign(nanmean(values(:)));end 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.'); else 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, repmat('*',1,comparison(i,3))); end end else % two way anova x = get(hbar,'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,'ctype',correction); 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,'ctype',correction); comparison(:,3) = comparison(:,3) + double(comparison3(:,3).*comparison3(:,5)>0); sigFor2Groups(j,1) = comparison(1,3); for i=1:size(comparison,1), % s = sign(Portion(data(:,j)>0)./(Portion(data(:,j)>0 | data(:,j)<0))-0.5); s = sign(nanmean(data(:,j))); smallnumber = nanmean(abs(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 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