使用matplotlib绘制动态数据

2 投票
3 回答
15449 浏览
提问于 2025-04-18 02:40

我正在写一个应用程序,用来显示动态变化的数据(这些数据是从一个套接字读取的)。

作为一个简单的例子,我尝试画一个正弦波,每秒把振幅乘以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)

主要步骤如下:

  1. 通过 plt.ion() 打开交互模式。
  2. 记录你想要更新的线条,并直接修改它们的数据,而不是再次调用 plot
  3. 通过调用 plt.pause 给 Python 一些时间来更新绘图窗口。

我在代码中加入了自动调整视口大小的部分,但这并不是绝对必要的。

撰写回答