Python实时绘图
我有两个数组,一个用来存时间,另一个用来存数值。当我收集到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 个回答
如果想要实现完全的互动效果,可以使用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"
我知道我回答这个问题有点晚,不过对于你的问题,你可以看看“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()
使用一个固定大小的数组,然后用matplot来绘制它。
import collections
array = collections.deque([None] * 1000, maxlen=1000)
每当你往这个数组里添加新元素时,它会把第一个元素删除。
你可以用最简单的方法来替换已有图表的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毫秒。不过,具体速度可能会根据不同的后台等因素有所不同。