【发布时间】:2012-10-17 15:32:23
【问题描述】:
假设我想用新数据更新绘图。我应该选择什么方法?
- 将
XDataSource属性设置为某个名称,更新变量,然后调用refreshdata - 抹掉原来的
plot,再次调用plot命令。 - 使用
Set('Xdata',...')
【问题讨论】:
标签: matlab plot graph matlab-figure matlab-guide
假设我想用新数据更新绘图。我应该选择什么方法?
XDataSource 属性设置为某个名称,更新变量,然后调用refreshdata plot,再次调用plot命令。Set('Xdata',...')
【问题讨论】:
标签: matlab plot graph matlab-figure matlab-guide
简短的回答:总是使用 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) - 刷新数据
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')
代码非常清晰。您想修改绘图的两个属性,XData 和 YData。这正是你所做的。此外,代码运行速度非常快,从下面的比较中可以看出。
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 秒。
【讨论】:
refreshdata 在 R2012b 中损坏的方法 (1) 添加到
set(h,'XDataSource',x),我认为是复制粘贴错字。第一个代码 sn-p 没有这些。否则,我明白你的意思。虽然另一方面,如果您有 100 个图要刷新,调用 refreshdata 是一行,而在重新计算输入数据后调用 set(h, ...) 100 次可能有点乏味。
set('XData') 比refreshdata 更容易。您唯一需要搜索(或使用persistent 缓存)是图形句柄。我在尝试自动链接内容时遇到了麻烦,切换到 XData 后非常流畅。
h.XData = x; h.YData = y; 在我的机器上实际上比同等的 set(h,'XData',x,'YData',y); 快 33%。我已经在您的脚本中进行了测试,设置 i=1:1000 的总时间为 0.058861 秒与 0.088543 秒。
假设我想用新数据更新绘图。我应该选择什么方法?
如果您在给定的轴上有多个线对象,则方法:
- 将 XDataSource 属性设置为某个名称,更新变量,然后调用
refreshdata
将在 MATLAB R2012b 中生成错误。 Andrey 的回答中提供了一个适当的例子。
一个错误已提交给 Mathworks。
【讨论】:
您可以调用函数drawnow 并执行类似的操作:
h = plot(nan);
for i = 1:n
y = ...
set(h,'YData',y);
drawnow %update the graph
end
【讨论】: