Python实时绘图

7

我有两个数组来存储数据:一个存时间,另一个存数值。当我收集了1000个点,就会触发信号并将它们绘制在图上(x轴为时间,y轴为数值)。

我需要在同一张图上保留之前的绘制结果,但只保留合适的数量以避免减慢进程。例如,我想在图上保留10000个点。matplotlib中的交互式绘图可以正常工作,但我不知道如何删除前面的点,并且速度非常慢。 我研究过matplotlib.animation, 但它似乎只是重复相同的绘图,而无法真正地更新。

我真的在寻找一种轻量级的解决方案,以避免任何减慢。

由于我需要长时间采集数据,所以每次循环都会清空输入数据(第1001个点存储在第一行等等)。

这是目前我的代码,但它会在图上保留所有的点:

import matplotlib.pyplot as plt

def init_plot():
  plt.ion()
  plt.figure()
  plt.title("Test d\'acqusition", fontsize=20)
  plt.xlabel("Temps(s)", fontsize=20)
  plt.ylabel("Tension (V)", fontsize=20)
  plt.grid(True)

def continuous_plot(x, fx, x2, fx2):
  plt.plot(x, fx, 'bo', markersize=1)
  plt.plot(x2, fx2, 'ro', markersize=1)
  plt.draw()

我调用init函数一次,而continuous_plot在一个进程中运行,每当我有1000个点在我的数组中时,便被调用。

你有尝试过自己编写这个程序吗?如果有的话,请分享你的代码。 - Ffisegydd
4个回答

8

最轻便的解决方案是替换现有图形的X和Y值。(如果你的X数据不变,也可以仅替换Y值。)以下是一个简单的例子:

import matplotlib.pyplot as plt
import numpy as np
import time

fig = plt.figure()
ax = fig.add_subplot(111)

# some X and Y data
x = np.arange(10000)
y = np.random.randn(10000)

li, = ax.plot(x, y)

# draw and show it
ax.relim() 
ax.autoscale_view(True,True,True)
fig.canvas.draw()
plt.show(block=False)

# loop to update the data
while True:
    try:
        y[:-10] = y[10:]
        y[-10:] = np.random.randn(10)

        # set the new data
        li.set_ydata(y)

        fig.canvas.draw()

        time.sleep(0.01)
    except KeyboardInterrupt:
        break

这种解决方案也非常快。上面代码的最大速度为每秒100次重绘(由 time.sleep 限制),我得到的大约是70-80次,这意味着每次重绘需要大约4毫秒。但由于后端等因素的影响,你的情况可能会有所不同。


3
你的解决方案看起来不错,但是使用你的代码后,坐标轴被阻塞了并且绘图发生在可视域之外。我应该添加什么以自动调整坐标轴?编辑:找到解决方案了:我需要在循环中的fig.canvas.draw之前添加以下这两行: ax.relim() ax.autoscale_view(True,True,True) - CoMartel

6

使用固定大小的数组,并使用matplot绘制它。

 import collections
 array = collections.deque([None] * 1000, maxlen=1000)

每当您向数组追加元素时,它都会删除第一个元素。

0

我知道我回答这个问题有点晚了,但是针对你的问题,你可以看一下“joystick”包。它基于line.set_data()和canvas.draw()方法,还可以选择重新调整轴。除了图形绘制之外,它还允许交互式文本记录或图像绘制。

不需要在单独的线程中编写自己的循环,该包会处理它,只需提供所需的更新频率即可。此外,控制台仍然可用于其他监视命令。

请参见http://www.github.com/ceyzeriat/joystick/https://pypi.python.org/pypi/joystick(使用pip install joystick进行安装)

尝试一下:

import joystick as jk
import numpy as np
import time

class test(jk.Joystick):
    # initialize the infinite loop decorator
    _infinite_loop = jk.deco_infinite_loop()

    def _init(self, *args, **kwargs):
        """
        Function called at initialization, see the doc
        """
        self._t0 = time.time()  # initialize time
        self.xdata = np.array([self._t0])  # time x-axis
        self.ydata = np.array([0.0])  # fake data y-axis
        # create a graph frame
        self.mygraph = self.add_frame(jk.Graph(name="test", size=(500, 500), pos=(50, 50), fmt="go-", xnpts=10000, xnptsmax=10000, xylim=(None, None, 0, 1)))

    @_infinite_loop(wait_time=0.2)
    def _generate_data(self):  # function looped every 0.2 second to read or produce data
        """
        Loop starting with the simulation start, getting data and
    pushing it to the graph every 0.2 seconds
        """
        # concatenate data on the time x-axis
        self.xdata = jk.core.add_datapoint(self.xdata, time.time(), xnptsmax=self.mygraph.xnptsmax)
        # concatenate data on the fake data y-axis
        self.ydata = jk.core.add_datapoint(self.ydata, np.random.random(), xnptsmax=self.mygraph.xnptsmax)
        self.mygraph.set_xydata(t, self.ydata)

t = test()
t.start()
t.stop()

0
为了完全交互,您可以使用 Bokeh。具体而言,您可以使用每隔 X 毫秒调用一次的更新函数并流式传输新数据。
这里有一个我使用的片段:
def update():
     candle_data.stream(new_data, 300)   

plot = figure(x_axis_type='datetime',x_range=(start_day, final_day), width=1500, height=900, title='Live Chart', sizing_mode='scale_both')
plot.segment(x0='time', y0='highest', x1='time', y1='lowest', color='black', source=candle_data)
plot.vbar(x='time', width = 0.5*60*60*50 ,bottom='open', top='close',fill_color='color', line_color='black', source = candle_data) 
doc.add_root(column([plot]))
doc.add_periodic_callback(update, 20000)
doc.title = "Candle Data Live Rates"

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