How to plot errorbars in a grouped bar? - matlab

I want to plot errorbar in my grouped bars. I wrote following code
x = categorical({'a', 'b', 'c', 'd', 'e'});
y = [6816,7215; 5824,6180; 4860,5200; 3860,4206; 2838,3185];
errlow = [238,337;270,355;303,297;291,340;259,382];
errhigh = [231,264;225,337;153,171;185,286;167,247];
b = bar(x,y);
hold on
xtips1 = b(1).XEndPoints;
ytips1 = b(1).YEndPoints;
er = errorbar(xtips1, ytips1, errlow(:,1), errhigh(:,1));
er.Color = [0 0 0];
er.LineStyle = 'none';
hold on
xtips2 = b(2).XEndPoints;
ytips2 = b(2).YEndPoints;
er2 = errorbar(xtips2, ytips2, errlow(:,2), errhigh(:,2));
er2.Color = [0 0 0];
er2.LineStyle = 'none';
hold off
ylim([2400 7600]);
Errorbars are displaying into the graph. Please have a look, they are straight over a, b, c, d and e but not on the bar as shown in the figure.
I want them on the associated bars ('GREEN' marked error should be on right bar whereas 'RED' marked error should be on left bar) as presented in the figure. How do I do that?
Thanks in advance!

This is the answer provided by the MathWorks Support Team, as posted on MATLAB Answers (with the exception of some minor modifications).
The ability to specify that the errorbar function should display the error bars inside the patches is not available in MATLAB.
There are two work arounds for this limitation, usage of which depends on the release of MATLAB that you are using.
If you are using R2019a or earlier releases, find the center of each bar and pass this data into errorbar with the respective error values.
If you are using R2019b or later releases, retrieve the x coordinate of each bar using the XEndPoints property and pass this data into errorbar.
The following is an example of the above:
% Example data
model_series = [10 40 50 60; 20 50 60 70; 30 60 80 90];
model_error = [1 4 8 6; 2 5 9 12; 3 6 10 13];
b = bar(model_series, 'grouped');
For MATLAB R2019a or earlier releases:
hold on
% Find the number of groups and the number of bars in each group
ngroups = size(model_series, 1);
nbars = size(model_series, 2);
% Calculate the width for each bar group
groupwidth = min(0.8, nbars/(nbars + 1.5));
% Set the position of each error bar in the centre of the main bar
% Based on barweb.m by Bolu Ajiboye from MATLAB File Exchange
for i = 1:nbars
% Calculate center of each bar
x = (1:ngroups) - groupwidth/2 + (2*i-1) * groupwidth / (2*nbars);
errorbar(x, model_series(:,i), model_error(:,i), 'k', 'linestyle', 'none');
end
hold off
For MATLAB 2019b or later releases:
hold on
% Calculate the number of bars in each group
nbars = size(model_series, 2);
% Get the x coordinate of the bars
x = [];
for i = 1:nbars
x = [x ; b(i).XEndPoints];
end
% Plot the errorbars
errorbar(x',model_series,model_error,'k','linestyle','none')'
hold off

Related

Single boxplot for multiple group comparison

Here is the sample code that i used to compare two groups with random mean and standard deviation. However, i want to plot both groups in a single box in the box plot as shown in the attached figure where x-axis is group 1 and y-axis is group 2. I could not find any code doing this. Can some one please help me with this?
clc
clear
x=[rand(1,10) rand(1,10) rand(1,10) rand(1,10) rand(1,10) rand(1,10)];
n=10 ; xx=([1:6])'; % example
r=repmat(xx,1,n)';
g=r(:)';
positions = [1 2 3 4 5 6 ];
h=boxplot(x,g, 'positions', positions);
set(h,'linewidth',2)
set(gca,'xtick',[mean(positions(1:2)) mean(positions(3:4)) mean(positions(5:6)) ])
set(gca,'xticklabel',{'exp1','exp2','exp3'},'Fontsize',28)
color = ['c', 'y', 'c', 'y','c', 'y'];
h = findobj(gca,'Tag','Box');
for j=1:length(h)
patch(get(h(j),'XData'),get(h(j),'YData'),color(j),'FaceAlpha',.5);
end
now i want yellow and blue for exp1 in one box as shown below.. similarly for exp2 and exp3 so on.. so 3 boxes in one boxplot..Ideally this should work for any number of experiments.
For a single two-sided boxplot, we can use the 'Orientation' property, and overlay 2 boxplots one above the other:
x = [1 2 3 4 5 6 7 1 2 3 4 5 6 7];
group = [1,1,1,1,1,1,1,2,2,2,2,2,2,2];
% we need the precntiles of the groups so the boxes will overlap.
% on each boxplot we set the width to the other boxplot hight:
p1 = prctile(x(group==1),[25 75]);
p2 = prctile(x(group==2),[25 75]);
ax = axes;
% first group is vertical:
boxplot(x(group==2),'Positions',mean(x(group==1)),...
'Orientation','vertical','Widths',p1(2)-p1(1),'Colors','r');
lims1 = axis;
hold on
% secound group is horizontal:
boxplot(x(group==1),'Positions',mean(x(group==2)),...
'Orientation','horizontal','Widths',p2(2)-p2(1),'Colors','k');
% the values of the axis are no longer relevant, since they have two
% different meanings, depend on the group. So we hide them.
ax.XAxis.Visible = 'off';
ax.YAxis.Visible = 'off';
hold off
lims2 = axis;
% because each axis represent to different things, we make sure we see
% everything:
axis([max(lims1(1),lims2(1)),...
min(lims1(2),lims2(2)),...
min(lims1(3),lims2(3)),...
max(lims1(4),lims2(4))])
To create multiple two-sided box-plots you need to use an axes for each experiment:
x = rand(10,6);
nsp = floor(size(x,2)/2); % the number of subplots
meanx = mean(x);
% we need the precntiles of the groups so the boxes will overlap.
% on each boxplot we set the width to the other boxplot hight:
width = range(prctile(x,[25; 75]));
main_ax = axes; % create a tmporary axes
% we get the measurements of the ploting area:
pos = main_ax.Position;
% and divide it to our data:
axwidth = pos(3)/nsp; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),nsp+1);
clf % clear the area!
% now we plot each pair of boxplot on a different subplot:
for k = 1:2:size(x,2)
ax = axes('Position',[corner((k+1)/2) pos(2) axwidth pos(4)]);
hold on
% first group is vertical:
boxplot(x(:,k),'Positions',meanx(k+1),...
'Orientation','vertical','Widths',width(k+1),'Colors','r');
% secound group is horizontal:
boxplot(x(:,k+1),'Positions',meanx(k),...
'Orientation','horizontal','Widths',width(k),'Colors','k');
% the values of the y-axis are no longer relevant, since they have two
% different meanings, depend on the group. So we hide them.
ax.YAxis.Visible = 'off';
% we use the x-axis to label the pairs of boxplots:
ax.XAxis.TickLabels = ['Exp ' num2str((k+1)/2)];
% because each axis represent to different things, we make sure we see
% everything:
minx = min(min(x(:,k:k+1)))*0.1;
maxx = max(max(x(:,k:k+1)))*1.1;
axis ([minx maxx minx maxx])
hold off
box off
% set the locations to the exact same place:
bx = findobj(ax,'tag','Box'); % get the boxes
posXdif = bx(2).XData(1)-bx(1).XData(1); % get the horizontal difference
posYdif = bx(2).YData(1)-bx(1).YData(1); % get the vertical difference
bx2Xdata = get(ax.Children(2).Children,{'XData'}); % get all X-data of box 2
bx2Ydata = get(ax.Children(2).Children,{'YData'}); % get all Y-data of box 2
% substruct horizontal difference X-data:
set(ax.Children(2).Children,{'XData'},...
cellfun(#(x) x-posXdif,bx2Xdata,'UniformOutput',false))
% substruct vertical difference Y-data:
set(ax.Children(2).Children,{'YData'},...
cellfun(#(y) y-posYdif,bx2Ydata,'UniformOutput',false))
end

How to plot a line on the top of a grouped bar chart?

I have a grouped bar chart and I want to compare the values .i.e , I mean I want to visualize it using lines. I tried the following code and output is also follows
Y=rand(5,5)
str = {'A'; 'B'; 'C'; 'D'; 'E';};
bar_widh=0.2;
h = bar(Y,bar_widh);
hold on;plot(Y,'b');
set(gca, 'XTickLabel',str, 'XTick',1:numel(str))
grid on
l = cell(1,5);
l{1}='P'; l{2}='Q'; l{3}='R'; l{4}='S'; l{5}='T';
legend(h,l);
I got the following output:
I want to visualize smallest quantity /larger quantity of the bar.In some cases larger value is bad. Can you help me to plot the color of the line same as the bar
I got output as follows
You can try this:
Y=rand(5,5);
str = {'A'; 'B'; 'C'; 'D'; 'E';};
bar_widh=0.2;
figure; hold on;grid on
h = bar(Y,bar_widh);
% to highlight the minimum of each group,
% copy data into a new matrix
Y_ = Y;
% find the minimum values and make the rest zeors
Y_(Y_~=repmat(min(Y_,[],1),size(Y,1),1)) = 0;
% then plot with so sort of highlighting
h2 = bar(Y_,0.5);
pause(0.1) % pause to allow bars to be drawn
% now go through each group of bars and plot the line
for i = 1:numel(h)
x = h(i).XData + h(i).XOffset; % find the x coordinates where the bars are plotted
ax = plot(x,Y(:,i)); % plot the line
% set color of the bars the same as the line
h(i).FaceColor = ax.Color;
h2(i).FaceColor = ax.Color;
end
set(gca, 'XTickLabel',str, 'XTick',1:numel(str))
legend('P','Q','R','S','T');
h(i).XData
is the center coordinates of the ith group of bars.
For example, in your case:
h(1).XData = [ 1 2 3 4 5 ]; % group P
h(2).XData = [ 1 2 3 4 5 ]; % group Q
...
h(5).XData = [ 1 2 3 4 5 ]; % group T
h(i).XOffset
is the offset value of each bar in the group from its corresponding centre coordinate.
For example, in your case:
h(1).XOffset = -0.3077; % group P
h(2).XOffset = -0.1538; % group Q
...
h(5).XOffset = 0.3077; % group T
Without highlighting the minimum values
Minimum values highlighted

Hierarchically grouped boxplot

I would like to draw a boxplot with two sets of data to compare. I am willing to use Hierarchically grouped boxplot. I could just plot one set of my data using this function. I was wondering how I can use this function to plot two sets of data together. I drew the second set of data in red one by hand to show what I am trying to plot!
My problem is that I can't put two sets of data on one graph with hold on.
Well, this is not precisely what you asked for, and does not use the function hierarchicalBoxplot, but it may be a workaround. I demonstrate it using MATLAB example data:
load carsmall
% cleaning the data a little
Origin = categorical(cellstr(Origin));
MPG(Origin=='Italy') = [];
Origin(Origin=='Italy') = [];
% this part is just for readability:
data1 = MPG;
groups1 = Origin;
data2 = MPG*3;
groups2 = Origin;
% And we start:
% =============
% we need a wider figure, with a white background:
figure('Color',[1 1 1],'Position',[178 457 1114 521])
main_ax = axes; % create a tmporary axes
% we get the measurements of the ploting area:
pos = main_ax.Position;
% and divide it to our data:
group_number = 6;
width = pos(3)/group_number; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),group_number+1);
clf % clear the area!
% Now we plot everything in a loop:
for k = 1:group_number
% create a different axes for each group:
ax = axes;
boxplot(ax,data1,groups1); % plot the first set
hold on
boxplot(ax,data2,groups2) % plot the second set
% set the ylim to include all data:
ax.YLim = [min([data1; data2])-5 max([data1; data2])+10];
ax.XTickLabelRotation = 90; % rotate xlables if needed
box off
if k == 1
ylabel('Miles per Gallon (MPG)') % only for the most right axes
else
ax.YTick = [];
end
xlabel(['Group ' num2str(k)])
ax.Position = [corner(k) 0.2 width 0.7];
end
% and finally we place the title:
main_ax = axes('Position',[corner(1) 0.11 width*group_number 0.815]);
title('Miles per Gallon by Vehicle Origin')
axis off
% and this will color the data:
f = gcf;
colors = [1 0 0;0 0 1]; % red and blue
for g = 2:numel(f.Children)
for k = 1:numel(f.Children(g).Children(1).Children)
f.Children(g).Children(1).Children(k).Color = colors(1,:);
f.Children(g).Children(1).Children(k).MarkerEdgeColor = colors(1,:);
f.Children(g).Children(2).Children(k).Color = colors(2,:);
f.Children(g).Children(2).Children(k).MarkerEdgeColor = colors(2,:);
end
end
All this procedure gives:
It will probably need some final tweaks, but it's somthing to start from ;)
Edit
For a side by side view, you can plot all groups together, and just move the x-ticks:
% Making some data:
% MAKE SURE YOU UNDERSTAND HOW THE DATA IS ARRANGED WITHIN THE GRAPH
years = 6; % try to change this number
groups = 5; % try to change this number
data1 = rand(100,years);
data2 = rand(100,years)+0.3;
groups1 = randi(groups,100,1)*2-1; % groups 1 3 5 7 9
groups2 = randi(groups,100,1)*2; % groups 2 4 6 8 10
legendEntries = {'A' 'B'};
colors = [1 0 0;0 0 1]; % red and blue
% And we start:
% =============
% we need a wider figure, with a white background:
figure('Color',[1 1 1],'Position',[178 457 1400 521])
main_ax = axes; % create a temporary axes
% we get the measurements of the plotting area:
pos = main_ax.Position;
% and divide it to our data:
width = pos(3)/years; % the width of each group
% the bottom left corner of each group:
corner = linspace(pos(1),pos(3)+pos(1),years+1);
clf % clear the area!
% Now we plot everything in a loop:
for k = 1:years
% create a different axes for each group:
ax = axes;
boxplot(ax,[data1(:,k); data2(:,k)],[groups1; groups2]);
ax.XTick = 1.5:2:(groups*2-0.5); % to "combine" the groups in pairs
ax.XTickLabel = {'a','b','c','v','f'};
% set the ylim to include all data:
ax.YLim = [min([data1(:); data2(:)]) max([data1(:); data2(:)])];
box off
if k == 1
ylabel('Miles per Gallon (MPG)') % only for the most right axes
else
ax.YTick = [];
end
xlabel(num2str(2000+k)) % the labels for the years
ax.Position = [corner(k) 0.11 width 0.8];
% this will color the data:
for g = 1:2:numel(ax.Children.Children)-1
ax.Children.Children(g).Color = colors(1,:);
ax.Children.Children(g).MarkerEdgeColor = colors(1,:);
ax.Children.Children(g+1).Color = colors(2,:);
ax.Children.Children(g+1).MarkerEdgeColor = colors(2,:);
end
if k == years
% you can try to change here the index to 1:2 and see if you like it:
leg = legend(ax.Children.Children(20:21),legendEntries);
leg.Position(1) = 0.92;
end
end
% and finally we place the title:
main_ax = axes('Position',[corner(1) 0.11 width*years 0.815]);
title('Miles per Gallon by Vehicle Origin')
axis off
And we get the crowded plot:

how to add data labels for bar graph in matlab

For example (code):
x = [3 6 2 9 5 1];
bar(x)
for this I need to add data labels on top of the each bar.
I know that I have to use TEXT keyword, but I'm not getting how to implement it.
Here is a simple solution with text:
x = [3 6 2 9 5 1];
bar(x)
ylim([0 max(x)*1.2])
text(1:numel(x),x+0.5,num2cell(x))
Based off this answer:
data = [3 6 2 9 5 1];
figure; %// Create new figure
hbar = bar(data); %// Create bar plot
%// Get the data for all the bars that were plotted
x = get(hbar,'XData');
y = get(hbar,'YData');
ygap = 0.1; %// Specify vertical gap between the bar and label
ylimits = get(gca,'YLim');
%// The following two lines have minor tweaks from the original answer
set(gca,'YLim',[ylimits(1),ylimits(2)+0.2*max(y)]);
labels = cellstr(num2str(data')) %//'
for i = 1:length(x) %// Loop over each bar
xpos = x(i); %// Set x position for the text label
ypos = y(i) + ygap; %// Set y position, including gap
htext = text(xpos,ypos,labels{i}); %// Add text label
set(htext,'VerticalAlignment','bottom', 'HorizontalAlignment','center')
end
After some attempts I have found the solution. Do the following:
y = Data;
for b = 1 : 10
BarPlot(b) = bar(b, y(b), 'BarWidth', 0.9); % actual plot
set(BarPlot(b), 'FaceColor', 'blue'); %Apply color
barTopper = sprintf('%.1f%s', y(b)*100,'%'); % Place text on top
text(b-0.5, y(b)+0.01, barTopper, 'FontSize', barFontSize); % position the text
hold on;
end
Let me know if it works.

How to colorize individual bar in matlab? [duplicate]

I would like to draw a 3D histogram (with gnuplot or octave) in order to represent my data.
lets say that I have a data file in the following form:
2 3 4
8 4 10
5 6 7
I'd like to draw nine colored bars (the size of the matrix), in the set [1,3]x[1,3], such that the bar's color is proportional to the bar's height. How can I do this?
Below is a function I implemented that acts as a bar3 replacement (partially).
In my version, the bars are rendered by creating a patch graphics object: we build a matrix of vertex coordinates and a list of faces connecting those vertices.
The idea is to first build a single "3d cube" as a template, then replicate it for as many bars as we have. Each bar is shifted and scaled according to its position and height.
The vertices/faces matrices are constructed in a vectorized manner (look ma, no loops!), and the result is a single patch object drawn for all bars, as opposed to multiple patches one per bar (this is more efficient in terms of graphics performance).
The function could have been implemented by specifying coordinates of connected vertices that form polygons, by using the XData, YData, ZData and CData properties instead of the Vertices and Faces properties. In fact this is what bar3 internally does. Such approach usually requires larger data to define the patches (because we cant have shared points across patch faces, although I didn't care much about that in my implementation). Here is a related post where I tried to explain the structure of the data constructed by bar3.
my_bar3.m
function pp = my_bar3(M, width)
% MY_BAR3 3D bar graph.
%
% M - 2D matrix
% width - bar width (1 means no separation between bars)
%
% See also: bar3, hist3
%% construct patch
if nargin < 2, width = 0.8; end
assert(ismatrix(M), 'Matrix expected.')
% size of matrix
[ny,nx] = size(M);
% first we build a "template" column-bar (8 vertices and 6 faces)
% (bar is initially centered at position (1,1) with width=? and height=1)
hw = width / 2; % half width
[X,Y,Z] = ndgrid([1-hw 1+hw], [1-hw 1+hw], [0 1]);
v = [X(:) Y(:) Z(:)];
f = [
1 2 4 3 ; % bottom
5 6 8 7 ; % top
1 2 6 5 ; % front
3 4 8 7 ; % back
1 5 7 3 ; % left
2 6 8 4 % right
];
% replicate vertices of "template" to form nx*ny bars
[offsetX,offsetY] = meshgrid(0:nx-1,0:ny-1);
offset = [offsetX(:) offsetY(:)]; offset(:,3) = 0;
v = bsxfun(#plus, v, permute(offset,[3 2 1]));
v = reshape(permute(v,[2 1 3]), 3,[]).';
% adjust bar heights to be equal to matrix values
v(:,3) = v(:,3) .* kron(M(:), ones(8,1));
% replicate faces of "template" to form nx*ny bars
increments = 0:8:8*(nx*ny-1);
f = bsxfun(#plus, f, permute(increments,[1 3 2]));
f = reshape(permute(f,[2 1 3]), 4,[]).';
%% plot
% prepare plot
if exist('OCTAVE_VERSION','builtin') > 0
% If running Octave, select OpenGL backend, gnuplot wont work
graphics_toolkit('fltk');
hax = gca;
else
hax = newplot();
set(ancestor(hax,'figure'), 'Renderer','opengl')
end
% draw patch specified by faces/vertices
% (we use a solid color for all faces)
p = patch('Faces',f, 'Vertices',v, ...
'FaceColor',[0.75 0.85 0.95], 'EdgeColor','k', 'Parent',hax);
view(hax,3); grid(hax,'on');
set(hax, 'XTick',1:nx, 'YTick',1:ny, 'Box','off', 'YDir','reverse', ...
'PlotBoxAspectRatio',[1 1 (sqrt(5)-1)/2]) % 1/GR (GR: golden ratio)
% return handle to patch object if requested
if nargout > 0
pp = p;
end
end
Here is an example to compare it against the builtin bar3 function in MATLAB:
subplot(121), bar3(magic(7)), axis tight
subplot(122), my_bar3(magic(7)), axis tight
Note that I chose to color all the bars in a single solid color (similar to the output of the hist3 function), while MATLAB emphasizes the columns of the matrix with matching colors.
It is easy to customize the patch though; Here is an example to match bar3 coloring mode by using indexed color mapping (scaled):
M = membrane(1); M = M(1:3:end,1:3:end);
h = my_bar3(M, 1.0);
% 6 faces per bar
fvcd = kron((1:numel(M))', ones(6,1));
set(h, 'FaceVertexCData',fvcd, 'FaceColor','flat', 'CDataMapping','scaled')
colormap hsv; axis tight; view(50,25)
set(h, 'FaceAlpha',0.85) % semi-transparent bars
Or say you wanted to color the bars using gradient according to their heights:
M = 9^2 - spiral(9);
h = my_bar3(M, 0.8);
% use Z-coordinates as vertex colors (indexed color mapping)
v = get(h, 'Vertices');
fvcd = v(:,3);
set(h, 'FaceVertexCData',fvcd, 'FaceColor','interp')
axis tight vis3d; daspect([1 1 10]); view(-40,20)
set(h, 'EdgeColor','k', 'EdgeAlpha',0.1)
Note that in the last example, the "Renderer" property of the figure will affect the appearance of the gradients. In MATLAB, the 'OpenGL' renderer will interpolate colors along the RGB colorspace, whereas the other two renderers ('Painters' and 'ZBuffer') will interpolate across the colors of the current colormap used (so the histogram bars would look like mini colorbars going through the jet palette, as opposed to a gradient from blue at the base to whatever the color is at the defined height as shown above). See this post for more details.
I've tested the function in Octave 3.6.4 and 3.8.1 both running on Windows, and it worked fine. If you run the examples I showed above, you'll find that some of the advanced 3D features are not yet implemented correctly in Octave (this includes transparency, lighting, and such..). Also I've used functions not available in Octave like membrane and spiral to build sample matrices, but those are not essential to the code, just replace them with your own data :)
Solution using only functions available in OCTAVE, tested with octave-online
This solution generates a surface in a similar way to the internals of Matlabs hist3d function.
In brief:
creates a surface with 4 points with the "height" of each
value, which are plotted at each bin edge.
Each is surrounded by zeros, which are also plotted at each bin edge.
The colour is set to be based on the bin values and is applied to
the 4 points and the surrounding zeros. (so that the edges and tops of the 'bars' are coloured to match the "height".)
For data given as a matrix containing bin heights (bin_values in the code):
Code
bin_values=rand(5,4); %some random data
bin_edges_x=[0:size(bin_values,2)];
x=kron(bin_edges_x,ones(1,5));
x=x(4:end-2);
bin_edges_y=[0:size(bin_values,1)];
y=kron(bin_edges_y,ones(1,5));
y=y(4:end-2);
mask_z=[0,0,0,0,0;0,1,1,0,0;0,1,1,0,0;0,0,0,0,0;0,0,0,0,0];
mask_c=ones(5);
z=kron(bin_values,mask_z);
c=kron(bin_values,mask_c);
surf(x,y,z,c)
Output
I don't have access to Octave, butI believe this should do the trick:
Z = [2 3 4
8 4 10
5 6 7];
[H W] = size(Z);
h = zeros( 1, numel(Z) );
ih = 1;
for ix = 1:W
fx = ix-.45;
tx = ix+.45;
for iy = 1:W
fy = iy-.45;
ty = iy+.45;
vert = [ fx fy 0;...
fx ty 0;...
tx fy 0;...
tx ty 0;...
fx fy Z(iy,ix);...
fx ty Z(iy,ix);...
tx fy Z(iy,ix);...
tx ty Z(iy,ix)];
faces = [ 1 3 5;...
5 3 7;...
7 3 4;...
7 8 4;...
5 6 7;...
6 7 8;...
1 2 5;...
5 6 2;...
2 4 8;...
2 6 8];
h(ih) = patch( 'faces', faces, 'vertices', vert, 'FaceVertexCData', Z(iy,ix),...
'FaceColor', 'flat', 'EdgeColor','none' );
ih = ih+1;
end
end
view( 60, 45 );
colorbar;
I think the following should do the trick. I didn't use anything more sophisticated than colormap, surf and patch, which to my knowledge should all work as-is in Octave.
The code:
%# Your data
Z = [2 3 4
8 4 10
5 6 7];
%# the "nominal" bar (adjusted from cylinder())
n = 4;
r = [0.5; 0.5];
m = length(r);
theta = (0:n)/n*2*pi + pi/4;
sintheta = sin(theta); sintheta(end) = sqrt(2)/2;
x0 = r * cos(theta);
y0 = r * sintheta;
z0 = (0:m-1)'/(m-1) * ones(1,n+1);
%# get data for current colormap
map = colormap;
Mz = max(Z(:));
mz = min(Z(:));
% Each "bar" is 1 surf and 1 patch
for ii = 1:size(Z,1)
for jj = 1:size(Z,2)
% Get color (linear interpolation through current colormap)
cI = (Z(ii,jj)-mz)*(size(map,1)-1)/(Mz-mz) + 1;
fC = floor(cI);
cC = ceil(cI);
color = map(fC,:) + (map(cC,:) - map(fC,:)) * (cI-fC);
% Translate and rescale the nominal bar
x = x0+ii;
y = y0+jj;
z = z0*Z(ii,jj);
% Draw the bar
surf(x,y,z, 'Facecolor', color)
patch(x(end,:), y(end,:), z(end,:), color)
end
end
Result:
How I generate the "nominal bar" is based on code from MATLAB's cylinder(). One cool thing about that is you can very easily make much more funky-looking bars:
This was generated by changing
n = 4;
r = [0.5; 0.5];
into
n = 8;
r = [0.5; 0.45; 0.2; 0.1; 0.2; 0.45; 0.5];
Have you looked at this tutorial on bar3?
Adapting it slightly:
Z=[2 3 4
8 4 10
5 6 7]; % input data
figure;
h = bar3(Z); % get handle to graphics
for k=1:numel(h),
z=get(h(k),'ZData'); % old data - need for its NaN pattern
nn = isnan(z);
nz = kron( Z(:,k),ones(6,4) ); % map color to height 6 faces per data point
nz(nn) = NaN; % used saved NaN pattern for transparent faces
set(h(k),'CData', nz); % set the new colors
end
colorbar;
And here's what you get at the end: