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

描画範囲を更新する場合、更新動作中のユーザによる描画範囲の拡大・縮小などは行えません (変更してもすぐに再描画されるため)。

そこで、次回の説明ではキー入力イベントにより、再描画の停止、再開を制御する方法を紹介します

おすすめ