前回、「Matplotlib」のちょっと違った使い方を紹介って言ってたけど、どんな使い方なの?
今まで紹介していたMatplotlibって、基本的には既に用意されたデータを扱ってグラフを表示する使い方だったでしょ?
今回は、マイクの音をリアルタイムで取得してその情報を表示したいから、「グラフのアニメーション」を表示する方法になるんだ
「グラフのアニメーション」...?
まぁ簡単にいえば、常にグラフの情報を書き換え続けるって感じだね
「Pyxel」でホッケーを作った時みたいに、「値の更新」と「描画」を1秒間に何回も行う時の感覚に近いかな?
なるほど、そんなことがMatplotlibでもできるんだね!
それで、この更新を続けるために使うのが「matplotlib.animation」
ちゃんとリアルタイムで更新するための機能もあるんだね!
じゃあまずは試しに、「sin波」がずっと流れ続けるっていうアニメーションを作って解説していこう
下にあるサンプルのプログラムですぐに動くと思うから、コピー&ペーストで試してみてね!
オッケー!
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import japanize_matplotlib
class SinWaveAnimator:
"""sin波を少しずつずらしながら描き直すクラス"""
def __init__(self, ax):
self.x = np.linspace(0, 4 * np.pi, 200)
self.phase = 0 # 波をずらすための値。selfに持たせることで状態を保持する
# 最初に1回だけ線を描いておき、以降はこの self.line を描き直していく
self.line, = ax.plot(self.x, np.sin(self.x))
def update(self, frame):
"""
matplotlibが一定間隔ごとに自動で呼び出すメソッド。
frame: 何回目の呼び出しかを表す番号(今回は使わない)
"""
self.phase += 0.1 # 呼ばれるたびに波を少しずつずらす
self.line.set_ydata(np.sin(self.x + self.phase))
return self.line,
"""1.描画するグラフの土台を準備"""
fig, ax = plt.subplots()
ax.set_ylim(-1.5, 1.5)
ax.set_title("sin波が流れるアニメーション")
"""2.sin波を描画するためのクラスを扱うための準備"""
animator = SinWaveAnimator(ax)
"""4.アニメーションを適用"""
# animator.update を渡すことで、更新する度に同じ animator の self.phase が更新され続ける
ani = animation.FuncAnimation(fig, animator.update, interval=50)
"""5.グラフを表示"""
plt.show()
おおー!グラフが波打って動いてる...!!
このプログラムのポイントは、37行目にある「animation.FuncAnimation()」
1つ目の引数は、事前に用意したグラフの土台部分(fig)
2つ目の引数は、更新のたびに動かしたい関数(animator.update)
3つ目の引数は、どれくらいの頻度(ミリ秒)で更新するか(interval=50)
を指定することで、自分でループを書かなくてもウィンドウを閉じない限りはずっと動き続けてくれるよ
ani = animation.FuncAnimation(fig, animator.update, interval=50)
あれ?2つ目の引数で関数を指定してるのに、後ろに「()」が要らないんだね
あくまで、更新の度に動かしたい関数を「指定」してるだけだからね
逆に関数に「()」が付くと「その場で実行する」って意味になる、と認識しておくと良いと思うよ
そうなんだ!りょーかい!!
「SinWaveAnimator」については、sin波を更新の度に「0.1」ずつズラせるように変数「self.phase」(12, 22行目)を用意してるって感じ
「self.x」はNumPyの配列になってるから、「self.phase」の値を足し算すると、各要素にその数値がまとめて足し合わされるよ
class SinWaveAnimator:
def __init__(self, ax):
self.x = np.linspace(0, 4 * np.pi, 200)
self.phase = 0 # 波をずらすための値
self.line, = ax.plot(self.x, np.sin(self.x))
def update(self, frame):
self.phase += 0.1
self.line.set_ydata(np.sin(self.x + self.phase))
return self.line,
NumPyの配列に四則演算をすると、全ての要素にまとめて適用されるんだったね!
あとは、「self.line」(15行目)がグラフの描画を管理している変数になっているから、その中を「.set_ydata()」(23行目)で更新しているって流れだね
# 最初に1回だけ線を描いておき、以降はこの self.line を描き直していく
self.line, = ax.plot(self.x, np.sin(self.x))
# 更新のたびに y の値だけを差し替える
self.line.set_ydata(np.sin(self.x + self.phase))
ほぅほぅ
まぁ今回はあくまで「グラフのアニメーション」のデモだから、「self.line」の処理部分の流れをしっかりと覚えてくれたらOK
重要なのは、グラフを描画する部分を変数で管理(15行目)して、それを基準に更新できるようにする(23行目)ってことなんだね!
そういうこと
つまり、Matplotlib側でのアニメーションの手順は
1. グラフの土台を用意する(28行目)
2. アニメーションを描画するオブジェクトを用意する(33行目)
3. 更新する度に処理をする関数を定義する(17行目〜24行目)
4. アニメーションを適用する(37行目)
5. グラフを表示する(40行目)
って流れになるよ
fig, ax = plt.subplots()
ax.set_ylim(-1.5, 1.5)
ax.set_title("sin波が流れるアニメーション")
animator = SinWaveAnimator(ax)
ani = animation.FuncAnimation(fig, animator.update, interval=50)
plt.show()
更新の度に動かす関数さえちゃんと用意できれば、あとはほぼテンプレって感じなんだね!
そしたら次回は、「sounddevice」の処理について説明していくよ!
