hist3 plot with additional z axis - matlab

The following code creates a 2D stacked histogram for two 2D distributions:
%%first dataset
x1 = 200 + 300.*rand(1000,1)'; %rand values between 0 and 200
y1 = 100 + 250.*rand(1000,1)'; %rand values between 100 and 500
%%secnd dataset
x2 = 100 + 200.*rand(1000,1)'; %rand values between 0 and 200
y2 = 200 + 400.*rand(1000,1)'; %rand values between 100 and 500
one = linspace(100,400,20);
two = linspace(100,500,20);
EDGES = {one, two}; %edges
[n1,c1] = hist3([x1' y1'],'Edges',EDGES);%first dataset
[n2,c2] = hist3([x2' y2'],'Edges',EDGES);%second dataset
figure('Color','w');
% plot the first data set
bh=bar3(n1);
% Loop through each row and shift bars upwards
for ii=1:length(bh)
zz = get(bh(ii),'Zdata');
kk = 1;
% Bars are defined by 6 faces(?), adding values from data2 will
% shift the bars upwards accordingly, I'm sure this could be made
% better!
for jj = 0:6:(6*length(bh)-6)
zz(jj+1:jj+6,:)=zz(jj+1:jj+6,:)+n2(kk,ii);
kk=kk+1;
end
%erase zero height bars
%# get the ZData matrix of the current group
Z = get(bh(ii), 'ZData');
%# row-indices of Z matrix. Columns correspond to each rectangular bar
rowsInd = reshape(1:size(Z,1), 6,[]);
%# find bars with zero height
barsIdx = all([Z(2:6:end,2:3) Z(3:6:end,2:3)]==0, 2);
%# replace their values with NaN for those bars
Z(rowsInd(:,barsIdx),:) = NaN;
%# update the ZData
set(bh(ii), 'ZData',Z)
end
% Set face colour to blue for data1
set(bh,'FaceColor',[0 0 1]);
% Apply hold so that data2 can be plotted
hold on;
% Plot data2
bh=bar3(n2);
%erase zero height bars
for ii=1:numel(bh)
%# get the ZData matrix of the current group
Z = get(bh(ii), 'ZData');
%# row-indices of Z matrix. Columns correspond to each rectangular bar
rowsInd = reshape(1:size(Z,1), 6,[]);
%# find bars with zero height
barsIdx = all([Z(2:6:end,2:3) Z(3:6:end,2:3)]==0, 2);
%# replace their values with NaN for those bars
Z(rowsInd(:,barsIdx),:) = NaN;
%# update the ZData
set(bh(ii), 'ZData',Z)
end
% Set face color to red
set(bh,'FaceColor',[1 0 0]);
%set ticks
set(gca,'XTick',1:6:numel(one),'XTickLabel',one(1:6:end))
set(gca,'YTick',1:6:numel(one),'YTickLabel',one(1:6:end))
view(20,40)
%labels
xlabel('x')
ylabel('y')
zlabel('z')
%set transparency
set(gcf,'renderer','opengl');
set(get(gca,'child'),'FaceAlpha',0.8);
set(get(gca,'child'),'EdgeAlpha',0.3);
A first issue is the transparency (but I think it is a problem of my matlab version 2014a, so I am not bothered by that). It just makes all blurry.
My question is how to add a mesh plot on the same picture. The code creating the meshes is the following:
%create surface I want to plot
[X,Y] = meshgrid(one,two);
inds1=find(X(:).*Y(:)<.3e5);%condition
inds2=find(X(:).*Y(:)>.3e5);
I=Y./X.^2;%first surface
I(inds1)=NaN;%second surface
figure('Color','w');hold on
mesh(X,Y,I,'FaceColor',[0 0 1],'EdgeColor','none')
I(:,:)=NaN;
I(inds1)=Y(inds1)./X(inds1);%second surface
mesh(X,Y,I,'FaceColor',[1 0 0],'EdgeColor','none')
alpha(.5)
grid on
view(20,40)
%labels
xlabel('x')
ylabel('y')
zlabel('z')
The domain of the histograms and the meshes are the same. So I just need to add an extra z-axis on the first figure.
I tried substituting figure('Color','w');hold on in the second code with AxesH = axes('NextPlot', 'add');, but I was really wrong about that:
That just overlayed the two figures..
I also tried something along the lines of:
%add axis
axesPosition = get(gca,'Position'); %# Get the current axes position
hNewAxes = axes('Position',axesPosition,... %# Place a new axes on top...
'Color','none',... %# ... with no background color
'ZLim',[0 400],... %# ... and a different scale
'ZAxisLocation','right',... %# ... located on the right
'XTick',[],... %# ... with no x tick marks
'YTick',[],... %# ... with no y tick marks
'Box','off');
but it is not feasible because the property ZAxisLocation does not exist.
Does anyone know how to add the z-axis?
Also, if you have other comments on how to ameliorate the code, they're welcome!
acknowledgements
2d stacked histogram:https://stackoverflow.com/a/17477348/3751931
erasing the zero values in the hist plot: https://stackoverflow.com/a/17477348/3751931

I now think that this is not yet possible (http://www.mathworks.com/matlabcentral/answers/95949-is-there-a-function-to-include-two-3-d-plots-with-different-z-axes-on-the-same-plot-area-similar-to).
So I just added a fake axis:
[X,Y] = meshgrid(one,two);
inds1=find(X(:).*Y(:)<.3e5);%condition
inds2=find(X(:).*Y(:)>.3e5);
s=Y./X.^2;%first surface
s(inds1)=NaN;%second surface
%mesh(X,Y,I,'FaceColor',[0 0 1],'EdgeColor','none')
mesh((X-min(min(X)))/max(max(X-min(min(X))))*20,(Y-min(min(Y)))/max(max(Y-min(min(Y))))*20,...
s/max(max(s))*max(max(n1))+max(max(n1)),'FaceColor','g','EdgeColor','none','facealpha',.5)
s(:,:)=NaN;
s(inds1)=Y(inds1)./X(inds1);%second surface
%mesh(X,Y,I,'FaceColor',[1 0 0],'EdgeColor','none')
mesh((X-min(min(X)))/max(max(X-min(min(X))))*20,(Y-min(min(Y)))/max(max(Y-min(min(Y))))*20,...
s/max(max(s))*max(max(n1))+max(max(n1)),'FaceColor','y','EdgeColor','none','facealpha',.5)
alpha(.5)
grid on
%add fake z axis
line([20 20],[1 1],[max(max(n1))-min(min(s)) 20],...
'color','g','linewidth',2)
% text(20*ones(1,5),zeros(1,5),linspace(max(max(n1))-min(min(I)),20,5),...
% num2str(linspace(0,max(max(I)),5)),'color','g')
z=linspace(max(max(n1))-min(min(s)),20,5);
txto=linspace(0,max(max(s)),5);
for ii=1:5
line([20 20.3],[1 1],[z(ii) z(ii)],'color','g')%ticks
text(20,0,z(ii),num2str(txto(ii)),'color','g')%ticklabel
end
text(19.8,1,21,'s','color','g')%label
Over all the code is quite ugly and needs a lot of tuning..

Related

Create stacked 2d matrix along z axis with surf in MATLAB

The following figure is just a representation of a 2d array with surf. I would like to create a similar figure with 10 of these 2d arrays stacked on top of each other with some kind of offset along the z axis.
figure();
surf(X);
colormap(hsv);
shading interp;
campos([-70 -150 80]);
grid on;
set(gcf,'color','w');
Just call surf several times with hold on, applying a gradually increasing offset.
By default (1-input version of surf), the offset will affect the colors shown for each surface. Here's an example with three 2D arrays. Note that peak-to-peak amplitude is different for each one.
x{1} = .2*peaks(30);
x{2} = .4*peaks(30);
x{3} = .8*peaks(30); % cell array containing three 2D arrays
offset = 7; % desired offset
hold on
for k = 1:numel(x)
surf(x{k} + offset*(k-1))
end
campos([-100 -170 90])
grid on
To prevent offset from affecting color, i.e. achieve consistent color for all surfaces, use the 2- or 4-input version of surf to separately specify height and color:
x{1} = .2*peaks(30);
x{2} = .4*peaks(30);
x{3} = .8*peaks(30);
offset = 7;
hold on
for k = 1:numel(x)
surf(x{k} + offset*(k-1), x{k}) % Only this line has been changed
end
campos([-100 -170 90])
grid on
To generate stacked planes (no height variations) with color depending on value: modify the input arguments as follows:
x{1} = .2*peaks(30);
x{2} = .4*peaks(30);
x{3} = .8*peaks(30);
offset = 7;
hold on
for k = 1:numel(x)
surf(repmat(offset*(k-1), size(x{k})), x{k}) % Only this line has been changed
end
campos([-100 -170 90])
grid on

Using roof of a graph as x-axis for another

I've plotted a graph using pcolor which gives me the following graph-
My aim is to use the roof of the graph (by roof) I mean the highest axis (in this case, the line which is defined by y=57) as the base for a further graph.
I was able to use hold on to generate the following-
Code for this (removed some parts that defined the axis labels etc for brevity)-
load sparsemap ;
load d ;
residues = 57 ;
z = zeros(residues,residues); % define the matrix
index = find(sparsemap(:,3) ~= 0) ;
values = length(index);
hold on
%Plots the map you see in the first photo-
for k = 1:values
z(sparsemap(index(k),1),sparsemap(index(k),2)) = sparsemap(index(k),3);
z(sparsemap(index(k),2),sparsemap(index(k),1)) = sparsemap(index(k),3);
end
%Plots the line plot at the bottom of the graph.
A = d(:,1);
B = d(:,2) ;
plot(A, B) ;
pcolor(1:residues,1:residues,z);
works = load('colormap_works');
colormap(works);
colorbar;
As you can see, the line plot is using the same x axis as the first graph.
I am trying to get the line plot to come on top of the figure. I imagine a final figure like so-
Any ideas as to how I can use the top part of the first graph?
You can use 2 subplots. Here is an example:
data = randi(50,20,20); % some data for the pcolor
y = mean(data); % some data for the top plot
subplot(5,1,2:5) % create a subplot on the lower 4/5 part for the figure
pcolor(data) % plot the data
colormap hot;
h = colorbar('east'); % place the colorbar on the right
h.Position(1) = 0.94; % 'push' the colorbar a little more to the right
ax = gca;
pax = ax.Position; % get the position for further thightning of the axes
ax.YTick(end) = []; % delete the highest y-axis tick so it won't interfere
% with the first tick of the top plot
subplot(5,1,1) % create a subplot on the upper 1/5 part for the figure
plot(1:20,y) % plot the top data
ylim([0 max(y)]) % compact the y-axis a little
ax = gca;
ax.XAxis.Visible = 'off'; % delete the x-axis from the top plot
ax.Position(2) = pax(2)+pax(4); % remove the space between the subplots
Which creates this:

How to show a zoomed part of a graph within a MATLAB plot?

I have about four series of data on a Matlab plot, two of them are quite close and can only be differentiated with a zoom. How do I depict the zoomed part within the existing plot for the viewer. I have checked similar posts but the answers seem very unclear.
I look for something like this:
Here is a suggestion how to do this with MATLAB. It may need some fine tuning, but it will give you the result:
function pan = zoomin(ax,areaToMagnify,panPosition)
% AX is a handle to the axes to magnify
% AREATOMAGNIFY is the area to magnify, given by a 4-element vector that defines the
% lower-left and upper-right corners of a rectangle [x1 y1 x2 y2]
% PANPOSTION is the position of the magnifying pan in the figure, defined by
% the normalized units of the figure [x y w h]
%
fig = ax.Parent;
pan = copyobj(ax,fig);
pan.Position = panPosition;
pan.XLim = areaToMagnify([1 3]);
pan.YLim = areaToMagnify([2 4]);
pan.XTick = [];
pan.YTick = [];
rectangle(ax,'Position',...
[areaToMagnify(1:2) areaToMagnify(3:4)-areaToMagnify(1:2)])
xy = ax2annot(ax,areaToMagnify([1 4;3 2]));
annotation(fig,'line',[xy(1,1) panPosition(1)],...
[xy(1,2) panPosition(2)+panPosition(4)],'Color','k')
annotation(fig,'line',[xy(2,1) panPosition(1)+panPosition(3)],...
[xy(2,2) panPosition(2)],'Color','k')
end
function anxy = ax2annot(ax,xy)
% This function converts the axis unites to the figure normalized unites
% AX is a handle to the figure
% XY is a n-by-2 matrix, where the first column is the x values and the
% second is the y values
% ANXY is a matrix in the same size of XY, but with all the values
% converted to normalized units
pos = ax.Position;
% white area * ((value - axis min) / axis length) + gray area
normx = pos(3)*((xy(:,1)-ax.XLim(1))./range(ax.XLim))+ pos(1);
normy = pos(4)*((xy(:,2)-ax.YLim(1))./range(ax.YLim))+ pos(2);
anxy = [normx normy];
end
Note that the units of areaToMagnify are like the axis units, while the units of panPosition are between 0 to 1, like the position property in MATLAB.
Here is an example:
x = -5:0.1:5;
subplot(3,3,[4 5 7 8])
plot(x,cos(x-2),x,sin(x),x,-x-0.5,x,0.1.*x+0.1)
ax = gca;
area = [-0.4 -0.4 0.25 0.25];
inlarge = subplot(3,3,3);
panpos = inlarge.Position;
delete(inlarge);
inlarge = zoomin(ax,area,panpos);
title(inlarge,'Zoom in')

Show zero as a different character in bar graph. MATLAB

I am trying to make a simple bar graph in MATLAB and whenever the value is zero I want to put a character there. Like an asterisk or something to show that the value for that bar is zero. Is there a way to do this?
Is this what you want?
data = [3.1 4.5 0 6.3 2.7 0 6.1]; %// example data
H = -.008; %// horizontal offset relative to axis span. Set as needed
V = .03; %// vertical offset relative to axis span. Set as needed
h = bar(data); %// plot data
xdata = get(h, 'XData'); %// get x data from plot
ydata = get(h, 'YData'); %// get y data from plot
ind = ydata==0; %// logical index of zero-height data
xl = xlim; %// span of x axis
yl = ylim; %// span if y axis
hoffset = xl(1)+xl(2)*H; %// compute horizontal offset
voffset = yl(1)+yl(2)*V; %// compute vertical offset
text(xdata(ind)+hoffset, repmat(voffset,1,sum(ind)), '*', 'fontsize', 12) %// create text

Calculate gradients of a 3d structure in Matlab

I try to use the Matlab function gradient to calculate the gradient of a volume. I use quiver to display the gradients of slices.
I use a cube-like volume, which is symmetric with respect to x, y, and z axis. To my surprise, the result is not the same for all slices. Actually only the result in the xy-plane (Z-slice, last image) is the expected result.
I know that there are issues when calculating the gradient at the border of an image. But for me the result at the border is not important and so I don't care if the result next to the border is correct. For me it would be important that all three images look like the last one.
Can somebody tell me what is wrong with my code? Thanks!
f=zeros(20,20,20);
space = 5;
f(:,:,space) = 1; f(:,:,end-space) = 1;
f(:,space,:) = 1; f(:,end-space,:) = 1;
f(space,:,:) = 1; f(end-space,:,:) = 1;
space = 4;
f(:,:,space) = 1; f(:,:,end-space) = 1;
f(:,space,:) = 1; f(:,end-space,:) = 1;
f(space,:,:) = 1; f(end-space,:,:) = 1;
size_iso = size(f);
x_slice = round(size_iso(1)/2);
y_slice = round(size_iso(2)/2);
z_slice = round(size_iso(3)/2);
% display the gradient of the edge map
[fx,fy,fz] = gradient(f,0.1);
figure;
image(squeeze(f(x_slice,:,:))*50); colormap(gray(64)); hold on;
quiver(squeeze(fy(x_slice,:,:)),squeeze(fz(x_slice,:,:)));
axis equal;
title(['edge map gradient of X-slice ', num2str(x_slice)]);
figure;
image(squeeze(f(:,y_slice,:))*50); colormap(gray(64)); hold on;
quiver(squeeze(fx(:,y_slice,:)),squeeze(fz(:,y_slice,:)));
axis equal;
title(['edge map gradient of Y-slice ', num2str(y_slice)]);
figure;
image(squeeze(f(:,:,z_slice))*50); colormap(gray(64)); hold on;
quiver(squeeze(fx(:,:,z_slice)),squeeze(fy(:,:,z_slice)));
axis equal;
title(['edge map gradient of Z-slice ', num2str(z_slice)]);
Things are bit more complicated with 3D matrices and coordinates.
For example
img = rand(10,30);
imagesc(img);
axis equal;
will display an image 30 pixels wide and 10 pixels high.
In MatLab when you display an image its first dimension (rows) is actually Y-axis on the plot. Second dimension (columns) is X-axis on the plot.
See, for example, http://www.mathworks.com/help/matlab/math/multidimensional-arrays.html
To illustrate mistake in your code consider simplified example:
% we need a 3D matrix with
% 10 points along the X-axis
% 20 points along the Y-axis
% 30 points along the Z-axis
f = rand(20,10,30); % note the order of numbers
size_iso = size(f), % gives [20 10 30]
x_slice = round(size_iso(2)/2) % gives 5
y_slice = round(size_iso(1)/2) % gives 10
z_slice = round(size_iso(3)/2) % gives 15
figure;
image(squeeze(f(:,x_slice,:))*50); colormap(gray(64)); hold on;
axis equal;
title(['X-slice ', num2str(x_slice)]);
% this code produces image 30 pixels wide and 20 pixels high
% Thus 1st dimension (vertical axis) is actually the Y-axis
% Thus 2nd dimension (horizontal axis) is actually the Z-axis
figure;
image(squeeze(f(y_slice,:,:))*50); colormap(gray(64)); hold on;
axis equal;
title(['Y-slice ', num2str(y_slice)]);
% this code produces image 30 pixels wide and 10 pixels high
% Thus 1st dimension (vertical axis) is actually the X-axis
% Thus 2nd dimension (horizontal axis) is actually the Z-axis
figure;
image(squeeze(f(:,:,z_slice))*50); colormap(gray(64)); hold on;
axis equal;
title(['Z-slice ', num2str(z_slice)]);
% this code produces 10 pixels wide and 20 pixels high
% Thus 1st dimension (vertical axis) is actually the Y-axis
% Thus 2nd dimension (horizontal axis) is actually the X-axis
For your code to work properly you should pay attention not only to the order of dimensions in the slice image but also to the way they are shifted by squeeze function.
Therefore you should provide proper combination of coordinates to the subsequent quiver function call.
I modified your code to fill slab perpendicular to given axis with unique value so you should be able to distinguish them easier. Also I'm using different dimensions along each axis for the same purpose.
xvalue=0.33;
yvalue=0.66;
zvalue=1.00;
% we need a 3D matrix with
% 10 points along the X-axis
% 20 points along the Y-axis
% 30 points along the Z-axis
f = zeros(20,10,30); % note the order of numbers
space = 3;
f(:,space,:) = xvalue; f(:,end-space,:) = xvalue;
f(space,:,:) = yvalue; f(end-space,:,:) = yvalue;
f(:,:,space) = zvalue; f(:,:,end-space) = zvalue;
size_iso = size(f);
x_slice = round(size_iso(2)/2); % note dimension number here for x_slice
y_slice = round(size_iso(1)/2); % note dimension number here for y_slice
z_slice = round(size_iso(3)/2);
% display the gradient of the edge map
[fx,fy,fz] = gradient(f,0.1);
figure;
image(squeeze(f(:,x_slice,:))*50); colormap(gray(64)); hold on;
quiver(squeeze(fz(:,x_slice,:)),squeeze(fy(:,x_slice,:)));
axis equal;
title(['edge map gradient of X-slice ', num2str(x_slice)]);
xlabel('Z')
ylabel('Y')
figure;
image(squeeze(f(y_slice,:,:))*50); colormap(gray(64)); hold on;
quiver(squeeze(fz(y_slice,:,:)),squeeze(fx(y_slice,:,:)));
axis equal;
title(['edge map gradient of Y-slice ', num2str(y_slice)]);
xlabel('Z')
ylabel('X')
figure;
image(squeeze(f(:,:,z_slice))*50); colormap(gray(64)); hold on;
quiver(squeeze(fx(:,:,z_slice)),squeeze(fy(:,:,z_slice)));
axis equal;
title(['edge map gradient of Z-slice ', num2str(z_slice)]);
xlabel('X')
ylabel('Y')
Yes, this is tricky and hard to understand at first but you'll get used to it with practice.