Pyplotでのデータのリアルタイムプロット
Pyplotでデータを受信しながらプロットを更新していく方法
いろいろ実現方法はあるようですが、一番お手軽そうなのがmatplotlib.animation
を使用する方法です
matplotlib.animation.FuncAnimation
に、初期化関数、データ更新関数を登録することでリアルタイムにデータの更新が可能です
サンプルコード
下記にサンプルコードを示します。
RealtimePlot.plot_init()
でプロットを初期化します。実際には初期化関数でなく、更新関数 (update_data()
)内でも軸の範囲設定や系列の追加は可能です。
RealtimePlot.update_data()
はデータ更新用の関数です。こちらでUDPや、センサからデータを受信する処理を実行してもいいですし、別スレッドで更新データをセットしてもいいかもしれません。下記コードではダミーの受信関数receive_data_dummy()
でデータを更新しています
import math
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import time
class RealtimePlot:
def __init__(self):
self.fig, self.ax = plt.subplots()
self.x = 0
self.xdata = []
self.ydata = {'data1': [], 'data2': []}
self.func = {'data1': lambda x: x,
'data2': lambda x: math.sin(x)}
self.ani = FuncAnimation(self.fig, self.update_data,
init_func=self.plot_init,
frames=None)
plt.show()
def plot_init(self):
# lineデータの初期化 (update関数で追加することも可能)
self.ln = {}
for name, ydata in self.ydata.items():
self.ln[name], = plt.plot(self.xdata, ydata, label=name)
# X/Y軸の範囲指定 (update関数での更新も可能)
self.ax.set_xlim(0, math.pi*2)
self.ax.set_ylim(-math.pi*2, math.pi*2)
self.ax.legend()
self.ax.grid()
return self.ln.values()
def update_data(self, _):
self.receive_data_dummy()
for name, ydata in self.ydata.items():
self.ln[name].set_data(self.xdata, ydata)
return self.ln.values()
def receive_data_dummy(self):
"""ダミーのデータ更新関数
実際はUDPやセンサから受信したデータで更新する
"""
x = self.x
self.xdata.append(x)
for name, ydata in self.ydata.items():
ydata.append(self.func[name](x))
self.x += 0.05
time.sleep(0.0001)
def main():
rp = RealtimePlot()
if __name__ == "__main__":
main()
表示範囲の更新
上記例では初期で設定したX軸およびY軸の範囲を新しいデータが肥えた場合でも更新されません。(ユーザによる範囲調整は可能です)
範囲を更新するためには、更新関数の中でself.ax.set_xlim()
およびself.ax.set_ylim()
により新しい範囲を指定したうえで、self.fig.canvas.draw()
により再描画を行います
def update_data(self, _):
self.receive_data_dummy()
for name, ydata in self.ydata.items():
self.ln[name].set_data(self.xdata, ydata)
# 表示範囲の更新例 (x_max, y_min/maxは別のどこかで更新)
self.ax.set_xlim(0, self.x_max)
self.ax.set_ylim(self.y_min, self.y_max)
self.fig.canvas.draw() # 更新した場合は再描画
return self.ln.values()
描画範囲を更新する場合、更新動作中のユーザによる描画範囲の拡大・縮小などは行えません (変更してもすぐに再描画されるため)。
そこで、次回の説明ではキー入力イベントにより、再描画の停止、再開を制御する方法を紹介します
最近のコメント