Creating plots with disconnected/discrete lines - matlab

Here is my code:
for p = 1:length(id_unique)
h=figure;
hold on
j = 1;
for i = 1:1:length(id)
if id_unique(p)==id(i)
h=plot([startdate(i),enddate(i)],[j,j],'ok-');
hold on
j = j + 1;
end
end
grid on
hold off
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
end
% id: integer vector containing 50000 values
% id_unique: sorted unique values from vector 'id'
% startdate: datetime vector containing 50000 dates
% enddate: datetime vector containing 50000 dates
Each elements/values in 'id' vector implies an event where the startdate and the enddate of the event is available in the corresponding position in 'startdate' and 'enddate' vectors.
Thus an event id(1) has the start date in startdate(i) and end date in enddate(i).
The program takes a value from the 'id_unique' vector and for each matched value found in 'id', it draws a line in the plot, denoting the beginning and ending time of the event.
For example, suppose 55 is an id-value from vector 'id_unique' and we have this value 1000 times in id. So for 55 a plot is created depicting 1000 separate lines with a marker 'o' at the beginning of the event, a marker 'o' at the end of the event and a line connecting both markers.
Please look at the attached plot that is generated from this code block. If id_unique has 70 values, 70 such plots will be created from this code. In the image, many lines are too small because of small difference between start and end date, so the markers overlap each other and look like a single point.
Now the problem comes when for an id-value in 'id_unique', we have a lot of instances of it in the 'id' vector. When the program plots individual lines unto 100 it works quite fast, but after plotting 300 lines in the same figure the program gets slow. When the program plots 1000 lines in the same figure, it takes about 5-7 seconds for each line. So it takes many hours to generate a plot with many lines.
Is there a way to improve my code to make these plot generation faster.

You don't need a loop:
you can use something like:
a = 1:0.1:4*pi;
b = sin(a); %vector that represent the start of each line
c = sin(a)+1; %vector that represent the end of each line
plot([b;c],[a;a],'-ko','markersize',2)
Result:
The function plot need 2 arguments: x and y, but, and this is why the world is beautiful, the function plot can manage several lines. If x and y are matrices, matlab interpret each column as a new line.

