Removing connection between each section of data in a plot: MATLAB - matlab

As seen on the figure below, I have a collection of data from different time sets. However, there is a line connecting the end of each data section to the start of the next.
Is there any way of suppressing this connection without altering the data?

Intro
Currently MATLAB is drawing lines between all consecutive points in your arrays, including when there is a big jump in x value.
An equivalent example would be this plot:
% Create some x with points spaced by 0.1, except at 2 jumps of 5
x = [0:0.1:10, 15:0.1:25, 30:0.1:40];
y = sin(x);
plot(x,y)
Notice the straight line joins between our 3 sections (10-15, 25-30).
Method 1: hold on for multiple plots
We can use the find and diff to get the regions where x jumps, and then plot each region individually.
% Include indices for the first (we're going to +1, so start at 0) and last elements
% You can change this 1 to any tolerance you like, it might be of the order 10^4 for you!
idx = [0, find(diff(x) > 1), numel(x)];
% Hold on for multiple plots
hold on;
% Loop over sections and plot
for ii = 2:numel(idx)
% Plots will loop through colours if you don't specify one, specified blue ('b') here
plot(x(idx(ii-1)+1:idx(ii)), y(idx(ii-1)+1:idx(ii)), 'b')
end
hold off; % good practise so you don't accidentally plot more on this fig!
Method 2: using NaN
You could use also use NaN values to break up your data. NaN values aren't plotted by MATLAB, so if we include one in each break, there will be nothing to connect the line to and we'll see a break:
% Notice the NaNs!
x = [0:0.1:10, NaN, 15:0.1:25, NaN, 30:0.1:40];
y = sin(x);
plot(x,y)
Automation: You may want to use the diff function to find these jumps on the fly for some pre-existing x, like so
% Define some x and y as before, without NaNs, with big gaps in x
x = [0:0.1:10, 15:0.1:25, 30:0.1:40];
y = sin(x);
% Create anonymous function to insert NaN at vector index 'n'
insertNaN = #(v, n) [v(1:n), NaN, v(n+1:end)];
% Get indices where gap is bigger than some value (in this case 1)
idx = find(diff(x) > 1);
% Loop *backwards* through indices (so they don't shift up by 1 as we go)
for ii = numel(idx):-1:1
x = insertNaN(x,idx(ii));
y = insertNaN(y,idx(ii));
end
% Plot appears the same as the second plot above
plot(x,y);
Note: If you want to do further processing on x and y, it might not be best to add random NaN values into the array! You can either:
Remove them afterwards (assuming there weren't pre-existing NaNs)
x = x(~isnan(x));
y = y(~isnan(y));
Use temporary variables for plotting
xplot = x;
% then adding NaNs to xplot for plotting ...

Related

Highlight specific section of graph in MATLAB

I wish to highlight/mark some parts of a array via plot in MATLAB. After some research (like here) I tried to hold the first plot, find the indexes for highlighting and then a new plot, only with those points. However, those points are being drawn but all shifted to the beginning of the axis:
I'm currently trying using this code:
load consumer; % the main array to plot (157628x10 double) - data on column 9
load errors; % a array containing the error indexes (1x5590 double)
x = 1:size(consumer,1)'; % returns a (157628x1 double)
idx = (ismember(x,errors)); % returns a (157628x1 logical)
fig = plot(consumer(:,9));
hold on, plot(consumer(idx,9),'r.');
hold off
Another thing I would like to do was highlighting the whole section of the graph, like a "patch" on the same sections. Any ideas?
The trouble is that you are only providing the y-axis data to the plot function. By default, this means all data is plotted on the 1:numel(y) x locations of your plot, where y is your y-axis data.
You have 2 options...
Also provide x-axis data. You've already got the array x anyway!
figure; hold on;
plot(x, consumer(:,9));
plot(x(idx), consumer(idx,9), 'r.');
Aside: I'm slightly confused why you create idx. If errors is as you describe it (indexes of the array) then you should just be able to use consumer(errors,9).
Make all data which you don't want to appear equal to NaN. Because of the way you're loading your error indices in, this is less quick and easy. Basically you'd copy consumer(:,9) into a new variable, and index all undesirable points to set them equal to NaN.
This method has the benefit of breaking up discontinuous sections too.
y = consumer(:,9); % copy your y data before changes
idx = ~ismember(x, errors); % get the indices you *don't* want to re-plot
y(idx) = NaN; % Set equal to NaN so they aren't plotted
figure; hold on;
plot(x, consumer(:,9));
plot(x, y, 'r'); % Plot all points, NaNs wont show

In Matlab, how to draw lines from the curve to specific xaxis position?

I have a spectral data (1000 variables on xaxis, and peak intensities as y) and a list of peaks of interest at various specific x locations (a matrix called Peak) which I obtained from a function I made. Here, I would like to draw a line from the maximum value of each peaks to the xaxis - or, eventually, place a vertical arrow above each peaks but I read it is quite troublesome, so just a vertical line is welcome. However, using the following code, I get "Error using line Value must be a vector of numeric type". Any thoughts?
X = spectra;
[Peak,intensity]=PeakDetection(X);
nrow = length(Peak);
Peak2=Peak; % to put inside the real xaxis value
plot(xaxis,X);
hold on
for i = 1 : nbrow
Peak2(:,i) = round(xaxis(:,i)); % to get the real xaxis value and round it
xline = Peak2(:,i);
line('XData',xline,'YData',X,'Color','red','LineWidth',2);
end
hold off
Simple annotation:
Here is a simple way to annotate the peaks:
plot(x,y,x_peak,y_peak+0.1,'v','MarkerFaceColor','r');
where x and y is your data, and x_peak and y_peak is the coordinates of the peaks you want to annotate. The add of 0.1 is just for a better placing of the annotation and should be calibrated for your data.
For example (with some arbitrary data):
x = 1:1000;
y = sin(0.01*x).*cos(0.05*x);
[y_peak,x_peak] = PeakDetection(y); % this is just a sketch based on your code...
plot(x,y,x_peak,y_peak+0.1,'v','MarkerFaceColor','r');
the result:
Line annotation:
This is just a little bit more complicated because we need 4 values for each line. Again, assuming x_peak and y_peak as before:
plot(x,y);
hold on
ax = gca;
ymin = ax.YLim(1);
plot([x_peak;x_peak],[ymin*ones(1,numel(y_peak));y_peak],'r')
% you could write instead:
% line([x_peak;x_peak],[ymin*ones(1,numel(y_peak));y_peak],'Color','r')
% but I prefer the PLOT function.
hold off
and the result:
Arrow annotation:
If you really want those arrows, then you need to first convert the peak location to the normalized figure units. Here how to do that:
plot(x,y);
ylim([-1.5 1.5]) % only for a better look of the arrows
peaks = [x_peak.' y_peak.'];
ax = gca;
% This prat converts the axis unites to the figure normalized unites
% AX is a handle to the figure
% PEAKS is a n-by-2 matrix, where the first column is the x values and the
% second is the y values
pos = ax.Position;
% NORMPEAKS is a matrix in the same size of PEAKS, but with all the values
% converted to normalized units
normpx = pos(3)*((peaks(:,1)-ax.XLim(1))./range(ax.XLim))+ pos(1);
normpy = pos(4)*((peaks(:,2)-ax.YLim(1))./range(ax.YLim))+ pos(2);
normpeaks = [normpx normpy];
for k = 1:size(normpeaks,1)
annotation('arrow',[normpeaks(k,1) normpeaks(k,1)],...
[normpeaks(k,2)+0.1 normpeaks(k,2)],...
'Color','red','LineWidth',2)
end
and the result:

plot a curve for each parameter in a figure by recalling mat file

I am doing something similar to the following example:
t=linspace(0,1);
z=rand(1);
theta=pi*rand(1);
a_range=[1,2,3];
for nplot=1:3
a=a_range(nplot);
x=z*t.^2+a*sin(theta);
end
fname=['xsin.mat'];
save(fname)
the parameter a has three different values. For each value of a, I want to plot the function in one single figure. So at the end I will have three figures. I can do it using subplot but this will generate three plots in one figure which is not what I want.
In a new script, I load the mat file:
load('xsin.mat')
for nplot=1:3
figure(nplot)
plot(t,x)
end
but I get one figure not three as I should have. I mean for a=1, I should plot the curve in figure 1; for a=2, I should plot the curve in figure 2 and so on. How can I do this? Any help is appreciated.
You are overwriting x in every iteration; you could modify your code by saving each x for the respective nplot in a separate column of a matrix X (with minimal changes to your code):
t=linspace(0,1);
z=rand(1);
theta=pi*rand(1);
a_range=[1,2,3];
X = NaN(length(t), length(a_range)); % Create matrix to hold x values
for nplot=1:3
a = a_range(nplot);
x = z * t.^2 + a * sin(theta);
X(:,nplot) = x; % Store x in column of X
end
fname=['xsin.mat'];
save(fname)
Then, to create the figures:
load('xsin.mat')
for nplot=1:3
x = X(:,nplot); % Retrieve x from column of X
figure(nplot)
plot(t,x)
end

In MATLAB: How can XData and YData be updated with a changing number of lines?

I am looking for a way to add vertical dividing lines separating consecutive days in a dynamically updating plot of animal migration data (location vs. time). Part of the challenge is that the number of these dividers changes as the plot domain expands to display more temporal data: As the number of days in the plot increases from 3 to 5, for example, the number of dividers increases by 2.
A minimal code example, written in MATLAB, is shown below:
xcols = [1; 1];
ycols = [0; 1];
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
xlabel('time')
ylabel('location')
for ii=2:6
xcols(:,end+1) = [ii; ii];
ycols(:,end+1) = [0; 1];
% set(h.divs, 'XData', xcols, 'YData', ycols);
% set(h.divs, {'XData'}, num2cell(xcols',2), {'YData'}, num2cell(ycols',2));
drawnow
pause(1)
end
The problem centers on the two lines that have been commented out. If I comment in the first of these to try to update XData and YData with each new set of dividers (given as the 2 x DividerCount matrices xcols and ycols), then I receive an error that these inputs "must be a vector of numeric type". If I instead comment in the second line as way of using cell arrays to get around this (per this Stack Overflow post and this MATLAB Newsgroup post), then the code returns an error that "cell array handle dimension must match handle vector length" as soon as the number of dividers changes.
Hacky solutions are certainly possible. For example, the dividers can be plotted as a single line of horizontal and vertical segments, where the horizontal segments are placed above and below the y-axis limits of the plot. Or a fixed number of dividers can be used, with some of the dividers plotted outside the x-axis limits of the plot. The question is whether there is a non-hacky approach – one that can plot a potentially changing number of lines of identical style in the same figure with each pass of the loop.
Assume we have at the beginning
xcols = [1:3; 1:3];
ycols = [0 0 0; 1 1 1];
then h.divs = plot(xcols,ycols,':k'); will create 3 line objects.
Using
set(h.divs,{'XData'},num2cell(xcols',2),{'YData'},num2cell(ycols',2));
with size(xcols, 2)>3 will fail, because this assumes there are more than 3 graphics objects to set values for (but numel(h.divs) is still 3).
So do we have to create a new plot for every divider?
No!, because if we insert NaNs into our data, we can introduce gaps into lines.
As a first iteration, we could use this:
xcols = [1 1];
ycols = [0 1];
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
for ii=2:6
xcols(end+(1:3)) = [nan ii ii];
ycols(end+(1:3)) = [nan 0 1];
set(h.divs, 'XData', xcols, 'YData', ycols);
drawnow
pause(1)
end
Starting with one divider which we plot, we then grow the data vectors (one-dimensional!) with a new value pair separated from the old data by a nan.
This of course grows the data vectors in the loop, which is not so great. Instead, if we know how many dividers there will be, we can preallocate with NaNs and only fill in new data in the loop:
n_div = 6;
xcols = nan(3 * n_div, 1);
ycols = nan(3 * n_div, 1);
figure(4)
clf
h.divs = plot(xcols,ycols,':k');
for ii=1:6
xcols((ii - 1)*3 + (1:2)) = [ii ii];
ycols((ii - 1)*3 + (1:2)) = [0 1];
set(h.divs, 'XData', xcols, 'YData', ycols);
drawnow
pause(1)
end
This has also the benefit that we can assign XData and YData separately, as in the new handle syntax:
h.divs.XData = xcols;
h.divs.YData = cols;
(or even better: h.divs.XData((ii - 1)*3 + (1:2)) = [ii ii];), because the lengths don't change.
You can start with divider definition:
divider.x=[];
divider.y=[];
divider.counter=0;
h.div=line('xdata',divider.x,'ydata',divider.y,'linestyle',':','color','k');
Which will draw no line, but handle will be set.
Then, perhaps in some loop, you can call:
divider.counter=divider.counter+1; % increase the counter
divider.x=[divider.x,nan,divider.counter*[1 1]]; % append X coords of new divider line
divider.y=[divider.y,nan,divider.counter*[0 1]]; % append Y coords of new divider line
set(h.div,'xdata',divider.x,'ydata',divider.y) % update dividers
drawnow % update figure immediately
This approach works because NaN value can be passed to line function but will not be plotted and neither will be lines from neighbour points.

matlab plotting a family of functions

Generate a plot showing the graphs of
y=(2*a+1)*exp(-x)-(a+1)*exp(2*x)
in the range x ∈ <-2, 4> for all integer values of a between -3 and 3
I know how to make typical plot for 2 values and set a range on the axes, but how to draw the graph dependent on the parameter a?
To elaborate on Ben Voigt's comment: A more advanced technique would be to replace the for-loop with a call to bsxfun to generate a matrix of evaluations of M(i,j) = f(x(i),a(j)) and call plot with this matrix. Matlab will then use the columns of the matrix and plot each column with individual colors.
%%// Create a function handle of your function
f = #(x,a) (2*a+1)*exp(-x)-(a+1)*exp(2*x);
%%// Plot the data
x = linspace(-2, 4);
as = -3:3;
plot(x, bsxfun(f,x(:),as));
%%// Add a legend
legendTexts = arrayfun(#(a) sprintf('a == %d', a), as, 'uni', 0);
legend(legendTexts, 'Location', 'best');
You could also create the evaluation matrix using ndgrid, which explicitly returns all combinations of the values of x and as. Here you have to pay closer attention on properly vectorizing the code. (We were lucky that the bsxfun approach worked without having to change the original f.)
f = #(x,a) (2*a+1).*exp(-x)-(a+1).*exp(2*x); %// Note the added dots.
[X,As] = ndgrid(x,as);
plot(x, f(X,As))
However for starters, you should get familiar with loops.
You can do it using a simple for loop as follows. You basically loop through each value of a and plot the corresponding y function.
clear
clc
close all
x = -2:4;
%// Define a
a = -3:3;
%// Counter for legend
p = 1;
LegendText = cell(1,numel(a));
figure;
hold on %// Important to keep all the lines on the same plot.
for k = a
CurrColor = rand(1,3);
y= (2*k+1).*exp(-x)-(k+1).*exp(2.*x);
plot(x,y,'Color',CurrColor);
%// Text for legend
LegendText{p} = sprintf('a equals %d',k);
p = p+1;
end
legend(LegendText,'Location','best')
Which gives something like this:
You can customize the graph as you like. Hope that helps get you started!