はじめに
Matplotlibライブラリを利用して、矢印プロットを作成します。
【前の内容】
【目次】
quiver関数の矢印サイズの設定:2次元の場合
Matplotlibライブラリのquiver関数で矢印を描画できます。今回は、2次元の場合の矢のサイズ・形状に関する引数を確認します。次回は、矢の色に関する引数を確認します。
利用するライブラリを読み込みます。
# 利用ライブラリ import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation
矢印の作図
まずは、2次元の場合の矢印の座標・長さに関する引数を簡単に確認します。
詳しくは「矢印プロットの作図【ゼロつく1のノート(Python)】 - からっぽのしょこ」を参照してください。
座標と移動量を指定して、矢印を描画します。
# 座標・移動量を指定 x, y = 0.0, 1.0 u, v = 3.0, 2.0 # 矢幅・矢頭幅・矢頭長・矢軸長を指定 w = 3.4 hw = 3.0 hl = 5.0 hal = 4.5 # 2Dベクトルを作図 fig, ax = plt.subplots(figsize=(6, 5), facecolor='white') ax.scatter(x, y, s=100, label='(X, Y)') # 始点 ax.scatter(x+u, y+v, s=100, label='(X+U, Y+V)') # 終点 ax.quiver(x, y, u, v, units='dots', width=w, headwidth=hw, headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1, label='({: .2f}, {: .2f}, {: .2f}, {: .2f})'.format(x, y, u, v)) # 矢印 ax.set_xticks(ticks=np.arange(np.floor(x-abs(u))-1, np.ceil(x+abs(u))+2)) ax.set_yticks(ticks=np.arange(np.floor(y-abs(v))-1, np.ceil(y+abs(v))+2)) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headlength={}, headaxislength={}'.format(w, hw, hl, hal), loc='left', fontsize=10) fig.suptitle('pyplot.quiver(X, Y, U, V)', fontsize=15) ax.grid() ax.legend() ax.set_aspect('equal') plt.show()
2次元ベクトルの場合は、第1・2引数に始点の座標 X, Y
、第3・4引数に移動量 U, V
を指定します。終点の座標は X + U, Y + V
になります。
angles='xy'
、scale_units='xy'
、scale=1
を指定すると、指定した座標の通りに描画されます。デフォルトでは矢印の長さ(終点の座標)が自動調整されます。
移動量の値(終点の座標)が変化するアニメーションで確認します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 60 # ベクトルのノルムを指定 r = 1.0 # 移動量の計算用のラジアンを作成 ts = np.linspace(start=0.0, stop=2.0*np.pi, num=n+1)[:n] # 移動量を作成 us = r * np.cos(ts) vs = r * np.sin(ts) # 矢幅・矢頭幅・矢頭長・矢軸長を指定 w = 3.4 hw = 3.0 hl = 5.0 hal = 4.5 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 6), facecolor='white') fig.suptitle('pyplot.quiver(X, Y, U, V)', fontsize=15) # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.scatter(0, 0, label='(X, Y)') # 始点 ax.scatter(us[range(i-n+1, i+1)][::-1], vs[range(i-n+1, i+1)][::-1], alpha=np.arange(n)[::-1]/n, label='(X+U, Y+V)') # 終点の軌道 ax.quiver(0, 0, us[i], vs[i], units='dots', width=w, headwidth=hw, headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1, label='(0.00, 0.00, {: .2f}, {: .2f})'.format(us[i], vs[i])) # 矢印 ax.set_xticks(ticks=np.arange(np.floor(-r)-1, np.ceil(r)+2)) ax.set_yticks(ticks=np.arange(np.floor(-r)-1, np.ceil(r)+2)) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headlength={}, headaxislength={}'.format(w, hw, hl, hal), loc='left', fontsize=10) ax.grid() ax.legend(loc='upper left') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_uv.gif')
作図処理をupdate()
として定義して、FuncAnimation()
でgif画像を作成します。
フレームごとに移動量を変化させることで、終点の座標が変化します。
この例では、円形の軌道をとるように変化します。
始点と終点の座標を変更して確認します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 60 # 座標・移動量の計算用の値を指定 r = 1.0 ts = np.linspace(start=0.0, stop=2.0*np.pi, num=n+1)[:n] rs = np.sin(ts)**2 * 2.0 # 座標・移動量を指定 xs = r * np.cos(ts) ys = r * np.sin(ts) us = rs * np.cos(2 * ts) vs = rs * np.sin(2 * ts) # 矢幅・矢頭幅・矢頭長・矢軸長を指定 w = 3.4 hw = 3.0 hl = 5.0 hal = 4.5 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 6), facecolor='white') fig.suptitle('pyplot.quiver(X, Y, U, V)', fontsize=15) # グラフサイズ用の値を設定 x_min = np.floor(min(xs+us)) - 1 x_max = np.ceil(max(xs+us)) + 1 y_min = np.floor(min(ys+vs)) - 1 y_max = np.ceil(max(ys+vs)) + 1 # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.scatter(xs[range(i-n+1, i+1)][::-1], ys[range(i-n+1, i+1)][::-1], alpha=np.arange(n)[::-1]/n) # 始点の軌道 ax.scatter((xs+us)[range(i-n+1, i+1)][::-1], (ys+vs)[range(i-n+1, i+1)][::-1], alpha=np.arange(n)[::-1]/n) # 終点の軌道 ax.quiver(xs[i], ys[i], us[i], vs[i], units='dots', width=w, headwidth=hw, headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1, label='({: .2f}, {: .2f}, {: .2f}, {: .2f})'.format(xs[i], ys[i], us[i], vs[i])) # 矢印 ax.set_xticks(ticks=np.arange(x_min, x_max+1)) ax.set_yticks(ticks=np.arange(y_min, y_max+1)) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headlength={}, headaxislength={}'.format(w, hw, hl, hal), loc='left', fontsize=10) ax.grid() ax.legend(loc='upper left') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_xyuv.gif')
フレームごとに始点の座標と移動量(終点の座標)を変化させることで、ベクトルの長さ(ノルム)が変化します。
この例では、始点が円形の軌道を1周する変化をする間に、移動量が2周する変化をします。
形状に関する引数
次からは、矢印の形状(サイズ)に関する引数を確認していきます。3次元の場合とは挙動が異なります。
width引数
width
引数で矢の幅を設定できます。
引数の値を変更した矢印を並べて描画します。引数に複数の値を指定できないので、for
文を使って1つずつ設定を変えて描画します。
・作図コード(クリックで展開)
# 矢印の数を指定 n = 21 # 矢幅の範囲を指定 ws = np.linspace(start=0, stop=10, num=n) # 矢頭幅・矢頭長・矢軸長を指定 hw = 3.0 hl = 5.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # 2Dベクトルを作図 fig, ax = plt.subplots(figsize=(6, 8), facecolor='white') for i in range(n): ax.quiver(0, i, u, 0, units='dots', width=ws[i], headwidth=hw, headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, i, s='width='+str(ws[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=np.arange(n)) ax.set_xlim(-6, np.ceil(u)+1) ax.set_ylim(-1, n) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('headwidth={}, headlength={}, headaxislength={}'.format(hw, hl, hal), loc='left', fontsize=10) fig.suptitle('pyplot.quiver(width)', fontsize=15) ax.grid(axis='x') ax.set_aspect('equal') plt.show()
width
引数は、矢全体の幅に影響します。
0
だと矢が表示されません(警告文が出ます)。負の値も指定できますが矢の形が崩れます。
引数の値を変更した矢印のアニメーションを作成します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 101 # 矢幅の範囲を指定 ws = np.linspace(start=0, stop=10, num=n) # 矢頭幅・矢頭長・矢軸長を指定 hw = 3.0 hl = 5.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 3), facecolor='white') fig.suptitle('pyplot.quiver(width)', fontsize=15) # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.quiver(0, 0, u, 0, units='dots', width=ws[i], headwidth=hw, headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, 0, s='width={:.1f}'.format(ws[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=[0]) ax.set_xlim(-3, np.ceil(u)+1) ax.set_ylim(-1, 1) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('headwidth={}, headlength={}, headaxislength={}'.format(hw, hl, hal), loc='left', fontsize=10) ax.grid(axis='x') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_w.gif')
矢全体の長さは、移動量(など)の引数によって決まります。linewidth
引数は、矢の枠線の幅です。
以降の引数は、width
引数の値の影響を相対的に受けます。
headwidth引数
headwidth
引数で矢頭の幅を設定できます。
引数の値を変更した矢印を並べて描画します。
・作図コード(クリックで展開)
# 矢印の数を指定 n = 21 # 矢頭幅の範囲を指定 hws = np.linspace(start=0, stop=5, num=n) # 矢幅・矢頭長・矢軸長を指定 w = 3.4 hl = 5.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # 2Dベクトルを作図 fig, ax = plt.subplots(figsize=(6, 8), facecolor='white') for i in range(n): ax.quiver(0, i, u, 0, units='dots', width=w, headwidth=hws[i], headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, i, s='headwidth='+str(hws[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=np.arange(n)) ax.set_xlim(-6, np.ceil(u)+1) ax.set_ylim(-1, n) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headlength={}, headaxislength={}'.format(w, hl, hal), loc='left', fontsize=10) fig.suptitle('pyplot.quiver(headwidth)', fontsize=15) ax.grid(axis='x') ax.set_aspect('equal') plt.show()
headwidth
引数は、矢頭の幅が一番広い部分のサイズです。
headwidth=1
のとき矢尻と同じ幅になります。1
未満だと矢の形が崩れます。
引数の値を変更した矢印のアニメーションを作成します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 101 # 矢頭幅の範囲を指定 hws = np.linspace(start=0, stop=10, num=n) # 矢幅・矢頭長・矢軸長を指定 w = 3.4 hl = 5.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 3), facecolor='white') fig.suptitle('pyplot.quiver(headwidth)', fontsize=15) # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.quiver(0, 0, u, 0, units='dots', width=w, headwidth=hws[i], headlength=hl, headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, 0, s='headwidth={:.1f}'.format(hws[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=[0]) ax.set_xlim(-3, np.ceil(u)+1) ax.set_ylim(-1, 1) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headlength={}, headaxislength={}'.format(w, hl, hal), loc='left', fontsize=10) ax.grid(axis='x') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_hw.gif')
headlength引数
headlength
引数で矢頭の長さを設定できます。
引数の値を変更した矢印を並べて描画します。
・作図コード(クリックで展開)
# 矢印の数を指定 n = 21 # 矢頭長の範囲を指定 hls = np.linspace(start=0, stop=10, num=n) # 矢幅・矢頭幅・矢軸長を指定 w = 3.4 hw = 3.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # 2Dベクトルを作図 fig, ax = plt.subplots(figsize=(6, 8), facecolor='white') for i in range(n): ax.quiver(0, i, u, 0, units='dots', width=w, headwidth=hw, headlength=hls[i], headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, i, s='headlength='+str(hls[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=np.arange(n)) ax.set_xlim(-6, np.ceil(u)+1) ax.set_ylim(-1, n) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headaxislength={}'.format(w, hw, hal), loc='left', fontsize=10) fig.suptitle('pyplot.quiver(headlength)', fontsize=15) ax.grid(axis='x') ax.set_aspect('equal') plt.show()
headlength
引数は、「矢の先端」から「矢頭の幅が一番広い部分」までのサイズです。
headlength
が headaxislength
と同じだと矢頭が三角形、headaxislength
の半分だとひし形、0
だと逆三角形になります。0
未満だと矢の形が崩れます。
引数の値を変更した矢印のアニメーションを作成します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 101 # 矢頭長の範囲を指定 hls = np.linspace(start=0, stop=10, num=n) # 矢幅・矢頭幅・矢軸長を指定 w = 3.4 hw = 3.0 hal = 4.5 # x軸方向の移動量を指定 u = 3.0 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 3), facecolor='white') fig.suptitle('pyplot.quiver(headlength)', fontsize=15) # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.quiver(0, 0, u, 0, units='dots', width=w, headwidth=hw, headlength=hls[i], headaxislength=hal, angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, 0, s='headlength={:.1f}'.format(hls[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=[0]) ax.set_xlim(-3, np.ceil(u)+1) ax.set_ylim(-1, 1) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headaxislength={}'.format(w, hw, hal), loc='left', fontsize=10) ax.grid(axis='x') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_hl.gif')
headaxislength引数
headaxislength
引数で矢軸の長さを設定できます。
引数の値を変更した矢印を並べて描画します。
・作図コード(クリックで展開)
# 矢印の数を指定 n = 21 # 矢軸長の範囲を指定 hals = np.linspace(start=0, stop=10, num=n) # 矢幅・矢頭幅・矢頭長を指定 w = 3.4 hw = 3.0 hl = 5.0 # x軸方向の移動量を指定 u = 3.0 # 2Dベクトルを作図 fig, ax = plt.subplots(figsize=(6, 8), facecolor='white') for i in range(n): ax.quiver(0, i, u, 0, units='dots', width=w, headwidth=hw, headlength=hl, headaxislength=hals[i], angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, i, s='headaxislength='+str(hals[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=np.arange(n)) ax.set_xlim(-6, np.ceil(u)+1) ax.set_ylim(-1, n) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headlength={}'.format(w, hw, hl), loc='left', fontsize=10) fig.suptitle('pyplot.quiver(headaxislength)', fontsize=15) ax.grid(axis='x') ax.set_aspect('equal') plt.show()
headaxislength
引数は、「矢の先端」から「矢頭の付け根」までのサイズです。
headaxislength
が headlength
と同じだと矢頭が三角形、headlength
の倍だとひし形になります。
引数の値を変更した矢印のアニメーションを作成します。
・作図コード(クリックで展開)
# フレーム数を指定 n = 101 # 矢軸長の範囲を指定 hals = np.linspace(start=0, stop=10, num=n) # 矢幅・矢頭幅・矢頭長を指定 w = 3.4 hw = 3.0 hl = 5.0 # x軸方向の移動量を指定 u = 3.0 # グラフオブジェクトを初期化 fig, ax = plt.subplots(figsize=(6, 3), facecolor='white') fig.suptitle('pyplot.quiver(headaxislength)', fontsize=15) # 作図処理を関数として定義 def update(i): # 前フレームのグラフを初期化 plt.cla() # 2Dベクトルを作図 ax.quiver(0, 0, u, 0, units='dots', width=w, headwidth=hw, headlength=hl, headaxislength=hals[i], angles='xy', scale_units='xy', scale=1) # 矢印 ax.text(0, 0, s='headaxislength={:.1f}'.format(hals[i])+' ', size=10, ha='right', va='center') # 引数ラベル ax.set_xticks(ticks=np.arange(u+1)) ax.set_yticks(ticks=[0]) ax.set_xlim(-3, np.ceil(u)+1) ax.set_ylim(-1, 1) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('width={}, headwidth={}, headlength={}'.format(w, hw, hl), loc='left', fontsize=10) ax.grid(axis='x') ax.set_aspect('equal') # gif画像を作成 ani = FuncAnimation(fig=fig, func=update, frames=n, interval=100) # gif画像を保存 ani.save(filename='2d_quiver_hal.gif')
線分の作図
最後は、矢頭の形状を調整して線分を描画します。
「矢印の作図」のときのコードで作図します。
( headwidth
引数と) headlength
引数と headaxislength
引数を 0
にすると、矢頭が存在しなくなるので、線分を描画できます。
同様の図をplot関数を使って作成します。
# 始点・終点の座標を指定 x, y = 0.0, 1.0 u, v = 3.0, 2.0 # 線の太さを指定 lw = 3.4 # 2D線分を作図 fig, ax = plt.subplots(figsize=(6, 5), facecolor='white') ax.scatter(x, y, s=100, label='(X, Y)') # 始点 ax.scatter(x+u, y+v, s=100, label='(X+U, Y+V)') # 終点 ax.plot([x, x+u], [y, y+v], color='black', linewidth=lw, label='([{: .2f}, {: .2f}], [{: .2f}, {: .2f}])'.format(x, y, u, v)) # 線分 ax.set_xticks(ticks=np.arange(np.floor(x-abs(u))-1, np.ceil(x+abs(u))+2)) ax.set_yticks(ticks=np.arange(np.floor(y-abs(v))-1, np.ceil(y+abs(v))+2)) ax.set_xlabel('x') ax.set_ylabel('y') ax.set_title('linewidth='+str(lw), loc='left', fontsize=10) fig.suptitle('pyplot.plot([X, X+U], [Y, Y+V])', fontsize=15) ax.grid() ax.legend() ax.set_aspect('equal') plt.show()
第1引数に始点と終点のx軸座標、第2引数にy軸座標を指定します。
この記事では、矢印のサイズ・形状に関する引数を確認しました。次の記事では、色に関する引数を確認します。
参考リンク
おわりに
ずっと雰囲気で使えてしまってたのですが、このままテキトーなコードを書いてたらマズい気がしてきたので確認してみました。やっぱり使い方を間違ってたっぽいです。あー書き直さないといけない。
【次の内容】