How should I update the data of a plot in Matlab? - matlab

Suppose that I want to update a plot with a new data. What method should I choose?
Set the XDataSource property to some name, update the variable, and call refreshdata
Erase the original plot, and call plot command again.
Use Set('Xdata',...')

Short answer : always use Set('Xdata',...').
Example code:
function PlotUpdate()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
y = sin(x.^3);
set(h,'XData',x,'YData',y);
end
Long answer:
There are three relevant measures by which one should choose the best method.
Code clarity - How easy it is for someone to read your code?
Runtime - How quick each method performs its task?
Code portability - How fast can you re-factor your code?
Now, let's analyze the possible methods.
Method(1) - refreshdata
function PlotUpdate()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
set(h,'YDataSource','y')
set(h,'XDataSource','x')
y = sin(x.^3);
refreshdata(h,'caller');
end
M-lint immediately issues a warning in the line y=sin(x.^3)
The value assigned to variable `y` might be unused
Why does it happen? refreshdata uses eval and m-lint cannot know that you will use y. Someone reading your code, might as well remove this line completely. This happened because you broke the encapsulation principle. refreshdata accesses variables from the caller workspace. Another way to take a look at this, suppose that you pass the handle of the plot to another function. The reader has no clue to why on earth you wrote y = sin(x.^3);, and how is it going to be related to the update of the plot.
Now let's discuss speed/runtime. By taking a look at refreshdata source code, you will notice two ugly for-loops, that go through all of the graphics handles variables in your space. Here is the first:
% gather up all the objects to refresh
objs = {};
for k = 1:length(h)
obj = h(k);
objfields = fields(obj);
for k2 = 1:length(objfields)
% search for properties ending in DataSource
if strncmpi(fliplr(objfields{k2}),'ecruoSataD',10)
objs = {objs{:},obj, objfields{k2}};
end
end
end
Imagine that you have not one plot, but 100 plot and you want to update only the first. This will be very slow, because for each of the plots, you attempt to find the one you need! (I am leaving as an exercise for the reader to figure out what is ecruoSataD, and how it is used.)
Even if you give the relevant plot as an argument, you still have the second loop, that runs eval several times. Not exactly efficient. I will show a time comparison in the end.
Conclusion : Hard to understand, hard to refactor, slow runtime
Method (2) - Delete and re-plot
function PlotUpdate()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
set(h,'YDataSource','y')
set(h,'XDataSource','x')
y = sin(x.^3);
delete(h);
h = plot(x,y);
end
This method is quite clear for the reader. You deleted the plot, and drew a new one. However, as we will see from the time comparison in the end, that is the slowest method.
Conclusion : Easy to understand, easy to refactor, very slow runtime
Method(3) - set('XData',...,'YData')
The code is really clear. You want to modify a two properties of your plot, XData and YData. And that is exactly what you do. Also, the code runs really fast, as you can see from the comparison below.
function PlotUpdate()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
y = sin(x.^3);
set(h,'XData',x,'YData',y);
end
Since the new graphics engine hg2 (R2014b and up), you can also use property syntax for specifying data if you prefer that notation:
function PlotUpdate()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
y = sin(x.^3);
h.XData = x;
h.YData = y;
end
Conclusion : Easy to understand, easy to refactor, fast runtime
Here is the time comparison code
function PlotUpdateTimeCompare()
x = 0:.1:8;
y = sin(x);
h = plot(x,y);
set(h,'YDataSource','y')
set(h,'XDataSource','x')
y = sin(x.^3);
tic
for i=1:100
refreshdata(h,'caller');
end
toc
tic
for i=1:100
delete(h);
h = plot(x,y);
end
toc
tic
for i=1:100
set(h,'XData',x,'YData',y);
end
toc
end
And the results:
Elapsed time is 0.075515 seconds.
Elapsed time is 0.179954 seconds.
Elapsed time is 0.002820 seconds.

You can call the function drawnow and do something like that :
h = plot(nan);
for i = 1:n
y = ...
set(h,'YData',y);
drawnow %update the graph
end

Suppose that I want to update a plot with a new data. What method should I choose?
If you have more than one line object in the given axes then Method:
Set the XDataSource property to some name, update the variable, and call refreshdata
will generate an error in MATLAB R2012b. An appropriate example is provided in Andrey's answer.
A bug has been submitted to the Mathworks.

Related

Animate a line on matlab

I would like to animate a self updating plot on matlab. For example a string vibrating between two ends. All the basic animate functions I have found in the documentation accomplish the same thing, mainly to animate an evolving plot. I.e the function remains the same but the number of points plotted increases (or decreases) in time. For example this script:
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
traces a sine function as if an invisible hand was drawing it. What I would like to do is animate the entire function but with a varying parameter, like the phase or amplitude. I tried to modify using the following:
x = linspace(0,4*pi,1000);
;
axis([0,4*pi,-1,1])
for k = 1:10
h = animatedline(x,sin(k*x))
drawnow
end
This is sort of close to what I need but the functions are progressively appended, not replaced. This results in a total of 10 functions being plotted instead of an animation.
Does anyone understand what I need to do? If so how can this be accomplished?
What about this:
h = animatedline;
axis([0,4*pi,-1,1]);
x = linspace(0,4*pi,1000);
for k = 1:0.01:10
y = sin(k*x);
clearpoints(h);
addpoints(h,x,y);
drawnow
end

MATLAB how do I plot my Runge-Kutta4 ODE solution on whole interval

I made this code for ODE solutions using Runge-Kutta4:
function y=myODE(f,y0,x0,h,x_final)
x=x0;
y=y0;
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4)
x=x+h;
end
f is the function y' = f(x,y), y0 is initial value, x0 is where the function starts, h subinterval and x_final is where the function stops.
I tried my code and it solves ODEs for me correctly, but I also want to plot my function over a xy-axis on the interval x0 to x_final with h subintervals. When I try to plot it using plot(x0:h:x_final,y) I only get an empty graph. I understand (guessing) that I have to bind my y to several x in order to plot, but how can I do that without changing my code too much?
How can I plot the graph for y given y0, interval x0 to x_final and given h?
New to MATLAB, appreciate all help I can get!
Edit: To make clear what my code is for;
I need this ODE solver both for solution and graphing. I'm supposed to study the truncation error by looking at values of y on h compared to 2*h and the stability of Runge-Kutta4 by looking at graphs of y with different h.
This is not a very smart refactoring of your code (because it will slow down the solving, also will kill you graphics depending on how many steps you have in your ODE) but I'm sleepy so I go for the hack:
function y=myODE(f,y0,x0,h,x_final)
hold(axes('Parent',figure),'on');
x=x0;
y=y0;
plot(x,y, 'o');
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
plot(x,y,'o');
end;
end
Maybe tomorrow I'll re-write this answer to something proper, but—for now—good-night! :-)
function y=myode(f,y0,x0,h,x_final)
x=x0;
y=y0;
plot(x0,y0,'.')
hold on
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
plot(x,y,'.')
disp([x,y])
end
The comment box didn't let me to put my fixed-code in "code-format" so post it as an answer.
I would suggest you store the x and y values to vectors and plot them outside the loop. You may want to also output bigX and bigY in order to compare with the exact solution.
function y=myODE(f,y0,x0,h,x_final)
% Example:
% f = #(x,y) (x.^2+cos(y));
% y_final = myODE(f,0,0,0.1,2);
x=x0;
y=y0;
bigX = x0:h:x_final;
if bigX(end)<x_final
% Doesn't occur when x_final = n*h for some integer n
bigX = [bigX,x_final];
end
bigY = zeros(size(bigX));
count = 1;
bigY(1) = y0;
while x<x_final
h=min(h,x_final-x);
k1=f(x,y);
k2=f(x+h/2,y+h*k1/2);
k3=f(x+h/2,y+h*k2/2);
k4=f(x+h,y+h*k3);
y=y+(h/6)*(k1+2*k2+2*k3+k4);
x=x+h;
count = count+1;
bigY(count) = y;
end
plot(bigX,bigY,'b-o')
xlabel('x')
ylabel('y')

How to plot error in composite simpson's rule method

I want to plot the error in the composite simpson's rule
... Here is my code
for r=1:100
n=600;
a=0;
b=5;
err=[];
x=zeros(1,n);
f=#(x)cos(x)+x.^2;
h=(b-a)/n;
xexact=integral(f,a,b);
p=0;
q=0;
for i=1:n
x(i)=a+(i-1)*h;
end
for i=1:n-1
p=p+2*(f(x(i)))+4*(f(x(i)+h/2));
end
x=(h/6)*(f(a)+f(a+h/2)+p+f(b))
err(end+1)=x-xexact;
plot(r,x,'*')
end
When I run the code I get one point in the plot .. I want to have all the points ploted in the plot , How to do that ?
thanks
The simplest fix is to put hold on; before your code here and hold off; after it. That way, each of your calls to plot will add a point to an existing plot, rather than creating a new one. (What is happening at the moment is that you are plotting a point with r=1, and then replacing that plot with one for r=2, etc. So what you end up with is a plot showing just what happens for r=100.)
You might do better, though, to build up an array containing your xs as your loop runs, and then do a single plot(1:100, xs, '*'); at the end.
There is a conceptual problem in your code: you want to plot the x variable which is not function of r and that will never change for each r value. So you will plot a row of r dots with a fixed x value.
Moreover I think that, if possible, is good to avoid for loops in Matlab because there are more powerful and optimized ways to do things that you do with them. For example I will write this code in this form:
function [x, err] = compSimp(n, a, b)
err = [];
x=zeros(1,n+1);
f=#(x)cos(x)+x.^2;
h=(b-a)/n;
xexact=integral(f,a,b);
% x_0 = a
x(1)=a;
% x_n = b
x(n+1)=b;
p=0;
% x_j = a + j*h with j = 1, 2, ... , n-2, n-1 (no need to set again x_0 and x_n)
x(2:n)= a + (1:(n-1))*h;
% summations
for i=2:2:n
p = p + 2*(f(x(i+1))) + 4*(f(x(i)));
end
x=(h/3)*(f(a) + p + f(b));
err(end+1)=x-xexact;
end
As you can see i've changed some code logic, following wikipedia for theory. If you want, you can also change code so that you can pass a custom f function to the compSimp function.

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