Created
January 14, 2013 14:29
-
-
Save zertrin/4530403 to your computer and use it in GitHub Desktop.
BAR2 Generalized Bar graph (Stacked, Groups, Clustered, Floating, Mixed)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function bar2(varargin) | |
%BAR2 Generalized Bar graph (Stacked, Groups, Clustered, Floating, Mixed) | |
% BAR2(X,Y) draws the columns of the M-by-N-by-Q matrix Y as N main | |
% categories with (M/2) subcategories (as default, see below) and Q | |
% simultaneous data sets with same X coordinates displayed on the | |
% same graph | |
% | |
% For MIXED charts, use input as a cell with each row containing the | |
% information for each type of chart, see example below | |
% | |
% BAR2(Y) uses the default value of X=1:N. | |
% | |
% BAR2(X,Y,'SPACING',SPACING) specifies the spacing parameters [w s] | |
% with w=width of bar and s=spacing between bars within a subcategory. | |
% | |
% BAR2(...,'BASE',VAL) interprets Y to be M subcategories starting | |
% from VAL. VAL must be a single numeric value. | |
% Note: The default is (M/2) subcategories with [low high] | |
% given for each one | |
% | |
% BAR2(...,'XLABEL',XLABEL) labels the X-axis categories | |
% | |
% BAR2(...,'STACKED') plots a stacked chart, with possibility of | |
% subcategories. | |
% | |
% | |
% X must be a vector of length N (if given) | |
% Y must be a matrix of length M-by-N-by-Q, with | |
% Columns : Main Categories | |
% Rows : Alternating [low high] for each subcategory within a | |
% group, or if 'base' is set then the height from zero. | |
% 3rd Dim : (optional) Multiple vertical bars within each subcategory | |
% | |
% Uses NaN to ignore any plot within the subcategories. | |
% | |
% | |
% EXAMPLES: | |
% | |
% SIMPLE BAR CHART | |
% data=rand(1,4); | |
% bar2(data) | |
% | |
% BAR CHART with spaced data and labels: | |
% data=rand(1,4); | |
% bar2([1 2 7 8],data,'BASE','XLABEL',{'A','B','C','D'}) | |
% | |
% FLOATING BAR CHART: | |
% data=rand(2,4); | |
% bar2([1 2 7 8],data,'XLABEL',{'A','B','C','D'}) | |
% | |
% BAR CHART WITH LOG AXIS AND USER SPECIFIED BASE: | |
% data=rand(1,4); | |
% bar2([1 2 7 8],data,'BASE',1e-3,'XLABEL',{'A','B','C','D'}); | |
% set(gca,'Yscale', 'log'), grid on; | |
% | |
% CLUSTERED BAR CHART: | |
% data=rand(3,4); | |
% bar2([1 2 7 8],data,'BASE','XLABEL',{'A','B','C','D'}) | |
% | |
% FLOATING CLUSTERED BAR CHART: | |
% data=rand(6,4); %Six rows representing three vertical [low high] | |
% bar2(data) | |
% legend({'A','B','C'},'Location','BestOutside') | |
% | |
% FLOATING CLUSTERED BAR CHART: (with NaNs) | |
% data=rand(6,4); data([3,4],[2 3])=NaN; | |
% bar2(data) | |
% legend({'A','B','C'},'Location','BestOutside') | |
% | |
% STACKED BAR CHART: | |
% data=rand(1,4,3); %Note: One row for one vertical column per category | |
% bar2(data,'STACKED') | |
% | |
% STACKED CLUSTERED BAR CHART: | |
% data=rand(2,4,3); %Two clusters, both stacked | |
% bar2([1 2 5 6],data,'STACKED','XLABEL',{2001,2002,2005,2006}) | |
% legend({'A','B','C','D','E','F'},'Location','BestOutside') | |
% | |
% Multiple data sets on same X-coordinate: | |
% data=rand(6,4,2); | |
% data(:,:,2)=data(:,:,2)+max(data(:)); %for illustration | |
% bar2(data,'XLABEL',{'A','B','C','D'}) | |
% | |
% MIXED CHARTS | |
% (Bar chart + Clustered Bar Chart + Stacked Bar Chart + Floating) | |
% alldata{1}={[1 2 6 10],rand(1,4),'BASE','XLABEL',{'B1','B2','B3','B4'}}; | |
% alldata{2}={[3 7 8 12],rand(3,4),'BASE','XLABEL',{'CB1','CB2','CB3','CB4'}}; | |
% alldata{3}={[4 5 11 13],rand(1,4,3),'STACKED','XLABEL',{'S1','S2','S3','S4'}}; | |
% alldata{4}={9,rand(2,1),'XLABEL',{'FLOAT'}}; | |
% bar2(alldata) | |
% legend({'Bar','CB_1','CB_2','CB_3','V_1','V_2','V_3','Float'},'Location','BestOutside') | |
% | |
% | |
% Mike Sheppard | |
% Last Modified: 14-Dec-2010 | |
%Parse inputs | |
S = parsevar(varargin); | |
%Using X and Y data points construct Xc and Yc in the right format that is | |
%needed for PATCH | |
for indx=1:size(S.Y,2) | |
X=S.X{indx}; | |
Y=S.Y{indx}; | |
dist=S.dist{indx}; | |
sbcts=S.sbcts{indx}; | |
cts=S.cts{indx}; | |
vplts=S.vplts{indx}; | |
Xc=repmat(sort(repmat(X,1,sbcts)),4,1); | |
Xc=Xc+repmat([dist(1:2:end);dist(2:2:end);dist(2:2:end);dist(1:2:end)],1,cts); | |
Yc=reshape(Y,2,cts*sbcts,vplts); | |
Yc=Yc([1 1 2 2],:,:); | |
S.Xc{indx}=Xc; | |
S.Yc{indx}=Yc; | |
S.numcols{indx}=sbcts*vplts; | |
end | |
totalnumcols=sum(cell2mat(S.numcols)); | |
%Use range of colormap | |
c=colormap; | |
clrindx=round(linspace(1,length(c),totalnumcols)); | |
c=c(clrindx,:); | |
cindx=1; | |
%Instead of calling PATCH once, with all coordinates and colors specified, | |
%a for loop is done so that the legend will be correct; else the overall | |
%patch will be grouped and legend will only consist of one value. | |
hold on | |
for indx=1:size(S.Y,2) | |
vplts=S.vplts{indx}; | |
sbcts=S.sbcts{indx}; | |
Yc=S.Yc{indx}; | |
Xc=S.Xc{indx}; | |
for sbctsindx=1:sbcts %Loop through all subcategories | |
for vpltsindx=1:vplts %With each vertical bar being a different color | |
Yplot=Yc(:,:,vpltsindx); | |
%Same color of vertical bar within subcategories among all categories | |
patch(Xc(:,sbctsindx:sbcts:end),Yplot(:,sbctsindx:sbcts:end),c(cindx,:)); | |
cindx=cindx+1; | |
end | |
end | |
end | |
hold off | |
%Combine X coordinates and possible Xlabels into one | |
X=[]; | |
Xlabel=[]; | |
for indx=1:size(S.Y,2) | |
X=[X S.X{indx}]; | |
if S.Xlabelind{indx} | |
Xlabel=[Xlabel S.Xlabel{indx}]; | |
else | |
Xlabel=[Xlabel num2cell(X)]; | |
end | |
end | |
[X,indx]=unique(X); | |
Xlabel=Xlabel(indx); | |
%Replace tickmarks, add labels and change scale if need be | |
set(gca,'XTick',X) | |
set(gca,'xticklabel',Xlabel) | |
end | |
%SUBFUNCTION, Parsing of inputs | |
function S = parsevar(args1) | |
%Turn cell args into struct S | |
%S.X : X coordinates | |
%S.Y : Y coordinates | |
%S.cts : Categories | |
%S.sbcts : Subcategories | |
%S.vplts : Vertical categories | |
%S.dist : distance vector | |
%S.Xlabelind : X label indicator | |
%S.Xlabel : X label | |
%Inputs may be multiple formats depending on user specifications | |
%Change formatted to be consistent, one cell containing everything in the | |
%correct format used in parsevar | |
if isnumeric(args1{1}) %bars2(var1,[var2,var3,...]) | |
args1={{args1}}; | |
elseif ~iscell(args1{:}{1}) %bar2(cell1) where cell1 is one row | |
args1={args1}; | |
end %Else its bar2(celln) where celln is n rows | |
args1=args1{:}; %Format to one cell, length of which is number of unique charts | |
%Loop through rows of cell args1, | |
%format and parse inputs then save to struct | |
%For almost every case, other then MIXED charts, the length will be one | |
args1rows=length(args1); | |
for args1indx=1:args1rows | |
args=args1{args1indx}; | |
%Initialize | |
Xlabelind=0; | |
logind=0; | |
Xlabel=[]; | |
spacing=[NaN NaN]; | |
%Find X and Y | |
if numel(args)==1 | |
X=1:size(args{1},2); | |
Y=args{1}; | |
%Check to see if input was just Y values and no other input | |
%make into simple bar chart | |
if (length(args)==1)&&(isvector(Y)), args{2}='BASE'; end | |
else | |
if ~isnumeric(args{2}) | |
X=1:size(args{1},2); | |
Y=args{1}; | |
else | |
X=args{1}; | |
Y=args{2}; | |
end | |
end | |
sz=size(Y); | |
for i=1:numel(args) | |
if isequal(args{i},'BASE') | |
%X is to taken as starting from 0 or user specified value; | |
%Change so it is written as [low high] to set low=0 or user | |
%specified value for all | |
if i==numel(args) | |
tempY=zeros([sz(1)*2 sz(2)]); | |
elseif (isnumeric(args{i + 1})) | |
tempY=ones([sz(1)*2 sz(2)]) * args{i + 1}; | |
else | |
tempY=zeros([sz(1)*2 sz(2)]); | |
end | |
tempY(2:2:end)=Y; | |
Y=tempY; | |
end | |
if isequal(args{i},'XLABEL') Xlabelind=1; Xlabel=args{i+1}; end | |
if isequal(args{i},'SPACING') spacing=args{i+1}; end | |
%if isequal(args{i},'LOG') logind=1; end | |
if isequal(args{i},'STACKED') | |
tempY=zeros([sz(1)*2 sz(2) sz(3)]); | |
for j=1:sz(3) | |
tempY(2:2:end,:,j)=Y(:,:,j); | |
end | |
%Make it cumulative (stacked) | |
for j=2:sz(3) | |
tempY(1:2:end,:,j)=tempY(2:2:end,:,j-1); | |
tempY(2:2:end,:,j)=tempY(1:2:end,:,j)+tempY(2:2:end,:,j); | |
end | |
Y=tempY; | |
end | |
end | |
%Find how many categories, subcats , and vertical plots | |
sz=size(Y); | |
cts=sz(2); %Main categories | |
sbcts=sz(1)/2; %Sub categories (within each group), divided by two since [low high] for each | |
if length(sz)==3 | |
vplts=sz(3); %(optional) number of vertical plots (default=1) | |
else | |
vplts=1; | |
end | |
if isnan(spacing) | |
%if param not given use default | |
s=1/(2*((2*sbcts)+(sbcts-1))); | |
w=2*s; | |
else | |
w=spacing(1); | |
s=spacing(2); | |
end | |
dist=[0 cumsum(repmat([w s],1,sbcts))]; | |
dist(end)=[]; | |
dist=dist-(dist(end)/2); | |
%Catch Errors | |
if length(X)~=size(Y,2) | |
error('X must have same number of elements as columns of Y'); | |
end | |
if (Xlabelind)&&(length(X)~=length(Xlabel)) | |
error('XLabel must have same number of elements as X'); | |
end | |
%Save all variables to Struct and clear vars before next inputs | |
S.X{args1indx}=X; clear X; | |
S.Y{args1indx}=Y; clear Y; | |
S.cts{args1indx}=cts; clear cts; | |
S.sbcts{args1indx}=sbcts; clear sbcts; | |
S.vplts{args1indx}=vplts; clear vplts; | |
S.dist{args1indx}=dist; clear dist; | |
S.Xlabelind{args1indx}=Xlabelind; clear Xlabelind; | |
S.Xlabel{args1indx}=Xlabel; clear Xlabel; | |
S.logind{args1indx}=logind; clear logind; | |
end %Loop through args | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment