Vertically offset stem plot - matlab

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

Related

In MATLAB, how to shade a small non overlapping area when two plots and a threshold line involved, as shown in figure attached

I need to shade the area in Matlab plots, above the threshold, the small area as highlighted, under the blue curve (DC) but outside red one (PV), above the threshold; th=120
load('LP_DCHEMS_PV.mat','DC'); % DCCHEMS loaded
load('FifteenPercentHotDay.mat','PV') % Hot Day Case I, 15%
th=120;
plot(DC);
hold on
plot(PV);
yline(th);
When I use the following commands, it shades the area as shown in fig 2 attached. something has to be corrected in this line : h2=area([y1(:) , (PV(:)-DC(:)).* (DC(:)>th )]);
If anyone can please guide,
y1=zeros(1,144);
y1(1,:)=th;
x=1:144;
h2=area([y1(:) , (PV(:)-DC(:)).* (DC(:)>th )]);
h2(1).FaceColor=[1 1 1]; % white
h2(2).FaceColor=[0 0 1 ]; % blue
hold on;
plot(x,DC,'b',x,PV,'r');
title('overlapped area ');
xlim([1 144]);
Consider the script below:
clear; close all; clc;
% Generate dummy data
x = 1:150;
y1 = exp(-(x(:)-35).^2/750)+1.4*exp(-(x(:)-100).^2/1000)+0.1*rand(150,1);
y2 = -1e-3*(x(:)-75).^2 + 1.85 + 0.1*rand(150,1); y2(y2<0) = 0;
% Set some threshold
threshold = 0.85;
% Create the plot
fig = figure(1); hold on; box on;
plot(x, y1, 'LineWidth', 1.5);
plot(x, y2, 'LineWidth', 1.5);
plot([x(1) x(end)], threshold*ones(1,2), 'k:', 'LineWidth', 1.5);
% Mark the areas and customise them (by changing FaceAlpha, as an example)
areas = MarkAreasInPlot(x, y1, y2, threshold, 'g');
for i = 1:length(areas)
areas{i}.FaceAlpha = 0.5;
end
legend('y_1', 'y_2', 'threshold', 'Location', 'NorthWest');
At the top, I generate some dummy data. The generation of the y1 values is loosely based on what bla did in this answer, while y2 is simply a parabolic function (with some distortion) where all values below zero are filtered out. The next thing we do is define some threshold, which indicates the lowest possible boundary. After this, we simply plot the data and the threshold.
Then, I call the MarkAreasInPlot function:
function areas = MarkAreasInPlot(x, y1, y2, threshold, color)
% This function fills the areas for which the following two statements
% are true:
% (1) y1 > y2
% (2) y1 > threshold
% Reshape arrays (to column vectors) if necessary
x = reshape(x, 1, []);
y1 = reshape(y1, 1, []);
y2 = reshape(y2, 1, []);
% Create filter and find indices
filter = (y1 > y2) & (y1 > threshold);
idx = find(filter==1);
% If nothing was found, return empty array
if isempty(idx)
areas = {};
return;
end
% Determine the number of areas by looping over the indices (idx) and
% checking if the next index follows up the previous one. If this is
% not the case, it means that we are dealing with separate areas
num_areas = 0;
idx_areas = idx(1);
for i = 1:length(idx)-1
if(idx(i+1) ~= idx(i)+1)
num_areas = num_areas + 1;
idx_areas = [idx_areas, idx(i), idx(i+1)];
end
end
idx_areas = [idx_areas, idx(end)];
% Draw all areas
areas = cell(num_areas+1, 1);
for i = 1:2:2*(num_areas+1)
% Determine span of indices for area
idx_span = idx_areas(i):idx_areas(i+1);
% Determine x- and y-coordinates
x_area = x(idx_span);
x_area = [x_area, flip(x_area)];
y_top = y1(idx_span);
y_bot = y2(idx_span);
y_bot(y_bot < threshold) = threshold;
y_area = [y_top, flip(y_bot)];
% Fill the area
areas{i} = fill(x_area, y_area, color, 'EdgeColor', 'None');
end
end
This function works as follows. First, we make sure that all data arrays (x, y1 and y2) are column vectors (this is to prevent possible issues in case they are row vectors). Subsequently, we determine at which indices of the data arrays y1 > y2 and y1 > threshold. Note that filter is a logical array where an element is 1 only when the aforementioned two statements are true. Then, we determine the indices of the elements in filter were we have a value of 1. At these indices of the data arrays, we know that the two statements are true. Of course, we check for the case that the statements are false at all positions, in which case we don't draw any areas and return an empty cell array. After finding the indices, we loop over them and check for a case were the element at the next index is not equal to the value at the current index plus 1. If we find such a case, we now that we are dealing with two separate areas. Any time this happens, we store the two values at these two indices in idx_areas and increase the counter for the number of areas (num_areas) by 1. After doing this, idx_areas contains an even amount of elements. We know that we need to fill the area between y1 and max(y2, threshold) at the indices given by the range idx_areas(n):idx_areas(n+1) for n = 1, 3, 5, 7, ... (the values for n of course depending on the value of num_areas). This is exactly what we do at the end of the function, using the fill function. Note that we use a for loop over the number of areas we should fill and store the handle for each fill call in the areas cell array, which is returned.
If I run the script at the top, I get the following output:
As you can see, it properly detects and fills the areas. However, due to the discrete nature of the data, it can happen that there is a small part at the side of an area that ends up not being completely filled:
This is not an error! Instead, this is due to the fact that the next data point for y1 is located below the threshold, meaning that the second requirement for the MarkAreasInPlot function does not hold! You could try to 'solve' this using an interpolation procedure (to find at what x-value the y1-value is exactly equal to the threshold), but I think it is not worth the effort (as it is only a very small piece that is not filled).
To show you some more results, I now set threshold to 1.6 such that no area will be detected:
And as a final example, I swap y1 and y2 in the call to MarkAreasInPlot:
areas = MarkAreasInPlot(x, y2, y1, threshold, 'g');
As expected, it now fills the area where y2 > y1 and y2 > threshold:

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

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