Having thought about this more, I now would suggest doing the following - rather than creating multiple lines in a plot, which generates a lot of objects in the background, it is better to produce one line per plot, that is broken up into multiple segments. If startdate and enddate are vectors of 1xlength(id), then the following code will be much faster as it concatenates all of the lines together, with NaN values in between. This makes the plot break the line at that point. All very quick, without the need to mess about with xlim and ylim as I previously suggested...
Here's my code:
for p = 1:length(id_unique)
h=figure;
block=[startdate(id==id_unique(p));enddate(id==id_unique(p))];
x_vals=reshape([block;nan(1,size(block,2))],1,[]);
y_vals=reshape([(1:size(block,2));(1:size(block,2));nan(1,size(block,2))],1,[]);
plot(x_vals,y_vals,'ok-');
grid on
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
end
I hope this works better for you. (Of course, if your startdate and enddate vectors are length(id) by 1, then you should transpose them using .' in the 3rd line above).
I'm not sure what the purpose of the separate plots is, but it would be possible to plot them all on the same graph if that were useful. The code would then be:
h=figure;
hold on;
for p = 1:length(id_unique)
block=[startdate(id==id_unique(p));enddate(id==id_unique(p))];
x_vals=reshape([block;nan(1,size(block,2))],1,[]);
y_vals=reshape([(1:size(block,2));(1:size(block,2));nan(1,size(block,2))],1,[]);
plot(x_vals,y_vals,'Marker','o');
grid on
end
savefig([plotname,'.fig'])
print(plotname,'-djpeg','-r300')
close
This lets Matlab set the line type and colour using its standard series. It also allows you to use the legend function to add labels. Of course, if the position within id were important, rather than using a sequence for y_vals, you could use the position information obtained from find, changing the 4th and 6th lines to be:
[~,index]=find(id==id_unique(p));
block=[startdate(index);enddate(index)];
y_vals=reshape(index;index;nan(1,size(block,2))],1,[]);
Then you see all of id on one plot, with the different values of id distinguished by colour and linetype. You can then use the legend function to generate a legend:
legend(num2str(id_unique.'));
(This assumes that id_unique is a row vector. If it is a column vector, remove the .').

Here is way to also plot 2 different markers:
% some random data:
N = 50;
id = randi(5,N,1);
startdate = sort(randi(100,N,1));
enddate = startdate+randi(10,N,1);
% plotting:
ax = plot([startdate(:).'; enddate(:).'],[1:N; 1:N],'-k',...
startdate,1:N,'k<',enddate,1:N,'k>')
The (:).' after the vectors is not really needed, it's just to make sure they will be given in the right direction to plot - 2 rows, startdate above enddate.
which gives (randomly):
If you want to divide the data to groups by id, and color them this way, you can do the following:
N = 30;
id = randi(5,N,1);
startdate = datetime(sort(736000+randi(100,N,1)),'ConvertFrom','datenum');
enddate = startdate+randi(20,N,1);
% plotting:
ax = plot([startdate(:).'; enddate(:).'],[1:N; 1:N],'-',...
startdate,1:N,'k<',enddate,1:N,'k>');
% coloring:
cmap = colormap('jet');
col = cmap(floor(linspace(1,64,numel(unique(id)))),:);
for k = 1:N
ax(k).Color = col(id(k),:);
ax(k).LineWidth = 2;
end
% set the legend:
c = 1;
leg_ent = zeros(numel(unique(id)),1);
for k = unique(id).'
leg_ent(c) = find(id==k,1,'first');
c = c+1;
end
legend(ax(leg_ent),num2str(unique(id)),'Location','SouthEast')
and you will get:

Related

Plot a cell into a time-changing curve

I have got a cell, which is like this : Data={[2,3],[5,6],[1,4],[6,7]...}
The number in every square brackets represent x and y of a point respectively. There will be a new coordinate into the cell in every loop of my algorithm.
I want to plot these points into a time-changing curve, which will tell me the trajectory of the point.
As a beginner of MATLAB, I have no idea of this stage. Thanks for your help.
Here is some sample code to get you started. It uses some basic Matlab functionalities that you will hopefully find useful as you continue using it. I added come data points to you cell array for illustrative purposes.
The syntax to access elements into the cell array might seem weird but is important. Look here for details about cell array indexing.
In order to give nice colors to the points, I generated an array based on the jet colormap built-in in Matlab. Basically issuing the command
Colors = jet(N)
create a N x 3 matrix in which every row is a 3-element color ranging from blue to red. That way you can see which points were detected before other (i.e. blue before red). Of course you can change that to anything you want (look here if you're interested).
So here is the code. If something is unclear please ask for clarifications.
clear
clc
%// Get data
Data = {[2,3],[5,6],[1,4],[6,7],[8,1],[5,2],[7,7]};
%// Set up a matrix to color the points. Here I used a jet colormap
%// available from MATLAB but that could be anything.
Colors = jet(numel(Data));
figure;
%// Use "hold all" to prevent the content of the figure to be overwritten
%// at every iterations.
hold all
for k = 1:numel(Data)
%// Note the syntax used to access the content of the cell array.
scatter(Data{k}(1),Data{k}(2),60,Colors(k,:),'filled');
%// Trace a line to link consecutive points
if k > 1
line([Data{k-1}(1) Data{k}(1)],[Data{k-1}(2) Data{k}(2)],'LineStyle','--','Color','k');
end
end
%// Set up axis limits
axis([0 10 0 11])
%// Add labels to axis and add a title.
xlabel('X coordinates','FontSize',16)
ylabel('Y coordinates','FontSize',16)
title('This is a very boring title','FontSize',18)
Which outputs the following:
This would be easier to achieve if all of your data was stored in a n by 2 (or 2 by n) matrix. In this case, each row would be a new entry. For example:
Data=[2,3;
5,6;
1,4;
6,7];
plot(Data(:, 1), Data(:, 2))
Would plot your points. Fortunately, Matlab is able to handle matrices which grow on every iteration, though it is not recommended.
If you really wanted to work with cells, there are a couple of ways you could do it. Firstly, you could assign the elements to a matrix and repeat the above method:
NumPoints = numel(Data);
DataMat = zeros(NumPoints, 2);
for I = 1:NumPoints % Data is a cell here
DataMat(I, :) = cell2mat(Data(I));
end
You could alternatively plot the elements straight from the cell, though this would limit your plot options.
NumPoints = numel(Data);
hold on
for I = 1:NumPoints
point = cell2mat(Data(I));
plot(point(1), point(2))
end
hold off
With regards to your time changing curve, if you find that Matlab starts to slow down after it stores lots of points, it is possible to limit your viewing window in time with clever indexing. For example:
index = 1;
SamplingRate = 10; % How many times per second are we taking a sample (Hertz)?
WindowTime = 10; % How far into the past do we want to store points (seconds)?
NumPoints = SamplingRate * WindowTime
Data = zeros(NumPoints, 2);
while running
% Your code goes here
Data(index, :) = NewData;
index = index + 1;
index = mod(index-1, NumPoints)+1;
plot(Data(:, 1), Data(:, 2))
drawnow
end
Will store your data in a Matrix of fixed size, meaning Matlab won't slow down.

Setting a specific number of tick marks on MATLAB plot

I am trying to figure out how to set a custom number of tick marks on the x-axis of a plot I am creating in MATLAB, but I am not sure if this is possible. I saw this question that seems to be asking the same thing, but that answer wasn't helpful since
set(gca,'XTick',tickVector);
sets the location of the ticks, not the number of ticks.
My code is as follows.
rangeBegin = 100000;
rangeEnd = 200000;
numberOfXTicks = 5;
plot(data(:, 1));
xAxisVals = linspace(rangeBegin, rangeEnd, numberOfXTicks);
%set(gca,'XTick',rangeBegin:rangeEnd); % Doesn't work as expected
set(gca,'XTickLabel',xAxisVals);
So in this example, I am just looking for a way to force MATLAB to create the plot with 5 ticks on the x-axis in order to match the 5 XTickLabels that I have set.
data is an array of doubles that is roughly <3000x1>.
EDIT: I should also add that I want my x-axis values to be from a separate array. The data array shown above corresponds to a time array (not shown...my bad), and each value in the data array has a corresponding value in the time array. Since I am selecting a range from the data array, I want to select the corresponding time values and use those as the x labels. But obviously I do not want 3000 time labels on my x-axis.
Hopefully this is more clear.
numberOfXTicks = 5;
h = plot(data(:, 1));
xData = get(h,'XData');
set(gca,'Xtick',linspace(xData(1),xData(end),numberOfXTicks))

Merge two (or more) time stamped data and plot showing the gap(s)

what is the best way to merge and plot 2 (or more) time stamped data so that the plot includes the gaps between the known data?
For example, I have a cell with time and heart rate values for Monday, and another for Friday. I would like to plot all data from Mon to Friday which includesthe gaps showing nothing was recorded from Tues-Thurs?
So far if I merge the data
data = [mon;fri]
% get the time serial numbers
dateInd = 1;
dateString = data(dateIndex);
dateFormat = 'dd/mm/yyyy HH:MM:SS';
tNum = datenum(dateString{1},dateFormat);
t = linspace(tNum(1),tNum(end),length(tNum));
% get heart rates,
HRIndex = 2;
HR = data(HRIndex);
and plot
plot(t,HR)
datetick('x','ddd');
I obviously get Monday and Fridays data merged into a single plot 2 days long. but I would like to have a plot 5 days long with data only showing on monday and Friday. What is the best way to achieve this?
I hope this makes sense,
Many thanks,
Jon
To achieve such effect I usually fill missing data with NaNs, like here:
x = linspace(0,2*pi,100);
y = sin(x);
y(20:30) = NaN; % there will be a gap from point#20 to point#30
plot(x,y);
The reason is that MatLab does not draw plot points where either x or y data are NaNs.
In your case you may add missing time points to your time data (to have corect gap) and NaNs to corresponding Y-values.
By the way, why don't you plot two separate plots with X-data of the second one properly shifted?
EDIT
Case 1: your x-data is time relative to the start of the day (in 0-24 interval). If you plot them directly they will overlap. You have to add some offset manually, like this:
% generate test data
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25); % 25 points per second day
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
XOffset = 3;
figure;
plot(x1,y1,'*k-', x2+XOffset,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)+XOffset],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Case 2: your x-data have full time information including the date (like MatLab's serial date number, see help on now function, for example). Then just plot them, they will be offset automatically.
% generate test data
XOffset = 3;
x1 = linspace(0,1,25); % 25 points per first day
y1 = rand(25,1);
x2 = linspace(0,1,25)+XOffset; % 25 points per second day, with offset
y2 = rand(25,1);
% plot them as two separate plots
% so line style, color, markers may be set separately
figure;
plot(x1,y1,'*k-', x2,y2,'*r-');
% plot them as single separate plot
% so line style, color, markers are the same
figure;
plot([x1(:); NaN; x2(:)],[y1(:); NaN; y2(:)],'*k-');
% One NaN is enough to insert a gap.
Also instead of
plot(x1,y1,'*k-', x2,y2,'*r-');
you may do like this (number of plots is not limited):
hold on;
plot(x1,y1,'*k-');
plot(x2,y2,'*r-');
hold off;

Plot data with MATLAB biplot with more than 1 color

I have 3 groups of data that had PCA performed on them as one group. I want to highlight each variable group with a different color. Prior to this I overlaid 3 biplots. This gives different colors but creates a distortion in the data as each biplot function skews the data. This caused the groups to all be skewed by different amounts, making the plot not a correct representation.
How do I take a PCA scores matrix (30x3) and split it so the first 10x3 is one color, the next 10x3 is another and the third 10x3 is another, without the data being skewed?
"Skewing" is happening because biplot is renormalizing the scores so the farthest score is distance 1 . axis equal isn't going to fix this. You should use scatter3 instead of biplot
data = rand(30,3);
group = scores(1:10,:)
scatter3(group(:,1), group(:,2), group(:,3), '.b')
hold all
group = scores(11:20,:)
scatter3(group(:,1), group(:,2), group(:,3), '.r')
group = scores(21:30,:)
scatter3(group(:,1), group(:,2), group(:,3), '.g')
hold off
title('Data')
xlabel('X')
ylabel('Y')
zlabel('Z')
Or modify your code's scatter3 lines so that the markers are different colors. The parameter after 'marker' tells what symbol and what symbol and color to plot. E.g. '.r' is a red dot. See Linespec for marker and color parameters.
scatter3(plotdataholder(1:14,1),plotdataholder(1:14,2),plotdataholder(1:14,3),35,[1 0 0],'marker', '.b');
hold on;
scatter3(plotdataholder(15:28,1),plotdataholder(15:28,2),plotdataholder(15:28,3),35,[0 0 1],'marker', '.r') ;
scatter3(plotdataholder(29:42,1),plotdataholder(29:42,2),plotdataholder(29:42,3),35,[0 1 0],'marker', '.g');
This is the method I used to plot biplot data with different colors. The lines of code prior to plot are taken from the biplot.m file. The way biplot manipulates data is kept intact and stops skewing of data when using overlaid biplots.
This coding is not the most efficient, one can see parts that can be cut. I wanted to keep the code intact so one can see how biplot works in it's entirety.
%%%%%%%%%%%%%%%%%%%%%
xxx = coeff(:,1:3);
yyy= score(:,1:3);
**%Taken from biplot.m; This is alter the data the same way biplot alters data - having the %data fit on grid axes no larger than 1.**
[n,d2] = size(yyy);
[p,d] = size(xxx); %7 by 3
[dum,maxind] = max(abs(xxx),[],1);
colsign = sign(xxx(maxind + (0:p:(d-1)*p)));
xxx = xxx .* repmat(colsign, p, 1);
yyy= (yyy ./ max(abs(yyy(:)))) .* repmat(colsign, 42, 1);
nans = NaN(n,1);
ptx = [yyy(:,1) nans]';
pty = [yyy(:,2) nans]';
ptz = [yyy(:,3) nans]';
**%I grouped the pt matrices for my benefit**
plotdataholder(:,1) = ptx(1,:);
plotdataholder(:,2) = pty(1,:);
plotdataholder(:,3) = ptz(1,:);
**%my original score matrix is 42x3 - wanted each 14x3 to be a different color**
scatter3(plotdataholder(1:14,1),plotdataholder(1:14,2),plotdataholder(1:14,3),35,[1 0 0],'marker', '.');
hold on;
scatter3(plotdataholder(15:28,1),plotdataholder(15:28,2),plotdataholder(15:28,3),35,[0 0 1],'marker', '.') ;
scatter3(plotdataholder(29:42,1),plotdataholder(29:42,2),plotdataholder(29:42,3),35,[0 1 0],'marker', '.');
xlabel('Principal Component 1');
ylabel('Principal Component 2');
zlabel('Principal Component 3');
I am not sure if it will help, but try axis equal after you have overlaid the plots.

maybe matrix plot!

for an implicit equation(name it "y") of lambda and beta-bar which is plotted with "ezplot" command, i know it is possible that by a root finding algorithm like "bisection method", i can find solutions of beta-bar for each increment of lambda. but how to build such an algorithm to obtain the lines correctly.
(i think solutions of beta-bar should lie in an n*m matrix)
would you in general show the methods of plotting such problem? thanks.
one of my reasons is discontinuity of "ezplot" command for my equation.
ok here is my pic:
alt text http://www.mojoimage.com/free-image-hosting-view-05.php?id=5039TE-beta-bar-L-n2-.png
or
http://www.mojoimage.com/free-image-hosting-05/5039TE-beta-bar-L-n2-.pngFree Image Hosting
and my code (in short):
h=ezplot('f1',[0.8,1.8,0.7,1.0]);
and in another m.file
function y=f1(lambda,betab)
n1=1.5; n2=1; z0=120*pi;
d1=1; d2=1; a=1;
k0=2*pi/lambda;
u= sqrt(n1^2-betab^2);
wb= sqrt(n2^2-betab^2);
uu=k0*u*d1;
wwb=k0*wb*d2 ;
z1=z0/u; z1_b=z1/z0;
a0_b=tan(wwb)/u+tan(uu)/wb;
b0_b=(1/u^2-1/wb^2)*tan(uu)*tan(wwb);
c0_b=1/(u*wb)*(tan(uu)/u+tan(wwb)/wb);
uu0= k0*u*a; m=0;
y=(a0_b*z1_b^2+c0_b)+(a0_b*z1_b^2-c0_b)*...
cos(2*uu0+m*pi)+b0_b*z1_b*sin(2*uu0+m*pi);
end
fzero cant find roots; it says "Function value must be real and finite".
anyway, is it possible to eliminate discontinuity and only plot real zeros of y?
heretofore,for another function (namely fTE), which is :
function y=fTE(lambda,betab,s)
m=s;
n1=1.5; n2=1;
d1=1; d2=1; a=1;
z0=120*pi;
k0=2*pi/lambda;
u = sqrt(n1^2-betab^2);
w = sqrt(betab^2-n2^2);
U = k0*u*d1;
W = k0*w*d2 ;
z1 = z0/u; z1_b = z1/z0;
a0_b = tanh(W)/u-tan(U)/w;
b0_b = (1/u^2+1/w^2)*tan(U)*tanh(W);
c0_b = -(tan(U)/u+tanh(W)/w)/(u*w);
U0 = k0*u*a;
y = (a0_b*z1_b^2+c0_b)+(a0_b*z1_b^2-c0_b)*cos(2*U0+m*pi)...
+ b0_b*z1_b*sin(2*U0+m*pi);
end
i'd plotted real zeros of "y" by these codes:
s=0; % s=0 for even modes and s=1 for odd modes.
lmin=0.8; lmax=1.8;
bmin=1; bmax=1.5;
lam=linspace(lmin,lmax,1000);
for n=1:length(lam)
increment=0.001; tolerence=1e-14; xstart=bmax-increment;
x=xstart;
dx=increment;
m=0;
while x > bmin
while dx/x >= tolerence
if fTE(lam(n),x,s)*fTE(lam(n),x-dx,s)<0
dx=dx/2;
else
x=x-dx;
end
end
if abs(real(fTE(lam(n),x,s))) < 1e-6 %because of discontinuity some answers are not correct.%
m=m+1;
r(n,m)=x;
end
dx=increment;
x=0.99*x;
end
end
figure
hold on,plot(lam,r(:,1),'k'),plot(lam,r(:,2),'c'),plot(lam,r(:,3),'m'),
xlim([lmin,lmax]);ylim([1,1.5]),
xlabel('\lambda(\mum)'),ylabel('\beta-bar')
you see i use matrix to save data for this plot.
![alt text][2]
because here lines start from left(axis) to rigth. but if the first line(upper) starts someplace from up to rigth(for the first figure and f1 function), then i dont know how to use matrix. lets improve this method.
[2]: http://www.mojoimage.com/free-image-hosting-05/2812untitled.pngFree Image Hosting
Sometimes EZPLOT will display discontinuities because there really are discontinuities or some form of complicated behavior of the function occurring there. You can see this by generating your plot in an alternative way using the CONTOUR function.
You should first modify your f1 function by replacing the arithmetic operators (*, /, and ^) with their element-wise equivalents (.*, ./, and .^) so that f1 can accept matrix inputs for lambda and betab. Then, run the code below:
lambda = linspace(0.8,1.8,500); %# Create a vector of 500 lambda values
betab = linspace(0.7,1,500); %# Create a vector of 500 betab values
[L,B] = meshgrid(lambda,betab); %# Create 2-D grids of values
y = f1(L,B); %# Evaluate f1 at every point in the grid
[c,h] = contour(L,B,y,[0 0]); %# Plot contour lines for the value 0
set(h,'Color','b'); %# Change the lines to blue
xlabel('\lambda'); %# Add an x label
ylabel('$\overline{\beta}$','Interpreter','latex'); %# Add a y label
title('y = 0'); %# Add a title
And you should see the following plot:
Notice that there are now additional lines in the plot that did not appear when using EZPLOT, and these lines are very jagged. You can zoom in on the crossing at the top left and make a plot using SURF to get an idea of what's going on:
lambda = linspace(0.85,0.95,100); %# Some new lambda values
betab = linspace(0.95,1,100); %# Some new betab values
[L,B] = meshgrid(lambda,betab); %# Create 2-D grids of values
y = f1(L,B); %# Evaluate f1 at every point in the grid
surf(L,B,y); %# Make a 3-D surface plot of y
axis([0.85 0.95 0.95 1 -5000 5000]); %# Change the axes limits
xlabel('\lambda'); %# Add an x label
ylabel('$\overline{\beta}$','Interpreter','latex'); %# Add a y label
zlabel('y'); %# Add a z label
Notice that there is a lot of high-frequency periodic activity going on along those additional lines, which is why they look so jagged in the contour plot. This is also why a very general utility like EZPLOT was displaying a break in the lines there, since it really isn't designed to handle specific cases of complicated and poorly behaved functions.
EDIT: (response to comments)
These additional lines may not be true zero crossings, although it is difficult to tell from the SURF plot. There may be a discontinuity at those lines, where the function shoots off to -Inf on one side of the line and Inf on the other side of the line. When rendering the surface or computing the contour, these points on either side of the line may be mistakenly connected, giving the false appearance of a zero crossing along the line.
If you want to find a zero crossing given a value of lambda, you can try using the function FZERO along with an anonymous function to turn your function of two variables f1 into a function of one variable fcn:
lambda_zero = 1.5; %# The value of lambda at the zero crossing
fcn = #(x) f1(lambda_zero,x); %# A function of one variable (lambda is fixed)
betab_zero = fzero(fcn,0.94); %# Find the value of betab at the zero crossing,
%# using 0.94 as an initial guess