MatplotlibとPIL
前回、Pandasで行・列のデータをランダムで取得する方法を勉強しました。
今回から何回かはMatplotlibとPILを使って、GIFアニメーショングラフとして動くグラフを作成していきます。
第一回目は線グラフが伸びていくグラフを作成してみましょう。
動かないグラフの作成
まずは最終的にこんな感じのグラフというのが分かるように、動かないグラフを作成します。
ということでこんな感じ。
import matplotlib.pyplot as plt
import numpy as np
x_list = []; y_list =[]
for i in np.arange(0, 10, 0.1):
x_list.append(i)
y_list.append(2**i)
fig = plt.figure()
plt.clf()
plt.plot(x_list, y_list)
plt.show()
実行結果
xの値は1から10まで、yの値は20から210のまでのグラフです。
今回のアニメーションではグラフがx軸の0から10の方向にどんどん伸びていくグラフを作成してみます。
つまりこんな感じです。
グラフを書き出すプログラム
先ほどのGIFアニメーションを作成するにはまずはパラパラ漫画のようにそれぞれの画像を作成する必要があります。
つまりグラフがxの値が0から10まで順に増えていくグラフです。
import matplotlib.pyplot as plt
import numpy as np
import os
x_list = []; y_list =[]
for i in np.arange(0, 10, 0.1):
x_list.append(i)
y_list.append(2**i)
fig_path = './fig'
for i in range(len(x_list)):
fig = plt.figure(facecolor='white')
plt.clf()
plt.plot(x_list[:i], y_list[:i])
plt.xlim(0,10)
plt.ylim(0, 1024)
fig_name = f'{str(i).zfill(4)}.png'
fig_output = os.path.join(fig_path, fig_name)
plt.savefig(fig_output)
plt.close()
このプログラムではxの値の数だけfor文により処理を繰り返しています。
for i in range(len(x_list)):
fig = plt.figure(facecolor='white')
plt.clf()
plt.plot(x_list[:i], y_list[:i])
plt.xlim(0,10)
plt.ylim(0, 1024)
fig_name = f'{str(i).zfill(4)}.png'
fig_output = os.path.join(fig_path, fig_name)
plt.savefig(fig_output)
plt.close()
そして1回の処理ではそのxの値までのグラフを描写しています「plt.plot(x_list[:i], y_list[:i])」。
ここからGIFアニメーション用にグラフを書き出す時の注意点を解説していきます。
plt.close()を忘れない
まずはGIFアニメーション自体には影響しませんが、処理のパフォーマンスに影響するだろう点です。
それはプログラムの最後の行の「plt.close()」を忘れないということです。
これを忘れると1度描いたグラフがそのままメモリ上に残り、どんどんメモリを圧迫してしまう恐れがあります。
そしてこんな感じで警告が出てきます。
/var/folders/sp/hg7p80kx22s7vct7yb0zl5cm0000gn/T/ipykernel_29961/3487805850.py:13:
RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot
interface (`matplotlib.pyplot.figure`) are retained until explicitly closed and may consume
too much memory. (To control this warning, see the rcParam `figure.max_open_warning`).
fig = plt.figure(facecolor='white')
GIFアニメーションでは大量の画像を使用するため、効率的に処理させるためにも、メモリのことを気にするのがいいでしょう。
plt.figure(facecolor=’white’)で背景色を指定
実はプログラム上ここが一番苦戦したところでした。
maplotlibでグラフエリアを確保する「plt.figure()」ですが、普段ここはデフォルトで使っている(オプションを設定していない)ことも多いのではないでしょうか。
今回の場合、何も指定せずにGIFアニメーションを作成するとこうなります。
グラフの動きは先ほどと同じなのですが、軸の値が潰れてしまっています。
これを解消するのにかなり苦労したのですが、問題は背景色にありました。
背景色は「plt.figure(facecolor=’背景色’)」で設定できるのですが、「white」(白)を指定した場合と指定していない場合の一枚のグラフを見てみましょう。
Macの「プレビュー」で画像を開き、それをスクリーンショットしています。
plt.figure(facecolor=’white’)なし
plt.figure(facecolor=’white’)あり
「plt.figure(facecolor=’white’)なし」の方は軸の値の部分が灰色になっています。
これは実際灰色となっているわけではなくて、「透明化」されている状態です。
この透明化された部分はPILではアニメーションにならずに残ってしまうようで、そのため先ほどお見せしたGIFアニメーションのように軸の値が潰れてしまっているみたいです。
軸の値の範囲の指定
もう一つは軸の値の範囲を指定しておくことです。
プログラム上はこの部分です。
plt.xlim(0,10)
plt.ylim(0, 1024)
この値の範囲の指定をしておかないとこのようなGIFアニメーションになります。
これはこれでなかなか良いのですが、値がどんどん変わっていくため、他人にグラフの説明するために使用するにはちょっと理解が難しいグラフになってしまいます。
これを防ぐには出力される画像全てに同じ軸の値が表記されるようにする、つまり「xlim」と「ylim」を指定しておくというわけです。
ファイル名を連番にする
最後にファイル名を連番にすることです。
ファイル名が連番になっていないと、GIFアニメーションを作る際、画像の順番が入れ替わり、意図したアニメーションにならないことがあります。
また特に気をつけるのが、「1.png … 10.png … 20.png …」のように連番をつけている場合です。
この場合は「1.png, 10.png, 11.png … 2.png, 20.png, 21.png … 3.png, 30.png, 31.png …」のようになってしまい、順番が狂うことがあります。
この場合は数値をゼロ埋めすることで問題は解消します。
ゼロ埋め(ゼロパディング)に関してはこちらの記事で解説していますので、よかったらどうぞ。
GIFアニメーションを作るプログラム
そして思い通りのグラフの画像を手に入れたら、GIFアニメーションにしていきます。
画像からGIFアニメーションにする方法はこちらの記事で解説していますので、よかったらどうぞ。
プログラム自体はこんな感じです。
from PIL import Image
import os
fig_path = './fig'
im_list = []
for file in sorted(os.listdir(fig_path)):
file_path = os.path.join(fig_path, file)
im = Image.open(file_path)
im_list.append(im)
im_list[0].save("animation.gif", save_all=True, append_images=im_list[1:], duration=1, loop=0)
一枚一枚PILのイメージとして画像を読み込み、リストに入れ、それをアニメーションGIFとして保存するという流れです。
詳しくは先ほど紹介した記事をご覧ください。
結構、プレゼンテーションなど他人にグラフを説明する際、アニメーションになっていると説明しやすい場面もあると思いますので、そんなときにこのmatplotlibとPILでグラフのGIFアニメーションを作成する方法を使ってもらえたらなと思います。
次回は今回と同じくGIFアニメーションのグラフを作成する方法ですが、xの値の範囲が一定なGIFアニメーショングラフを作成する方法を解説していきます。
ではでは今回はこんな感じで。
コメント