使用matplotlib绘制动态数据
我正在写一个应用程序,用来显示动态变化的数据(这些数据是从一个套接字读取的)。
作为一个简单的例子,我尝试画一个正弦波,每秒把振幅乘以1.1:
import numpy as np
import matplotlib.pyplot as plt
import time
x = np.arange(0, 10, 0.1);
y = np.sin(x)
for i in xrange(100):
plt.plot(x, y)
time.sleep(1)
y=y*1.1
这显然不是正确的方法,但它表达了我的意图。
那么,应该怎么做才对呢?
编辑:以下是@mskimm回答中建议的代码的错误追踪输出:
plt.show() #Exception in thread Thread-2:
Traceback (most recent call last):
File "/usr/lib/python2.7/threading.py", line 552, in __bootstrap_inner
self.run()
File "/usr/lib/python2.7/threading.py", line 505, in run
self.__target(*self.__args, **self.__kwargs)
File "<ipython-input-5-ed773f8e3e84>", line 7, in update
plt.draw()
File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 466, in draw
get_current_fig_manager().canvas.draw()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 240, in draw
tkagg.blit(self._tkphoto, self.renderer._renderer, colormode=2)
File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 12, in blit
tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
RuntimeError: main thread is not in main loop
编辑 2:
结果发现同样的代码在qtconsole中运行是可以的……(有人知道为什么吗?)不过,每次打印的时候都要重新调整比例,所以“动画效果”就没了。我尝试使用 plt.autoscale_view(False,False,False)
,但这导致根本没有图形显示。
3 个回答
0
另一种解决办法是从头开始重新绘制图表。虽然这样比逐点更新要慢,但如果你的数据点不多,这种额外的时间消耗几乎可以忽略不计。
from IPython import display
from matplolib import pyplot as plt
def dynamic_plot(X,Y, figsize=[10,5], max_x=None, min_y=None, max_y=None):
'''plots dependency between X and Y dynamically: after each call current graph is redrawn'''
plt.gcf().set_size_inches(figsize)
plt.cla()
plt.plot(X,Y)
if max_x:
plt.gca().set_xlim(right=max_x)
if min_y:
plt.gca().set_ylim(bottom=min_y)
if max_y:
plt.gca().set_ylim(top=max_y)
display.display(plt.gcf())
display.clear_output(wait=True)
下面是一个在jupyter notebook中使用的示例:
import time
X=[]
Y=[]
for i in range(10):
X.append(i)
Y.append(i**.5)
dynamic_plot(X,Y,[14,10], max_x=10, max_y=4)
time.sleep(0.3)
3
使用 animated
这个功能难吗?那用 thread
来更新图形怎么样呢?虽然 plt.show()
是一个阻塞的操作,也就是说它会让程序停下来等着显示图形,但 thread
还是可以更新图形的。
import numpy as np
import matplotlib.pyplot as plt
import time
import threading
def update(x, y):
for i in xrange(100):
# clear
plt.clf()
plt.plot(x, y)
# draw figure
plt.draw()
time.sleep(1)
y=y*1.1
x = np.arange(0, 10, 0.1);
y = np.sin(x)
plt.plot(x, y)
# use thread
t = threading.Thread(target=update, args=(x, y))
t.start()
plt.show() # blocking but thread will update figure.
12
有更好的方法可以使用 matplotlib 动画 API 来实现这个功能,不过这里有一个简单粗暴的方法:
import numpy as np
import matplotlib.pyplot as plt
x = np.arange(0, 10, 0.1)
y = np.sin(x)
plt.ion()
ax = plt.gca()
ax.set_autoscale_on(True)
line, = ax.plot(x, y)
for i in xrange(100):
line.set_ydata(y)
ax.relim()
ax.autoscale_view(True,True,True)
plt.draw()
y=y*1.1
plt.pause(0.1)
主要步骤如下:
- 通过
plt.ion()
打开交互模式。 - 记录你想要更新的线条,并直接修改它们的数据,而不是再次调用
plot
。 - 通过调用
plt.pause
给 Python 一些时间来更新绘图窗口。
我在代码中加入了自动调整视口大小的部分,但这并不是绝对必要的。