如何在Matlab中更新绘图数据?

35

假设我想用新数据更新一个绘图,我应该选择哪种方法?

  1. XDataSource 属性设置为某个名称,更新变量,并调用 refreshdata 方法。
  2. 删除原始的 plot,然后再次调用 plot 命令。
  3. 使用 Set('Xdata',...') 方法。

相关:Matlab中的实时绘图 - gnovice
3个回答

57

简短回答:始终使用Set('Xdata',...')

示例代码:

function PlotUpdate()   
    x = 0:.1:8;
    y = sin(x);
    h = plot(x,y);
    y = sin(x.^3);    
    set(h,'XData',x,'YData',y);
end

长答案:

选择最佳方法应考虑三个相关因素。

  1. 代码清晰度 - 你的代码易于被他人阅读吗?
  2. 运行时间 - 每种方法执行任务的速度有多快?
  3. 代码可移植性 - 你重构代码需要多长时间?

现在,让我们分析可能的方法。

方法(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会立即在这行代码y=sin(x.^3)上发出警告。

The value assigned to variable `y` might be unused
为什么会发生这种情况?refreshdata 使用 eval,而 m-lint 不知道你将使用 y。读取你的代码时,可能会完全删除此行。这是因为你违反了封装原则。 refreshdata 从调用者工作区访问变量。另一种看待这个问题的方式是,假设你将图的处理传递给另一个函数。读者不知道你为什么写了 y = sin(x.^3);,以及它如何与图的更新相关。

现在让我们讨论速度/运行时间。通过查看 refreshdata 的源代码,你会注意到两个丑陋的 for 循环,遍历你空间中的所有图形句柄变量。以下是第一个:

% 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

想象一下,你不只有一个图表,而是有100个图表,并且你只想更新第一个。这会非常缓慢,因为对于每个图表,你都要尝试找到需要的那个!(我将其留作练习,让读者自己弄清楚ecruoSataD是什么,以及如何使用它。)

即使你将相关的图表作为参数传递,你仍然有第二个循环,运行了多次eval。效率并不高。最后我会展示一下时间比较。

结论:难以理解,难以重构,运行缓慢


方法(2)-删除并重新绘制

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

这种方法非常清晰易懂。你删除了绘图,然后画了一个新的。但是正如我们将在最后的时间比较中看到的那样,这是最慢的方法。

结论: 易于理解,易于重构,运行速度非常慢


方法(3) - set('XData',...,'YData')

这段代码非常清晰。你想修改绘图的两个属性,即XDataYData,并确实这就是你所做的。此外,该代码运行速度非常快,可以从以下比较中看出。

function PlotUpdate()   
    x = 0:.1:8;
    y = sin(x);
    h = plot(x,y);
    y = sin(x.^3);          
    set(h,'XData',x,'YData',y);
end
自从新的hg2图形引擎(R2014b及以上版本)推出以来,您也可以使用属性语法来指定数据,如果您更喜欢这种表示方法。
function PlotUpdate()   
    x = 0:.1:8;
    y = sin(x);
    h = plot(x,y);
    y = sin(x.^3);          
    h.XData = x;
    h.YData = y;
end

结论:易于理解,易于重构,运行速度快


这里是时间比较的代码

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

结果如下:

已用时间为0.075515秒。
已用时间为0.179954秒。
已用时间为0.002820秒。


2
而且你可以在方法(1)中添加,refreshdata 在 R2012b 中已经失效。 - MattLab
@MattLab,谢谢你的信息。我没有安装2012b版本。你能给个例子或者提供一些参考资料吗?你也可以将这个事实作为另一个回答来回答这个问题。 - Andrey Rubshtein
1
好的观点。我在“preferred”代码中修复了一个错别字 - 你调用了set(h,'XDataSource',x),我认为这是一个复制粘贴错误。第一个代码片段没有这些。否则,我理解你的观点。不过,另一方面,如果你有100个图表需要刷新,调用refreshdata只需要一行代码,而在重新计算输入数据后调用100次set(h, ...)可能会有点繁琐。 - angainor
加号:当你想在回调内部编辑数据时,使用 set('XData') 比使用 refreshdata 更容易。唯一需要寻找(或使用 persistent 缓存)的是图形句柄。我曾经尝试自动链接内容时遇到了麻烦,但切换到 XData 后非常顺畅。 - lemonzi
"h.XData = x; h.YData = y;" 在我的电脑上比等价的"set(h,'XData',x,'YData',y);"快33%。我已经在你的脚本中测试过,当i=1:1000时,总时间为0.058861秒,而"set(h,'XData',x,'YData',y);"则为0.088543秒。 - Lucademicus
@Lucademicus,谢谢你的信息。这篇文章有点旧了,也许新版本的Matlab已经优化了? - Andrey Rubshtein

3
你可以调用函数drawnow,并实现以下操作:
h = plot(nan);

for i = 1:n
  y = ...
  set(h,'YData',y);
  drawnow                 %update the graph
end

2
假设我想用新数据更新一个图表。应该选择哪种方法?
如果在给定的坐标轴中有多个线条对象,则应选择以下方法之一:
1.将XDataSource属性设置为某个名称,更新变量,并调用"refreshdata"。
在MATLAB R2012b中,这种方法会生成错误。Andrey的答案提供了适当的示例。
已经向Mathworks提交了一个错误报告。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接