传递新数据时无法更新图形(“动态图”)
Posted
技术标签:
【中文标题】传递新数据时无法更新图形(“动态图”)【英文标题】:Unable to update figure when new data are passed ("dynamic plot") 【发布时间】:2021-02-17 10:04:41 【问题描述】:我找不到在移动滑块时更新图表的解决方案(在 Jupyter 笔记本中)。该图显示了正弦和余弦函数,以及它们的总和。正弦和余弦通过 0 到 1 之间的滑块 (k) 的值进行加权:
正弦 = k * sin(x) 余弦 = (1-k) * cos(x)要绘制的数组已正确更新,例如对于 k = 0.2:
np.max(graph.ysin), np.max(graph.ycos)
0.19997482553477502, 0.7995972339065481)
绘图不会更新,它们继续显示 k = .5(初始值)的曲线。
请注意,我不希望(尽可能)在每次滑块更改时重新创建整个图形,只是通过使用 Line2D.set_data 传递更新后的数组来更新绘图。
我尝试了什么:
添加plt.show ()
调用(建议here)
添加fig.canvas.draw()
(建议here)
添加%matplotlib notebook
(建议here)
添加ax.relim() and ax.autoscale_view()
(建议here)
以及它们的一些组合......
class DynamicGraph ():
def __init__ (self, ratio):
# Define x range
self.x = np.linspace (-np.pi, np.pi, 100)
# Compute and display values
self.init_figure (ratio)
# Set figure, axe and plots
def init_figure (self, ratio):
self.fig, self.ax = plt.subplots (figsize=(8,3))
self.ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(np.pi/2))
self.ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(lambda x,pos:f'x/np.pi $\pi$'))
self.ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(.25))
self.ax.axhline(-.5, color='grey', lw=.5)
self.ax.axhline(0, color='grey', lw=.5)
self.ax.axhline(.5, color='grey', lw=.5)
self.ax.axvline(0, color='grey', lw=.5)
self.show_plots (ratio, init=True)
plt.legend ()
return
# Create/update plots
def show_plots (self, ratio, init=False):
self._compute_values_ (ratio)
if init:
self.sin_plot, = self.ax.plot(self.x, self.ysin, color='grey', ls=':', label='sin');
self.cos_plot, = self.ax.plot(self.x, self.ycos, color='grey', ls='--', label='cos');
self.sum_plot, = self.ax.plot(self.x, self.ysum, color='green', label='sin+cos');
else:
self.sin_plot.set_data (self.x, self.ysin)
self.cos_plot.set_data (self.x, self.ycos)
self.sum_plot.set_data (self.x, self.ysum)
return
# Compute values which change with the slider value
def _compute_values_ (self, ratio):
self.ysin = ratio*np.sin(self.x)
self.ycos = (1-ratio)*np.cos(self.x)
self.ysum = self.ysin + self.ycos
return
# Create slider, listen to changes
slider = widgets.FloatSlider (min=0, max=1, value=.5)
slider. observe(lambda change: graph.show_plots (slider.value), names='value')
# Show slider and graph
display(slider)
graph = DynamicGraph (slider.value)
我希望对解决方案进行一些解释,例如如果这与特定上下文或版本等有关,请了解上述建议为何不适用。
【问题讨论】:
【参考方案1】:经过多次尝试,我发现了这种有效的指令组合:
在代码开头添加 %matplotlib notebook
以允许 Jupyter notebook 上的交互模式(与我假设的 Jupyterlab 相反)。
更新数据后添加fig.canvas.draw()
。
这些说明似乎没用,但正如我看到它们经常使用的那样,它们可能会产生一些我忽略的影响:
plt.ion()
开启交互模式
fig.canvas.flush_events()
在fig.canvas.draw()
之后
%matplotlib notebook
#plt.ion()
class DynamicGraph ():
def __init__ (self, ratio):
# Define x range
self.x = np.linspace (-np.pi, np.pi, 100)
# Compute and display values
self.init_figure (ratio)
# Set figure, axe and plots
def init_figure (self, ratio):
self.fig, self.ax = plt.subplots (figsize=(8,3))
self.ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(np.pi/2))
self.ax.xaxis.set_major_formatter(mpl.ticker.FuncFormatter(lambda x,pos:f'x/np.pi $\pi$'))
self.ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(.25))
self.ax.axhline(-.5, color='grey', lw=.5)
self.ax.axhline(0, color='grey', lw=.5)
self.ax.axhline(.5, color='grey', lw=.5)
self.ax.axvline(0, color='grey', lw=.5)
self.show_plots (ratio, init=True)
plt.legend ()
return
# Create/update plots
def show_plots (self, ratio, init=False):
self._compute_values_ (ratio)
if init:
self.sin_plot, = self.ax.plot(self.x, self.ysin, color='grey', ls=':', label='sin');
self.cos_plot, = self.ax.plot(self.x, self.ycos, color='grey', ls='--', label='cos');
self.sum_plot, = self.ax.plot(self.x, self.ysum, color='green', label='sin+cos');
else:
self.sin_plot.set_data (self.x, self.ysin)
self.cos_plot.set_data (self.x, self.ycos)
self.sum_plot.set_data (self.x, self.ysum)
self.fig.canvas.draw()
#self.fig.canvas.flush_events()
return
# Compute values which change with the slider value
def _compute_values_ (self, ratio):
self.ysin = ratio*np.sin(self.x)
self.ycos = (1-ratio)*np.cos(self.x)
self.ysum = self.ysin + self.ycos
return
# Create slider, listen to changes
slider = widgets.FloatSlider (min=0, max=1, value=.5)
slider. observe(lambda change: graph.show_plots (slider.value), names='value')
# Show slider and graph
display(slider)
graph = DynamicGraph (slider.value)
【讨论】:
以上是关于传递新数据时无法更新图形(“动态图”)的主要内容,如果未能解决你的问题,请参考以下文章