からっぽのしょこ

読んだら書く!書いたら読む!同じ事は二度調べ(たく)ない

quiver関数の矢印サイズの設定:2次元の場合【Matplotlib】

はじめに

 Matplotlibライブラリを利用して、矢印プロットを作成します。

【前の内容】

www.anarchive-beta.com

【目次】

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()

quiver関数の座標と移動量の引数

 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')

quiver関数の移動量の引数

 作図処理を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')

quiver関数の座標と移動量の引数

 フレームごとに始点の座標と移動量(終点の座標)を変化させることで、ベクトルの長さ(ノルム)が変化します。
 この例では、始点が円形の軌道を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()

quiver関数の矢の幅の引数

 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')

quiver関数の矢の幅の引数

 矢全体の長さは、移動量(など)の引数によって決まります。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()

quiver関数の矢頭の幅の引数

 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')

quiver関数の矢頭の幅の引数


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()

quiver関数の矢頭の長さの引数

 headlength 引数は、「矢の先端」から「矢頭の幅が一番広い部分」までのサイズです。
 headlengthheadaxislength と同じだと矢頭が三角形、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')

quiver関数の矢頭の長さの引数


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()

quiver関数の矢軸の長さの引数

 headaxislength 引数は、「矢の先端」から「矢頭の付け根」までのサイズです。
 headaxislengthheadlength と同じだと矢頭が三角形、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')

quiver関数の矢軸の長さの引数


線分の作図

 最後は、矢頭の形状を調整して線分を描画します。

 「矢印の作図」のときのコードで作図します。

quiver関数による線分のグラフ

 ( 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()

plot関数による線分のグラフ

 第1引数に始点と終点のx軸座標、第2引数にy軸座標を指定します。

 この記事では、矢印のサイズ・形状に関する引数を確認しました。次の記事では、色に関する引数を確認します。

参考リンク

おわりに

 ずっと雰囲気で使えてしまってたのですが、このままテキトーなコードを書いてたらマズい気がしてきたので確認してみました。やっぱり使い方を間違ってたっぽいです。あー書き直さないといけない。

【次の内容】

www.anarchive-beta.com