ホームページ >バックエンド開発 >Python チュートリアル >pyplotによるリアルタイムプロット
単純な投票アプリから生成したデータをグラフにしたいと思いました。私は過去に pyplot をいじったことがありますが、最初から何かを作成しようとしたことはありません。幸いなことに、これは非常に人気があり、StackOverflow などでたくさんの例が見つかります。
私は検索を行って、時間の経過に伴うグラフの更新に関連するこのSOの回答から始めました。
import matplotlib.pyplot as plt import numpy as np # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() x = np.linspace(0, 6*np.pi, 100) y = np.sin(x) fig = plt.figure() ax = fig.add_subplot(111) line1, = ax.plot(x, y, 'r-') # Returns a tuple of line objects, thus the comma for phase in np.linspace(0, 10*np.pi, 500): line1.set_ydata(np.sin(x + phase)) fig.canvas.draw() fig.canvas.flush_events()
このコードは、位相が変化する正弦波をアニメーション化します。
最初の 2 行は、使用したいライブラリをインポートします。matplotlib.pyplot は、GUI のプロットと処理を行います。
ion() メソッドは、理解できれば (理解できないかもしれませんが)、pyplot で GUI を駆動します。 tkinter プログラム内で使用したり、静的画像を生成するために使用したりすることもできますが、この場合、プロットの GUI を処理させるのが合理的です。 (これは、後での flash_events() 呼び出しが行っていることです。つまり、Figure ウィンドウとの対話性を可能にします。)
この例では、numpy メソッド linspace() を使用して x 値を作成します。これは、派手な Python リストである numpy 配列を返します。
math.sin の代わりに np.sin を使用する理由はブロードキャストです。これは、リスト内のすべての項目に関数を適用するための厄介な用語です。実際、map:
を使用して numpy を使わなくても同じことが達成できるのではないかと思いました。
map(lambda n: math.sin(n), x)
しかし、numpy ブロードキャストは便利で使いやすいです。
次に、pyplot のセットアップを行います。まず、新しい「図形」(fig)を作成します。この図にサブプロット (ax) を追加します。サブプロットは多数ある可能性があります。 111 には、「1x1 グリッドを作成し、このサブプロットを最初のセルに配置する」というかなり難解な解釈があります。
このサブプロット (または一連の軸) に、渡された x 値と y 値を使用して線がプロットされます。 (点は直線で結ばれ、連続的にプロットされます。)「r-」は、赤い実線を指定する短縮方法です。複数の行を指定できるため、plot() はタプルを返します。上記のコードは、タプルのアンパックを使用して、必要な 1 つの値を抽出します。
これは良いスタートですが、時間の経過とともに X 軸を拡張する必要があります。また、このコードは、必要に応じて y 軸の境界を更新しません。最初のプロットで計算された境界にロックされます。もう少し検索すると、このSOの答えにたどり着きます。それらを引用すると:
Axes の dataLim を更新してから、dataLim に基づいて Axes の viewLim を更新する必要があります。適切なメソッドは、axes.relim() メソッドと ax.autoscale_view() メソッドです。
確かに、いいですね。彼らの例に基づいて、x と y の両方で成長するデモ グラフを作成しました。
import matplotlib.pyplot as plt import numpy as np from threading import Thread from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Returns a tuple of line objects growth = 0 while True: x.append(x_next_max / 10) x_next_max += 1 line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
今、私はどこかに向かっています。しかし、これはブロッキング ループであり、データを時々更新する必要があります。複数のスレッドがある場合、変数の更新がスレッドセーフであるかどうかを心配する必要があります。この場合、変数は 5 分に 1 回しか更新されない (ポーリング関数が実行される頻度は同じ) ことがわかっているため、怠惰になる可能性があります。コード行の途中で変数が上書きされる危険はありません。
import matplotlib.pyplot as plt import numpy as np from threading import Timer from time import sleep x = list(map(lambda x: x / 10, range(-100, 100))) x_next_max = 100 y = np.sin(x) # You probably won't need this if you're embedding things in a tkinter plot... plt.ion() fig = plt.figure() ax = fig.add_subplot(111) line1 = ax.plot(x, y, 'r-')[0] # Plot returns a tuple of line objects growth = 0 new_x = None dT = 1 def grow(): global new_x, x_next_max while True: new_x = x + [x_next_max / 10] x_next_max += 1 sleep(dT) # grow every dT seconds t = Thread(target=grow) t.start() while True: if new_x: x = new_x new_x = None line1.set_xdata(x) line1.set_ydata(np.sin(x) + np.sin(np.divide(x, 100)) + np.divide(x, 100)) ax.relim() ax.autoscale() fig.canvas.draw() fig.canvas.flush_events() sleep(0.1)
グラフは、成長スレッドが new_x に値を割り当てた場合にのみ更新されます。 flash_events() 呼び出しは「if」ステートメントの外側にあるため、頻繁に呼び出されていることに注意してください。
以上がpyplotによるリアルタイムプロットの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。