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

43,203

Solution 1

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.

  1. Code clarity - How easy it is for someone to read your code?
  2. Runtime - How quick each method performs its task?
  3. 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.

Solution 2

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

Solution 3

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:

  1. 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.

Share:
43,203
Andrey Rubshtein
Author by

Andrey Rubshtein

Visit my new blog about Matlab and Image Processing! My favorite answers: How to close files after an error happened in Matlab (Matlab, OOP) Image filters and noise (Image Processing) Identification of leaves (Computer Vision) Deconvolution (Signal processing) Deconvolution, slightly expanded (Signal Processing)

Updated on January 24, 2020

Comments

  • Andrey Rubshtein
    Andrey Rubshtein over 4 years

    Suppose that I want to update a plot with a new data. What method should I choose?

    1. Set the XDataSource property to some name, update the variable, and call refreshdata
    2. Erase the original plot, and call plot command again.
    3. Use Set('Xdata',...')
  • MattLab
    MattLab over 11 years
    And you can add to Method (1) that refreshdata is broken in R2012b
  • Andrey Rubshtein
    Andrey Rubshtein over 11 years
    @MattLab, thanks for the info. I don't have 2012b installed. Can you give an example, or point to some reference? You can also add this fact as another answer to the question.
  • angainor
    angainor over 11 years
    Good points. I fixed a typo in the 'preferred' code - you called set(h,'XDataSource',x), I think a copy-paste typo. The first code snippet does not have those. Otherwise, I see your point. Although on the other hand, if you have 100 plots that you want to refresh, calling refreshdata is one line, while calling set(h, ...) 100 times after you recalculate the input data can be a bit tedious.
  • lemonzi
    lemonzi over 8 years
    Plus: using set('XData') is easier than refreshdata when you want to edit the data inside a callback. The only thing you need to hunt (or cache with persistent) is the graphic handle. I was having trouble trying to auto-link stuff, and after switching to XData is super smooth.
  • Lucademicus
    Lucademicus over 4 years
    h.XData = x; h.YData = y; is actually 33% faster on my machine, than the equivalent set(h,'XData',x,'YData',y);. I've tested in your script, setting i=1:1000 gives a total time of 0.058861 seconds vs 0.088543 seconds.
  • Andrey Rubshtein
    Andrey Rubshtein over 4 years
    @Lucademicus, thanks for the info. The post is a bit old, maybe newer versions of Matlab got optimized?