Show zero as a different character in bar graph. MATLAB - 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

Related

build my own function that does the same work as 'semilogx' MATLAB function

How can I build my own function in Matlab that does the same work as the Matlab built-in function 'semilogx'?
Example: in this example both fig.1 and fig.2 plot x as a log scale but the values on the x-axis of fig.1 are not correct. So the question is "how can I make the values in fig.1 same as the values in fig.2 without using semilogx?"
x = 0:1000;
y = 2*x;
figure(1), plot(log10(x), y)
figure(2), semilogx(x,y)
I guess in my example above: in Fig.1 x limit is between [0,3] and in Fig.2 x limit is between [0,1000]. What I understand is that x limit should be [0:1000] but when we use log scale this would change to [0,3] so the semilogx function only maps the [0,3] limit to [0,1000]
Basically you have to reconstruct the x-axis tick mark locations and the corresponding tick labels on a log-scaled grid:
% Some data
x = 1:1000;
y = cumsum(rand(size(x)));
% For comparison
subplot(311); plot(log10(x), y)
subplot(312); semilogx(x,y)
% Simulated semilogx plot
subplot(313); plot(log10(x), y)
ax = gca; % Get a handle to the axis for tick modifications
% Compute tick mark locations in log10 scale
logxmax = ceil(log10(x(end)));
ticks = log10(1:9);
ticks = ticks' + (0:logxmax-1);
ticks = [ticks(:); logxmax];
% Set tick marks and labels
ax.XTick = ticks;
ax.XLim = [0 logxmax];
% Reset tick labels
ax.XTickLabel(:) = ''; % clear all tick labels
I = 1+9*(0:logxmax); % Tick labels for 10^n locations
S = arrayfun(#(x)'10^{'+string(x)+'}', (0:logxmax), 'UniformOutput', false);
ax.XTickLabel(I) = S;

create bins based on a range of values for histogram figure

I am doing some analysis and need to produce a histogram plot. I know how to create the standard histogram plot but I need something like the image below, where each point is an interval on the x axis. Each bin is based on a value from x-x for example.
You can use the histogram function, and then set the XTick positions and XTickLabels accordingly. See the comments in the code for explanation.
% random normally distrubuted data
x = 1*randn(1000,1);
edges = -5:1:5;
% create vector with labels (for XTickLabel ... to ...)
labels = [edges(1:end-1); edges(2:end)];
labels = labels(:);
% plot the histogram
figure();
ax = axes;
h = histogram(x, 'BinEdges', edges, 'Normalization', 'Probability');
ax.XTick = edges + mean(diff(edges)/2);
ax.XTickLabel = sprintf('%.1f to %.1f\n', labels);
ax.XTickLabelRotation = 90;
% set yticks to percentage
ax.YTickLabel = cellfun(#(a) sprintf('%i%%', (str2double(a)*100)), ax.YTickLabel, 'UniformOutput', false);
% text above bars
bin_props = h.BinCounts/numel(x); % determine probabilities per bin in axis units
bin_centers = ax.XTick(1:end-1); % get the bin centers
txt_heigts = bin_props + 0.01; % put the text slightly above the bar
txt_labels = split(sprintf('%.1f%% ', bin_props*100), ' ');
txt_labels(end) = []; % remove last cell, is empty because of split.
text(ax, bin_centers, txt_heigts, txt_labels, 'HorizontalAlignment', 'center')
% set ylim to fit all text (otherwise text is outside axes)
ylim([0 .4]);
Putting the text at the right location may require some tweaking. Most important is the 'HorizontalAlignment' option, and the distance to the bars. I also used the 'Normalization', 'probability' option from the histogram function, and set the y axis to also show percentages.
I figure you can make the addition below yourself when needed.
When your data can be outside of the defined binedges, you can clip your data, and set the XTickLabels with less than or greater than signs.
% when data can be outside of defined edges
x = 5*randn(1000,1);
xclip = x;
xclip(x >= max(edges)) = max(edges);
xclip(x <= min(edges)) = min(edges);
% plot the histogram
figure();
ax = axes;
h = histogram(xclip, 'BinEdges', edges);
ax.XTick = edges + mean(diff(edges)/2);
ax.XTickLabel = sprintf('%.1f to %.1f\n', labels);
ax.XTickLabelRotation = 90;
% set boundary labels
ax.XTickLabel{1} = sprintf('\\leq %.1f', edges(2));
ax.XTickLabel{end-1} = sprintf('\\geq %.1f', edges(end-1));
You can also set the outer edges to -Inf and Inf, as user2305193 pointed out. Since the outer bins are then much wider (because they actually extend to Inf on the x axis), which you can correct by setting the axis xlim. By the default the XTickLabels will display -Inf to -5.0, which I personally don't like, so I set them to lesser (and equal) than and greater than signs.
step = 1;
edges = -5:step:5; % your defined range
edges_inf = [-Inf edges Inf]; % for histogram
edges_ext = [edges(1)-step edges]; % for the xticks
x = 5*randn(1000,1);
% plot the histogram
figure();
ax = axes;
h = histogram(x, 'BinEdges', edges_inf, 'Normalization', 'probability');
labels = [edges_inf(1:end-1); edges_inf(2:end)];
labels = labels(:);
ax.XTick = edges_ext + step/2;
ax.XTickLabel = sprintf('%.1f to %.1f\n', labels);
ax.XTickLabelRotation = 90;
% show all bins with equal width (Inf bins are in fact wider)
xlim([min(edges)-step max(edges)+step])
% set boundary labels
ax.XTickLabel{1} = sprintf('\\leq %.1f', edges(1));
ax.XTickLabel{end-1} = sprintf('\\geq %.1f', edges(end));

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')

hist3 plot with additional z axis

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..

Vertically offset stem plot

How do I vertically offset a stem plot so that the stems emanate from say y == 0.5 instead of from the x-axis?
I know I could change the x-tick-marks but it would be better to rather just change the plot.
stem(X+0.5) doesn't work as it will just make the stems longer.
Also I have both positive and negative data. And also I have other plots on the same axis which I don't want to offset.
Based on Luis Mendo's answer below, I have written a function for this (however see my answer below as MATLAB actually has a built-in property for this anyway):
function stem_offset(x_data, y_data, offset, offset_mode, varargin)
%STEM_OFFSET stem plot in which the stems begin at a position vertically
%offset from the x-axis.
%
% STEM_OFFSET(Y, offset) is the same as stem(Y) but offsets all the lines
% by the amount in offset
%
% STEM_OFFSET(X, Y, offset) is the same as stem(X,Y) but offsets all the
% lines by the amount in offset
%
% STEM_OFFSET(X, Y, offset, offset_mode) offset_mode is a string
% specifying if the offset should effect only the base of the stems or
% also the ends. 'base' for just the base, 'all' for the baseand the
% ends. 'all' is set by default
%
% STEM_OFFSET(X, Y, offset, offset_mode, ...) lets call all the stem()
% options like colour and linewidth etc as you normally would with
% stem().
if nargin < 3
offset = 1:length(y_data);
y_data = x_data;
end
if nargin < 4
offset_mode = 'all';
end
h = stem(x_data, y_data, varargin{:});
ch = get(h,'Children');
%Offset the lines
y_lines = get(ch(1),'YData'); %// this contains y values of the lines
%Offset the ends
if strcmp(offset_mode, 'all')
set(ch(1),'YData',y_lines+offset)
y_ends = get(ch(2),'YData'); %// this contains y values of the ends
set(ch(2),'YData',y_ends+offset)
else
set(ch(1),'YData',y_lines+offset*(y_lines==0)) %// replace 0 (i.e. only the start of the lines) by offset
end
end
Which I have now uploaded to the file exchange (http://www.mathworks.com/matlabcentral/fileexchange/45643-stem-plot-with-offset)
The following seems to work. Apparently, the stem object's first child contains the vertical lines, so you just have to change all 0 values in their YData property to the desired offset:
delta = .5; %// desired offset
h = stem(1:10); %// plot to be offset. Get a handle
ch = get(h,'Children');
yy = get(ch(1),'YData'); %// this contains y values of the lines
set(ch(1),'YData',yy+delta*(yy==0)) %// replace 0 by delta
An example with both positive and negative data, and with other plots on the same axis:
stem(.5:4.5,ones(1,5),'g') %// not to be offset
hold on
h = stem(1:5,[-2 3 -4 1 -1]); %// to be offset
axis([0 5.5 -5 4])
ch = get(h,'Children');
yy = get(ch(1),'YData'); %// this contains y values of the lines
set(ch(1),'YData',yy+delta*(yy==0)) %// replace 0 by delta
You can draw a white rectangle over the stems right after you call stem.
x = 1:10
stem(x)
fudge=0.05
rectangle('Position', [min(x) * range(x)*fudge, 0.5*fudge, range(x)*(1+2*fudge), 0.5-fudge], 'FaceColor', 'w', 'EdgeColor', 'w')
The fudge is there to avoid painting over the axis, and to make sure the leftmost and rightmost stems are covered.
It appears that there actually is a property in stem for this that I somehow missed!
http://www.mathworks.com/help/matlab/ref/stem.html#btrw_xi-87
e.g. from the docs:
figure
X = linspace(0,2*pi,50)';
Y = (exp(0.3*X).*sin(3*X));
h = stem(X,Y);
set(h,'BaseValue',2);