hide/delete bars with zero values from a bar3 log ZScale - matlab

When z values are 0 with a log ZScale, the plot is rendered incorrectly. That's coherent, because log10(0) = -inf.
Example:
Y = cool(7);
bar3(Y)
set(gca,'ZScale','log')
But how can I remove this 0 bars from the plot?
A solution given by Mathworks
(http://www.mathworks.nl/support/solutions/en/data/1-2VFT6X/?product=ML&solution=1-2VFT6X)
is the following:
Y = cool(7);
bar3(Y)
set(gca,'ZScale','log')
llim = .1;
h = get(gca,'Children');
for i = 1:length(h)
ZData = get(h(i), 'ZData');
ZData(ZData==0) = llim;
set(h(i), 'ZData', ZData);
end
This solution replaces 0 values by 0.1 (then log10(0.1) = -1) but I want to remove 0 bars, not draw -1 bars.
Also I tried set to NaN all 0 values How to hide zero values in bar3 plot in MATLAB but set ZScale to log does not like it.
Any suggestion?
Thanks in advance
EDIT:
The easiest solution that I see is to apply the logarithmic scale manually:
Y = cool(7);
Y = log10(Y);
Y(Y==-inf)=NaN;
bar3(Y)

If you're fine replacing log z-values with 0.1, you can replace all zero values with 10^0.1, so when logged, they become 0.1.
So,
Y = cool(7);
Y(Y==0) = 10^0.1;
bar3(Y)
set(gca,'ZScale','log')
This will preserve the log-scale axes tick marks and labels.

Related

Visualization of zero values and small values in the same histogram in MATLAB

I want to plot a single histogram where some of the x-values are 0, while the other x-values range between 0.002 and 0.008. The problem is that even by specifying the xlim, the columns overlap but of course they should not.
x1 = zeros (3000,1);
hist(x1);xlim([0 0.008]);
h = findobj(gca,'Type','patch');
set(h,'Facecolor',[1 0 0]);
set(h,'FaceAlpha',0.7);
hold on
n = 3000;
R = [0.002 0.008];
x2 = rand(n,1)*range(R)+min(R);
hist(x2);xlim([0 1])
h1 = findobj(gca,'Type','patch');
set(h1,'facealpha',.3)
Do you really need to use two histograms?
hist([x1; x2])

How to turn y axis of histogram to show percentage ranging from 0 to 1

I want to change the y axis of the histogram to show percentages ranging from 0 to 1. this is what I've tried, but it doesn't seem to be working.
myTolerance=1e-12; % in erg units.
nbins=50;
for j=1:ntM/100:ntM
H = histfit(Wkinet(abs(Wkinet(:,j))>myTolerance, j) * erg2eV, nbins);
%Select from column j all rows in column j whose absolute values are
%greater than the tolerance.
H(1).delete; %%Remove bins, only keep the fit.
set(gca, 'YScale', 'log');
set(gca, 'XScale', 'log'); % Make logarithmic X
yt = get(gca, 'YTick');
set(gca, 'YTick', yt, 'YTickLabel',
yt/numel(Wkinet(abs(Wkinet(:,j))>myTolerance)))
pause;
end
This is what is currently looks like:
This is what I want:
Just to simplify the discussion below, the line
H = histfit(Wkinet(abs(Wkinet(:,j))>myTolerance, j) * erg2eV, nbins);
is equivalent to
data = Wkinet(abs(Wkinet(:,j))>myTolerance, j) * erg2eV;
H = histfit(data, nbins);
This means below we'll assume data is a vector.
histfit computes and plots a histogram though histogram, then fits a function to it through fitdist. Since you don't want to plot the histogram itself, just stick to fitdist:
pd = fitdist(data,'Normal'); % this is the default distribution used in `histfit`, is it correct?
x = linspace(min(data),max(data),200); % 200 points in the graph, you might want to change this?
y = pdf(pd,x);
plot(x,y);
Now it's simple to normalize the plot however we want. For example set the first element to 1:
pd = fitdist(data,'Normal');
x = linspace(min(data),max(data),200);
y = pdf(pd,x);
y = y/y(1); % <<< Normalize
plot(x,y);
You can set limits on your y-axis using
ylim([1e-3 1]) %lower limit is nonzero since it's plotted on log scale
or
set(gca, 'ylim', [1e-3 1])

How do I plot a single vertical line in matlab?

theta = linspace(0,2*pi,200); y = theta;x = 10;plot(x,y,'o');
When i write this i get the points but when i replace plot(x,y,'o') with plot(x,y) I should get a vertical line at x=4 but the graph comes empty..
You can use stem
x = 10;
y = 200;
stem(x,y,'Marker','none');
From this answer: https://it.mathworks.com/matlabcentral/answers/2031-adding-vertical-line-to-plot
fig=figure;
hax=axes;
x=0:0.1:10;
hold on
plot(x,sin(x))
SP=1; %your point goes here
line([SP SP],get(hax,'YLim'),'Color',[1 0 0])

Bar3 plot with seperate x,y,height and width values

Solution posted below function to plot bar 3 with separate x, y values and separate width and height values
bar3(x,y,z,xWidth,yWidth)
We are currently working on a project that allow one to visualize the area under a 3d function, f(x,y). The purpose of this is to demonstrate how the bars cut a 3d surface. Indirectly to visualize the desired integral.
We wish to have the bars match up with the intervals of the surface grid.
Below is a rough demonstration of the idea.
bar3 only has input for the x-values bar3(x,z), where as surf has a input for both the x and y surf(x,y,z)
Unfortunately this is what we are getting. - this is because bar3 cant be in terms of x and y
CODE:
clc;
cla;
d=eval(get(handles.edtOuterUpperB,'string'));
c=eval(get(handles.edtOuterLowerB,'string'));
b=eval(get(handles.edtInnerUpperB,'string'));
a=eval(get(handles.edtInnerLowerB,'string'));
n=eval(get(handles.edtInnerInterval,'string'));
m=eval(get(handles.edtOuterInterval,'string'));
h=(b-a)/n;
k=(d-c)/m;
[x,y] = meshgrid(a:h:b, c:k:d);
f=eval(get(handles.edtFunc,'string'));
surf(x,y,f);
hold on
bar3(f,1);
If you look closely, you will see that the XData and YData are different from the mesh to the 3D bar plot. This is because your mesh uses "real" x and y values while the bar plot uses indexes for the x and y values.
To fix this, you will want to change one or the other. For your case, the easiest one to change is going to be the surface. You can actually just omit the x and y inputs and the indexed x and y values will be used instead by default when generating the surface.
surf(f);
From the documentation for surf:
surf(Z) creates a three-dimensional shaded surface from the z components in matrix Z, using x = 1:n and y = 1:m, where [m,n] = size(Z). The height, Z, is a single-valued function defined over a geometrically rectangular grid. Z specifies the color data, as well as surface height, so color is proportional to surface height.
Update
If you want to keep the non-indexed values on the x and y axes, you will want to convert the bar3 plot instead. Unfortunately, MATLAB provides a way to specify the x axis bot not the y axis. You can take one of two approaches.
Change the XData
You can get the XData property of the resulting bar objects and change them to the data you want.
x = a:h:b;
y = c:k:d;
%// Anonymous function to scale things for us
scaler = #(vals)x(1) + ((vals-1) * (x(end) - x(1)) / (numel(x) - 1));
%// Create the bar plot
bars = bar3(y, f);
%// Change the XData
xdata = get(bars, 'XData');
xdata = cellfun(scaler, xdata, 'uni', 0);
set(bars, {'XData'}, xdata);
set(gca, 'xtick', x)
%// Now plot the surface
surf(x,y,f);
And just to demonstrate what this does:
x = linspace(0.5, 1.5, 5);
y = linspace(2.5, 4.5, 4);
f = rand(4,5);
scaler = #(vals)x(1) + ((vals-1) * (x(end) - x(1)) / (numel(x) - 1));
bars = bar3(y, f);
set(bars, {'XData'}, cellfun(scaler, get(bars, 'XData'), 'uni', 0))
set(gca, 'xtick', x)
axis tight
Change the XTickLabels
Instead of changing the actual data, you could simply change the values that are displayed to be what you want them to be rather than the indexed values.
x = a:h:b;
y = c:k:d;
labels = arrayfun(#(x)sprintf('%.2f', x), x, 'uni', 0);
bar3(y, f);
set(gca, 'xtick', 1:numel(x), 'xticklabels', labels);
hold on
%// Make sure to use the INDEX values for the x variable
surf(1:numel(x), y, f);
We found a user contributed function scatterbar3, which does what we want, in a different way than what bar3 uses:
http://www.mathworks.com/matlabcentral/fileexchange/1420-scatterbar3
There was however a slight hiccup that we had to correct:
hold on
scatterbar3(x,y,f,h);
scatterbar3 does not have separate inputs for the width and height of the bars, thus large gaps occur when the intervals do not equal one another. Demonstrated below.
We thus edited the scatterbar3 function in order to take both the width and height of the bars as inputs:
Edited scatterbar3 function:
function scatterbar3(X,Y,Z,widthx,widthy)
[r,c]=size(Z);
for j=1:r,
for k=1:c,
if ~isnan(Z(j,k))
drawbar(X(j,k),Y(j,k),Z(j,k),widthx/2,widthy/2)
end
end
end
zlim=[min(Z(:)) max(Z(:))];
if zlim(1)>0,zlim(1)=0;end
if zlim(2)<0,zlim(2)=0;end
axis([min(X(:))-widthx max(X(:))+widthx min(Y(:))-widthy max(Y(:))+widthy zlim])
caxis([min(Z(:)) max(Z(:))])
function drawbar(x,y,z,widthx,widthy)
h(1)=patch([-widthx -widthx widthx widthx]+x,[-widthy widthy widthy -widthy]+y,[0 0 0 0],'b');
h(2)=patch(widthx.*[-1 -1 1 1]+x,widthy.*[-1 -1 -1 -1]+y,z.*[0 1 1 0],'b');
h(3)=patch(widthx.*[-1 -1 -1 -1]+x,widthy.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
h(4)=patch([-widthx -widthx widthx widthx]+x,[-widthy widthy widthy -widthy]+y,[z z z z],'b');
h(5)=patch(widthx.*[-1 -1 1 1]+x,widthy.*[1 1 1 1]+y,z.*[0 1 1 0],'b');
h(6)=patch(widthx.*[1 1 1 1]+x,widthy.*[-1 -1 1 1]+y,z.*[0 1 1 0],'b');
set(h,'facecolor','flat','FaceVertexCData',z)
Finally the working solution in action:
hold on
scatterbar3(x,y,f,h,k);

how to add markers at limits of fplot matlab?

I want to add markers at the limits of a function in matlab. I am plotting the function using fplot this is what I tried:
user_func = '2*x-3';
user_limits = '-2,2';
user_limits = regexp(user_limits, '\,', 'split');
user_limit(1) = str2num(user_limits{1});
user_limit(2) = str2num(user_limits{2});
h = fplot(func,limits);
I am trying to add markers at the limits only (size 10 color 'r'). any idea on how to do that?
thank you
Not sure if this is exactly what you are trying to accomplish but I modified your code slightly so I could plot the function (using an anonymous function):
user_func = #(x) 2*x-3;
user_limits = '-2,2';
user_limits = regexp(user_limits, '\,', 'split')
user_limit(1) = str2num(user_limits{1})
user_limit(2) = str2num(user_limits{2})
figure;fplot(user_func,[user_limit(1) user_limit(2)]);
Next, set the ticks at your locations and change the font size to 10 pt:
set(gca,'XTick',[user_limit(1) user_limit(2)],'FontSize',10);
Change the color of your labels to red:
set(gca, 'XColor', [1 0 0]);
set(gca, 'YColor', [1 0 0]);
Just so you can see the ticks, stretch the x-range a bit:
axis([-2.1 2.1 0 1]); axis 'auto y'
EDIT: After some additional input from the OP, the red tick markers can be plotted as shown below.
First let the x-position at the first limit be given by:
x1 = user_limit(1);
The y-value for first marker is then obtained from the anonymous function like this:
y1 = user_func(x1);
y2 = y1;
We have, y2 = y1, since you want the y-value where where your function first crosses the x-axis to be the same. Now make your plot like this (with x2 = user_limit(2)):
hold on;
plot(x1, y1, 'ro', x2, y2,'ro');
hold off;
giving a plot like: