Python实时绘图

7 投票
4 回答
47340 浏览
提问于 2025-04-18 13:33

我有两个数组,一个用来存时间,另一个用来存数值。当我收集到1000个数据点时,我就会触发一个信号,并把这些点画出来(x轴是时间,y轴是数值)。

我希望在同一张图上保留之前的绘图,但只保留合理数量的数据点,以免让处理变慢。比如,我想在图上保留10000个点。

虽然matplotlib的交互式绘图效果不错,但我不知道怎么删除最开始的点,这样会很快拖慢我的电脑。

我查了一下matplotlib.animation,但它似乎只是重复之前的图,而不是实时更新。

我真的在寻找一个轻量级的解决方案,以避免任何的卡顿。

因为我收集的数据量很大,所以在每次循环中我会删除输入数据(第1001个点存储在第1行,以此类推)。

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

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()

我只调用一次初始化函数,而连续绘图的过程会在每次我有1000个点的时候被调用。

4 个回答

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"
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()
6

使用一个固定大小的数组,然后用matplot来绘制它。

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

每当你往这个数组里添加新元素时,它会把第一个元素删除。

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毫秒。不过,具体速度可能会根据不同的后台等因素有所不同。

撰写回答