Matlab update plot with multiple data lines/curves - matlab

I want to update a plot with multiple data lines/curves as fast as possible. I have seen some method for updating the plot like using:
h = plot(x,y);
set(h,'YDataSource','y')
set(h,'XDataSource','x')
refreshdata(h,'caller');
or
set(h,'XData',x,'YData',y);
For a single curve it works great, however I want to update not only one but multiple data curves. How can I do this?

If you create multiple plot objects with a single plot command, the handle returned by plot is actually an array of plot objects (one for each plot).
plots = plot(rand(2));
size(plots)
1 2
Because of this, you cannot simply assign another [2x2] matrix to the XData.
set(plots, 'XData', rand(2))
You could pass a cell array of new XData to the plots via the following syntax. This is only really convenient if you already have your new values in a cell array.
set(plots, {'XData'}, {rand(1,2); rand(1,2)})
The other options is to update each plot object individually with the new values. As far as doing this quickly, there really isn't much of a performance hit by not setting them all at once, because they will not actually be rendered until MATLAB is idle or you explicitly call drawnow.
X = rand(2);
Y = rand(2);
for k = 1:numel(plots)
set(plots(k), 'XData', X(k,:), 'YData', Y(k,:))
end
% Force the rendering *after* you update all data
drawnow
If you really want to use the XDataSource and YDataSource method that you have shown, you can actually do this, but you would need to specify a unique data source for each plot object.
% Do this when you create the plots
for k = 1:numel(plots)
set(plots(k), 'XDataSource', sprintf('X(%d,:)', k), ...
'YDataSource', sprintf('Y(%d,:)', k))
end
% Now update the plot data
X = rand(2);
Y = rand(2);
refreshdata(plots)

You can use drawnow:
%Creation of the vectors
x = 1:100;
y = rand(1,100);
%1st plot
h = plot(x,y);
pause(2);
%update y
y = rand(1,100);
set(h,'YData',y)
%update the plot.
drawnow

Related

How to extract data from figure in matlab?

I have saved different Matlab plots in an unique .fig. The figure is like this:
Now, I would like to introduce a filter in these plots to reduce the noises, but unfortunately I have lost the code that generates these signals.
Is there a way to extract data of each signal in this figure?
I tried this:
open('ttc_delay1000.fig');
h = gcf; %current figure handle
axesObjs = get(h, 'Children'); %axes handles
dataObjs = get(axesObjs, 'Children'); %handles to low-level graphics objects in axes
objTypes = get(dataObjs, 'Type'); %type of low-level graphics object
xdata = get(dataObjs, 'XData'); %data from low-level grahics objects
ydata = get(dataObjs, 'YData');
But I am confused and I don't know if it's the right way to act.
Thanks!
A one-liner for your problem:
data = get(findobj(open('ttc_delay1000.fig'), 'Type','line'), {'XData','YData'});
The steps are there (from the inner calls to the outer calls):
open the file;
look into it for the line series;
return the data.
data{n,1} will contain the XData of the LineSeries number n, wile the data{n,2} will contain the YData of the said LineSeries.
If you want to smooth the lines directly in the figure, the idea is the same:
%//Prepare moving average filter of size N
N = 5;
f = #(x) filter(ones(1,N)/N, 1, x);
%//Smooth out the Y data of the LineSeries
hf = open('ttc_delay1000.fig');
for hl = transpose(findobj(hf,'Type','line'))
set(hl, 'YData', f(get(hl,'YData')));
end;
saveas(hf, 'ttc_delay1000_smooth.fig');

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!

How to do an animated plot in matlab

I was wondering if anyone knew how to do an animation plot of
x = (dataset of 1000 points)
y = (dataset of 1000 points)
plot(x,y)
big problem is these are datasets that i am trying to plot , or x,y coordinates as opposed to a function which I would know how to plot via an animation.
I tried to do frames in a for loop but it gave me dots and didn't join them in a line graph so I couldn't really watch the path being traced out.
code I used was
for i = 1:length(DATASET1)
pause(0.1)
plot(DATASET1(i),DATASET2(i))
draw on
end
If what you want is for the plot to "grow" point by point: the easiest way is to create an empty plot and then update its XData and YData properties at each iteration:
h = plot(NaN,NaN); %// initiallize plot. Get a handle to graphic object
axis([min(DATASET1) max(DATASET1) min(DATASET2) max(DATASET2)]); %// freeze axes
%// to their final size, to prevent Matlab from rescaling them dynamically
for ii = 1:length(DATASET1)
pause(0.01)
set(h, 'XData', DATASET1(1:ii), 'YData', DATASET2(1:ii));
drawnow %// you can probably remove this line, as pause already calls drawnow
end
Here's an example1 obtained with DATASET1 = 1:100; DATASET2 = sin((1:100)/6);
1 In case someone's interested, the figure is an animated gif which can be created by adding the following code (taken from here) within the loop, after the drawnow line:
frame = getframe(1);
im = frame2im(frame);
[imind,cm] = rgb2ind(im,256);
if ii == 1;
imwrite(imind,cm,filename,'gif','Loopcount',inf);
else
imwrite(imind,cm,filename,'gif','WriteMode','append');
end
Looks like you were close. Not sure draw on is any command though.
See if the code here inspires you to solve your case -
%// Sample x and y values assumed for demo.
x = 1:1000;
y = x.^2;
%// Plot starts here
figure,hold on
%// Set x and y limits of the plot
xlim([min(x(:)) max(x(:))])
ylim([min(y(:)) max(y(:))])
%// Plot point by point
for k = 1:numel(x)
plot(x(k),y(k),'-') %// Choose your own marker here
%// MATLAB pauses for 0.001 sec before moving on to execue the next
%%// instruction and thus creating animation effect
pause(0.001);
end
Since R2014b, you can work with annimatedline object (doc and how-to) that is meant to handle animated graphs pretty well. Basically, the annimatedline object has a addpoints function that adds new points to the line without having to redefine the existing points, along with a clearpoints function that clears lines for more complex animations.
Here is an example:
h = animatedline;
axis([0,4*pi,-1,1])
x = linspace(0,4*pi,1000);
y = sin(x);
for k = 1:length(x)
addpoints(h,x(k),y(k));
drawnow
end

Trying to make MATLAB's figure stop 'blinking'