Change color of 2D plot line depending on 3rd value

I have a data set that looks like this
140400 70.7850 1
140401 70.7923 2
140402 70.7993 3
140403 70.8067 4
140404 70.8139 5
140405 70.8212 3
Where the first column corresponds to time (one second intervals between data points) and will be on the x axis, the second column corresponds with distance and will be on the y axis. The third column is a number (one through five) that is a qualification of the movement.
I want to make a plot that changes the color of the line between two points depending on what the number of the previous data point was. For example, I want the line to be red between the first and second data points because the qualification value was 1.
I've seen a lot of posts about making a sliding scale of colors depending on an intensity value, but I just want 5 colors: (red, orange, yellow, green, and blue) respectively.
I tried doing something like this:
plot(x,y,{'r','o','y','g','b'})
But with no luck.
Any ideas of how to approach this? Without looping if possible.
You can also do it with a trick which works with Matlab version anterior to 2014b (as far back as 2009a at least).
However, is will never be as simple as you expected (unless you write a wrapper for one of the solution here you can forget about plot(x,y,{'r','o','y','g','b'})).
The trick is to use a surface instead of a line object. Surfaces benefit from their CData properties and a lot of useful features to exploit color maps and texture.
Matlab surf does not handle 1D data, it needs a matrix as input so we are going to give it by just duplicating each coordinate set (for example xx=[x,x]).
Don't worry though, the surface will stay as thin as a line, so the end result is not ugly.
%% // your data
M=[140400 70.7850 1
140401 70.7923 2
140402 70.7993 3
140403 70.8067 4
140404 70.8139 5
140405 70.8212 3];
x = M(:,1) ; %// extract "X" column
y = M(:,2) ; %// same for "Y"
c = M(:,3) ; %// extract color index for the custom colormap
%% // define your custom colormap
custom_colormap = [
1 0 0 ; ... %// red
1 .5 0 ; ... %// orange
1 1 0 ; ... %// yellow
0 1 0 ; ... %// green
0 0 1 ; ... %// blue
] ;
%% // Prepare matrix data
xx=[x x]; %// create a 2D matrix based on "X" column
yy=[y y]; %// same for Y
zz=zeros(size(xx)); %// everything in the Z=0 plane
cc =[c c] ; %// matrix for "CData"
%// draw the surface (actually a line)
hs=surf(xx,yy,zz,cc,'EdgeColor','interp','FaceColor','none','Marker','o') ;
colormap(custom_colormap) ; %// assign the colormap
shading flat %// so each line segment has a plain color
view(2) %// view(0,90) %// set view in X-Y plane
colorbar
will get you:
As an example of a more general case:
x=linspace(0,2*pi);
y=sin(x) ;
xx=[x;x];
yy=[y;y];
zz=zeros(size(xx));
hs=surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(2) %// view(0,90)
will give you a sine wave with the color associated to the y value:
Do you have Matlab R2014b or higher?
Then you could use some undocumented features introduced by Yair Altman:
n = 100;
x = linspace(-10,10,n); y = x.^2;
p = plot(x,y,'r', 'LineWidth',5);
%// modified jet-colormap
cd = [uint8(jet(n)*255) uint8(ones(n,1))].' %'
drawnow
set(p.Edge, 'ColorBinding','interpolated', 'ColorData',cd)
My desired effect was achieved below (simplified):
indices(1).index = find( data( 1 : end - 1, 3) == 1);
indices(1).color = [1 0 0];
indices(2).index = find( data( 1 : end - 1, 3) == 2 | ...
data( 1 : end - 1, 3) == 3);
indices(2).color = [1 1 0];
indices(3).index = find( data( 1 : end - 1, 3) == 4 | ...
data( 1 : end - 1, 3) == 5);
indices(3).color = [0 1 0];
indices(4).index = find( data( 1 : end - 1, 3) == 10);
indices(4).color = [0 0 0];
indices(5).index = find( data( 1 : end - 1, 3) == 15);
indices(5).color = [0 0 1];
% Loop through the locations of the values and plot their data points
% together (This will save time vs. plotting each line segment
% individually.)
for iii = 1 : size(indices,2)
% Store locations of the value we are looking to plot
curindex = indices(iii).index;
% Get color that corresponds to that value
color = indices(iii).color;
% Create X and Y that will go into plot, This will make the line
% segment from P1 to P2 have the color that corresponds with P1
x = [data(curindex, 1), data(curindex + 1, 1)]';
y = [data(curindex, 2), data(curindex + 1, 2)]';
% Plot the line segments
hold on
plot(x,y,'Color',color,'LineWidth',lineWidth1)
end
When the result figure of two variables plotted is a circle, will be necessary to add the time in z axes.
For example the figure of induction machine rotor velocity vs electric torque in one laboratory test is: 2d plot figure
In the last figure the direction of the time point plotting could be clockwise or counter clockwise. For the last reason will be added time in z axis.
% Wr vs Te
x = logsout.getElement( 'Wr' ).Values.Data;
y = logsout.getElement( '<Te>' ).Values.Data;
z = logsout.getElement( '<Te>' ).Values.Time;
% % adapt variables for use surf function
xx = zeros( length( x ) ,2 );
yy = zeros( length( y ) ,2 );
zz = zeros( length( z ) ,2 );
xx (:,1) = x; xx (:,2) = x;
yy (:,1) = y; yy (:,2) = y;
zz (:,1) = z; zz (:,2) = z;
% % figure(1) 2D plot
figure (1)
hs = surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(2)
% %
figure(2)
hs = surf(xx,yy,zz,yy,'EdgeColor','interp') %// color binded to "y" values
colormap('hsv')
view(3)
Finally we can view the 3d form and detect that counterwise is the real direction of the time plotting is: 3d plot
Scatter can plot the color according to the value and shows the colormap of the range of values. It's hard to interpolate the color though if you want continuous curves.
Try:
figure
i = 1:20;
t = 1:20;
c = rand(1, 20) * 10;
scatter(i, t, [], c, 's', 'filled')
colormap(jet)
The figure looks like

