Loading [MathJax]/extensions/tex2jax.js

[matplotlib animation] 66. 3次元ランダムウォークアニメーション

matplotlib Animation

はじめに

matplotlibのFuncAnimationで3次元のランダムウォークアニメーションを表示する。

コード

#3d
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from IPython.display import HTML
from mpl_toolkits.mplot3d import Axes3D
theta=np.pi*np.random.random(499)
phi = 2.0 * np.pi*np.random.random(499)
x = np.sin(theta)*np.cos(phi)
y = np.cos(theta)*np.sin(phi)
z = np.cos(theta)
xyz = np.vstack((x,y,z)).T
norm_xyz = np.linalg.norm(xyz,axis=1)
xyz = xyz/norm_xyz[:,np.newaxis]
xyz = np.vstack(([[0,0,0]],xyz))
position_x = np.cumsum(xyz[:,0]) #累積和 x
position_y = np.cumsum(xyz[:,1]) #累積和 y
position_z = np.cumsum(xyz[:,2]) #累積和 z
fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
ax.set_box_aspect((1,1,1))
ax.plot(position_x,position_y, position_z, 'C6o-', alpha=0.5)
plt.show()
xlim = ax.get_xlim()
ylim = ax.get_ylim()
zlim = ax.get_zlim()
fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
p1, = ax.plot([], [], [],'o',color='C6', alpha=0.25)
ax.set_box_aspect((1,1,1))
ax.set(xlim=xlim,ylim=ylim,zlim=zlim)
def update(i):
p1.set_data((position_x[:i],position_y[:i]))
p1.set_3d_properties(position_z[:i])
return fig,
ani = animation.FuncAnimation(fig, update, 500,interval=20, blit=True)
ani.save('rw3d_anim.mp4', writer="ffmpeg",dpi=100)
HTML(ani.to_html5_video())

解説

モジュールのインポート

#3d
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
from IPython.display import HTML
from mpl_toolkits.mplot3d import Axes3D

3Dグラフなので from mpl_toolkits.mplot3d import Axes3D とする。

バージョン

#version
import matplotlib
print(matplotlib.__version__)
3.3.3
print(np.__version__)
1.19.5

データの生成

theta=np.pi*np.random.random(499)
phi = 2.0 * np.pi*np.random.random(499)
x = np.sin(theta)*np.cos(phi)
y = np.cos(theta)*np.sin(phi)
z = np.cos(theta)
xyz = np.vstack((x,y,z)).T
norm_xyz = np.linalg.norm(xyz,axis=1)
xyz = xyz/norm_xyz[:,np.newaxis]
xyz = np.vstack(([[0,0,0]],xyz))
position_x = np.cumsum(xyz[:,0]) #累積和 x
position_y = np.cumsum(xyz[:,1]) #累積和 y
position_z = np.cumsum(xyz[:,2]) #累積和 z

3次元のx,y,zデータを極座標系で媒介変数によって生成する。このデータ生成方法は下記でも用いた。

[matplotlib 3D] 18. 3D surfaceプロットで球を表示
matplotlibのmplot3dをつかって、3Dグラフ上にsurface plotで球を表示する方法について解説する。

x,y,zをvstackでひとまとめにし、.Tによって転置する。
np.linalg.norm()でaxis=1とすることで各行における要素のノルムを求める。
求めたノルムで各要素をわることで各行の要素を規格化する。
np.vstack(([[0,0,0]],xyz))によりスタート地点を[0,0,0]とした。
positionは下記の1Dランダムウォーク、2Dランダムウォークの例と同様にnp.cumsum()により、累積和とすることで順次変化するランダムウォークの位置を取得した。

[matplotlib animation] 64. 1次元ランダムウォークアニメーション
matplotlibのFuncAnimationで1次元のランダムウォークアニメーションを表示する。
[matplotlib animation] 65. 2次元ランダムウォークアニメーション
matplotlibのFuncAnimationで2次元のランダムウォークアニメーションを表示する。

3次元のランダムウォークデータの表示

fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
ax.set_box_aspect((1,1,1))
ax.plot(position_x,position_y, position_z, 'C6o-', alpha=0.5)
plt.show()

上記で作成したデータを表示すると以下のようになる。

xlim = ax.get_xlim()
ylim = ax.get_ylim()
zlim = ax.get_zlim()

上の図のx軸、y軸、z軸の表示範囲をax.get_xlim(), ax.get_ylim(), ax.get_zlim()で取得する。

アニメーションの表示

fig = plt.figure(figsize=(6,6))
ax = fig.gca(projection='3d')
p1, = ax.plot([], [], [],'o',color='C6', alpha=0.25)
ax.set_box_aspect((1,1,1))
ax.set(xlim=xlim,ylim=ylim,zlim=zlim)
def update(i):
p1.set_data((position_x[:i],position_y[:i]))
p1.set_3d_properties(position_z[:i])
return fig,
ani = animation.FuncAnimation(fig, update, 500,interval=20, blit=True)
ani.save('rw3d_anim.mp4', writer="ffmpeg",dpi=100)
HTML(ani.to_html5_video())

p1,で空のプロットを作成して、データを流し込んでいく。
x,yデータのセットには、set_dataを用いる。zデータのセットには、set_3d_propertiesを用いる。
x軸, y軸、z軸の表示範囲は、ax.set(xlim=xlim, ylim=ylim, zlim=zlim)のようにして先程取得したものを用いる。

ani = animation.FuncAnimationとし、このaniをHTML(ani.to_html5_video())とすることでjupyter lab(notebookも)上でアニメーションを表示できる。

コードをダウンロード(.pyファイル)

コードをダウンロード(.ipynbファイル)

参考

Animated 3D random walk — Matplotlib 3.1.2 documentation
numpy.random.choice — NumPy v1.15 Manual
numpy.cumsum — NumPy v2.2 Manual

コメント