So I have a simple loop in MATLAB that does the following:
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
figure(1)
plot(randn(1,100));
figure(2);
plot(randn(1,100));
end
The x and y are made up, but that is the jist of it. Anyway, when I run this code, not surprisingly, MATLAB will make two figures and plot accordingly. The problem is, I get a sort of 'blinking' between figures when I do this, and it makes the quality of seeing x and y evolve over time poorer.
I discovered a way to make one of the plots smoother like this:
figure(1);
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(randn(1,100));
drawnow
end
If I do this, then of course figure(1) will plot very smoothly showing x nicely, without figure(1) 'blinking' between plots, but now I cant show figure(2) or y!
How can I plot both those quantities on different figures (not subplots) smoothly without 'blinking'?
EDIT:
Thanks Geodesic for your answer, the solution works, however there is a subtlety that I did not think would be an issue, however it is.
1) I am unable to use 'imagesc' with this solution.
For example,
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*rand(10,100);
plot(aone,x);
drawnow;
imagesc(atwo,y);
drawnow;
end
In this case the part with imagesc(atwo, y) crashes.
Your flicker is because you're generating each figure window again and again through the loop, which is forcing the window to come to the foreground each time. Generate the figures first, attach some axes to them, and plot your data to each axis like so:
figure(1);
aone = axes;
figure(2);
atwo = axes;
for p = 1:100
x = 4.*randn(1,100);
y = 7.*randn(1,100);
plot(aone,randn(1,100));
drawnow;
imagesc(y,'Parent',atwo);
drawnow;
end
Edit: functions like plot take an axis argument directly, but imagesc does not. In this particular case you'll need to send a Property Name/Value pair in as an argument. The 'Parent' of the image generated will be our axis atwo (see above).
For p = 1, create the plots you need, using the plot command or the imagesc command. Keep the handle of the resulting graphics object by getting an output argument: for example h = plot(.... or h = imagesc(..... This will be a Handle Graphics lineseries or image object, or something else, depending on the particular plot type you create.
For p = 2:100, don't use the plotting commands directly, but instead update the relevant Data properties of the original Handle Graphics object h. For example, for a lineseries object resulting from a plot command, set its XData and YData properties to the new data. For an image object resulting from an imagesc command, set its CData property to the new image.
If necessary, call drawnow after updating to force a flush of the graphics queue.

MATLAB - best way to dynamically update a line handles' XData and YData?

I am collecting data and plotting that data in real time. The data are produced by a motion capture system. I have one class DynamicDataset that is just a wrapper around a 2-column matrix (although it's more nuanced than that) with an event notifier for new data added; another class DynamicPlotter that listens for the data-added event and updates the plot dynamically. Appropriate code snippets:
classdef DynamicDataset < handle
properties
newestData = [];
data = []
end
events
DataAdded
end
methods
function append(obj, val)
obj.data(end+1,:) = val;
obj.newestData = val;
notify(obj, 'DataAdded');
end
end
end
classdef DynamicPlotter < dynamicprops
properties
FH %# figure handle
AH %# axes handle
LH %# array of line handles - may have multiple lines on the plot
dynProps = {} %# cell array of dynamic property names -
%# use to access individual datasets
end
methods
function obj = DynamicPlotter(props) %# props is a cell array of dynamic
%# properties to store information
for i = 1:length(props)
addprop(obj, props{i});
obj.(props{i}) = DynamicDataset;
obj.dynProps = [obj.dynProps props{i}];
addlistener(obj.(props{i}), 'DataAdded', #obj.updatePlot(i));
end
obj.createBlankPlot();
end
function createBlankPlot(obj)
obj.FH = figure;
obj.AH = axes;
hold all;
for i = 1:length(obj.dynProps)
obj.LH(i) = plot(nan); %# only used to produce a line handle
set(obj.LH(i), 'XData', [], 'YData', []);
end
end
function updatePlot(obj, propNum)
X = get(obj.LH(propNum), 'XData');
Y = get(obj.LH(propNum), 'YData');
X(end+1) = obj.(dynProps{propNum}).newestData(1);
Y(end+1) = obj.(dynProps{propNum}).newestData(2);
set(obj.LH(propNum), 'XData', X, 'YData', Y);
end
end
end
Based on the MATLAB Code Profile, the set command in updatePlot() is rather expensive. I am wondering if there is a better way to plot individual points as they come? Ideally I would push the single point into XData and YData and draw that point only, but I don't know if this is possible.
Please note that there may be multiple lineseries objects (i.e., multiple graphs on the same plot); plot() takes an axes handle as an argument, so it wouldn't consider the properties of the previously drawn line handles (or is there a way to make it do so?); I thought of just doing plot(x,y);hold all; but that would give me separate line handles every time, each corresponding to a single point.
It might be that there's no way to make plotting incoming points any faster, but I figured I'd ask.
EDIT: Updated OP with actual code I'm working with, rather than using a generic example that's up for misinterpretation.
The amount of data you're handling in each update, is large (although only a single point is actually changing), making your code O(N^2).
By using a second lineseries to build up a large group of data, you can alternate between adding every point to a short "active" line, and infrequently adding large blocks to the main lineseries. While this doesn't exactly avoid O(N^2), it lets you reduce the constant significantly.
If you do this, remember to overlap the "old" lineseries and "active" lineseries by one point, so that they connect.
Essentially:
function updatePlot(obj, propNum)
X = get(obj.LHactive(propNum), 'XData');
Y = get(obj.LHactive(propNum), 'YData');
X(end+1) = obj.(dynProps{propNum}).newestData(1);
Y(end+1) = obj.(dynProps{propNum}).newestData(2);
if numel(X) > 100
Xold = [get(obj.LH(propNum), 'XData'); X(2:end)];
Yold = [get(obj.LH(propNum), 'YData'); Y(2:end)];
set(obj.LH(propNum), 'XData', Xold, 'YData', Yold);
X = X(end);
Y = Y(end);
end
set(obj.LHactive(propNum), 'XData', X, 'YData', Y);
end
Part of the reason why your code may be taking a long time to run is because you are using a for loop to assign your variables. Depending on what version of Matlab you are using, this will slow your process down significantly. I suggest using vectorization to assign values to your x and y like this:
x = 1:1000;
y = cosd(x);
You can then assign the first points in your data.
xi = x(1);
yi = y(1);
When you plot, assign the XDataSource and YDataSource.
h = plot(xi, yi, 'YDataSource', 'yi', 'XDataSource', 'xi');
Now when you loop through to change the values, use the refreshdata to update the Xdata and Ydata values. Use the drawnow function to update the figure window.
for k = 2:1000,
xi = x(1:k);
yi = y(1:k);
refreshdata(h, 'caller')
drawnow;
end
Your code is slow, because you are replotting all values everytime that you call updatePlot. I would therefore only plot the latest point in updatePlot (This is also the problem that you've stated: Ideally I would push the single point into XData and YData and draw that point only, but I don't know if this is possible.)
add property LH_point_counter
classdef DynamicPlotter < dynamicprops
properties
FH %# figure handle
AH %# axes handle
LH %# cell array of line handles - may have multiple lines on the plot
% counter that counts home many points we have for each dynProps
LH_point_counter = [];
dynProps = {} %# cell array of dynamic property names -
%# use to access individual datasets
end
modify updatePlot
function updatePlot(obj, propNum)
% plot new point
new_x = obj.(dynProps{propNum}).newestData(1);
new_y = obj.(dynProps{propNum}).newestData(2);
new_handle = plot(new_x, new_y);
% add new handle to list of handles of this property
counter_this_prop = obj.LH_point_counter(propNum);
counter_this_prop = counter_this_prop + 1;
obj.LH{propNum}(counter_this_prop) = new_handle;
% save new counter value
obj.LH_point_counter(propNum) = counter_this_prop;
end