Hough Transform: Converted polar coordinates back to Cartesian, but still can't plot them

So I have already implemented every part of a Hough Transform on my own, except for actually plotting the lines back onto the original image.
I can set up my array of data that I have like this.
points | theta | rho
-------|-------|----
[246,0] -90 -246
[128,0] -90 -128
[9,0] -90 -9
[0,9] 0 9
[0,128] 0 128
[0,246] 0 246
The points are the points that were converted from the peaks in Polar Coordinates. So now I need to draw all six of these lines and I have had no luck.
Edit
So I tried to change my code based off suggestions. This is what I came up with.
function help(img, outfile, peaks, rho, theta)
imshow(img);
x0 = 1;
xend = size(img,2);
peaks_len=length(peaks);
for i=1:peaks_len
peak=peaks(i,:);
r_ind=peak(1);
t_ind=peak(2);
r=rho(r_ind);
th=theta(t_ind);
%display([r,th,peak]);
%// if a vertical line, then draw a vertical line centered at x = r
% display([r, th]);
if (th == 0)
display('th=0');
display([1, size(img,1)]);
line([r r], [1 size(img,1)], 'Color', 'green');
else
%// Compute starting y coordinate
y0 = abs((-cosd(th)/sind(th))*x0 + (r / sind(th)))+11;%-25;
%// Compute ending y coordinate
yend = abs((-cosd(th)/sind(th))*xend + (r / sind(th)))+11;%-25;
display('y');
display([y0, yend]);
display('x');
display([x0 xend]);
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'green');
end
end
end
I had to change from r==0 to th==0 because th=0 would give NAN errors when r was not 0.
Based off the peaks, I then used that to get the data I needed to then calculate some values... but for some reason this isn't plotting well.
If you notice the + 11 for both y values. I had to do that to get the lines to be where they need to. I have a feeling something else went wrong.
I did change it so that my Rho values are all now positive.
If you recall from the parameterization of the Hough space, the direct relation between rho,theta to x,y is:
rho = x*cos(theta) + y*sin(theta)
Bear in mind that x,y represent the column and row location respectively. In addition, the origin is defined at the top-left corner of the image. Now that you want to plot the equation of the line, you have your rho and theta. Simply re-arrange the equation so that you can solve for an equation of the line of the form y = mx + b:
As such , simply loop over each rho and theta you have and draw a line that starts from the origin at x = 0 up to the limit of your image x = width-1. However, because MATLAB is 1-indexed, we need to go from x = 1 to x = width. Supposing that your rho and theta are stored in separate arrays of the same lengths and you have your edge image stored in im, you can do something like this:
imshow(im); %// Show the image
hold on; %// Hold so we can draw lines
numLines = numel(rho); %// or numel(theta);
%// These are constant and never change
x0 = 1;
xend = size(im,2); %// Get the width of the image
%// For each rho,theta pair...
for idx = 1 : numLines
r = rho(idx); th = theta(idx); %// Get rho and theta
%// Compute starting y coordinate
y0 = (-cosd(th)/sind(th))*x0 + (r / sind(th)); %// Note theta in degrees to respect your convention
%// Compute ending y coordinate
yend = (-cosd(th)/sind(th))*xend + (r / sind(th));
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'blue');
end
The above code is pretty simple. First, show the image using imshow in MATLAB. Next, use hold on so we can draw our lines in the image that will go on top of the image. Next, we calculate how many rho,theta pairs there are, and then we define the two x coordinates to be 1 and width as we will use these to determine where the starting and ending y coordinates are, given these x coordinates. Next, for each rho,theta pair we have, determine the corresponding y coordinates, then use line to draw a line from the starting and ending (x,y) coordinates in blue. We repeat this until we run out of lines.
Don't be alarmed if the y coordinates that are produced go out of bounds in the image. line will be intelligent enough to simply cap the result.
When theta = 0
The above code works assuming that you have no vertical lines detected in the Hough Transform, or when theta = 0. If theta = 0 (like in your case), this means that we have a vertical line which would thus produce an infinite slope and our formulation of y = mx + b is invalid. Should theta = 0, the equation of the line becomes x = rho. As such, you will need an additional if statement inside your loop that will detect this:
imshow(im); %// Show the image
hold on; %// Hold so we can draw lines
numLines = numel(rho); %// or numel(theta);
%// These are constant and never change
x0 = 1;
xend = size(im,2); %// Get the width of the image
%// For each rho,theta pair...
for idx = 1 : numLines
r = rho(idx); th = theta(idx); %// Get rho and theta
%// if a vertical line, then draw a vertical line centered at x = r
if (th == 0)
line([r r], [1 size(im,1)], 'Color', 'blue');
else
%// Compute starting y coordinate
y0 = (-cosd(th)/sind(th))*x0 + (r / sind(th)); %// Note theta in degrees to respect your convention
%// Compute ending y coordinate
yend = (-cosd(th)/sind(th))*xend + (r / sind(th));
%// Draw the line
line([x0 xend], [y0 yend], 'Color', 'blue');
end
end
In order to draw the vertical line, I need to know how high the image is so that we can draw a vertical line from the top of the image (y = 1) down to the bottom of the image (y = height) which is anchored at x = rho. As such, the above code should now properly handle any line, as well as the degenerate case when the slope is infinite. Therefore, this second version of the code is what you're after.
Good luck!