【python】quiverの矢印の長さをうまく調整したい【matplotlib.pyplot.quiver】

以前の記事でquiverを使って矢印をグラフに描く方法を紹介したのだが、長さがうまく調整できずに困っているという問題があった。

(x,y)=(0,0)から(3,1)に矢印を描きたい。まず普通に書いてみるとこんな感じになる。

%matplotlib notebook
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111)

ax.quiver(0, 0, 3, 1, units='xy',angles='uv',scale=1, color="Red")

ax.grid(axis='x', color='black', lw=0.5)
ax.grid(axis='y', color='black', lw=0.5)
ax.set_xlim(0,5)
ax.set_ylim(0,5)

 f:id:Chemstat:20201024080750p:plain

 

うまく(3,1)に矢印が届いている

そしてグラフのサイズだけ変更して同じプロットをしてみる。

%matplotlib notebook
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(4,2))
ax = fig.add_subplot(111)

ax.quiver(0, 0, 3, 1, units='xy',angles='uv',scale=1, color="Red")

ax.grid(axis='x', color='black', lw=0.5)
ax.grid(axis='y', color='black', lw=0.5)
ax.set_xlim(0,5)
ax.set_ylim(0,5)

 f:id:Chemstat:20201024081041p:plain

するとxもyもずれた変な位置に矢印が伸びてしまう。

 

unitsによる矢印の長さの指定と、anglesによる角度の指定をちゃんと理解しないといけないようだ。

plt.quiver(X,Y,U,V,units='xy',angles='uv')  

 quiverの解説

X, Yは始点の座標そのままだが、U, Vはベクトルの角度を指定することになる。

底辺U、高さVの直角三角形を作るように角度が決めている。ここで問題になるのはグラフの軸の長さが反映されず、U,Vの絶対値で決まるため、軸の単位長さがxとyで違っていると、角度がおかしくなってしまう。f:id:Chemstat:20201025200235j:plain

 

 

始点と角度を決めたので、今度は長さを'units'という項目で指定する。

この'units'は単位長さを指定する項目なので、例えばunits='x'だと「x軸の1」を基準として、そのU倍がベクトルの長さになる。その他使いづらいので一旦置いておく。

 

f:id:Chemstat:20201025200050j:plain

 


'angles'は矢印の角度を指定するパラメーターで、'uv'と'xy'がある。'uv'は先ほど説明した通りなので割愛する。

'xy'は説明を見ると「(X,Y)から(X+U, Y+V)に向かって矢印を伸ばす」、と書いているのだが、実際にやると全くそうならない。環境のせいだろうか?どうやっても挙動が理解できなかったので一旦保留しておく。

ちなみに絶対値で角度を指定することも出来る。

f:id:Chemstat:20201026010600j:plain

 

ついでにscaleという単位長さを指定するパラメーターもある。数字が大きくなるとベクトルの長さは小さくなる。

f:id:Chemstat:20201026011019j:plain

 

長さを調節するには

やっとquiverの挙動が分かってきた。角度と長さが別々で指定されちゃうので、軸の長さで補正してやればうまくいきそうな気がする。

get_ax_size()という関数を定義して軸の長さを取得して、それを利用する。

fig.get_size_inches()で図全体の長さを取得することもできるのだが、軸の比率と微妙に違うので少しだけずれてしまう。(ここでめちゃくちゃつまづいた)

 

%matplotlib notebook
import matplotlib.pyplot as plt

fig = plt.figure(figsize=(4,2))
ax = fig.add_subplot(111)

#軸の目盛幅を取得する関数を定義(詳細は参考サイト) def get_ax_size(ax): bbox = ax.get_window_extent().transformed(fig.dpi_scale_trans.inverted()) width, height = bbox.width, bbox.height width *= fig.dpi height *= fig.dpi return width, height #軸の目盛幅の比率を計算 x_lim = (0,5) y_lim = (0,3) lim_ratio = (x_lim[0] - x_lim[1]) / (y_lim[0] - y_lim[1]) ax.set_xlim(x_lim) ax.set_ylim(y_lim) ax.grid(axis='x', color='black', lw=0.5) ax.grid(axis='y', color='black', lw=0.5) #軸の長さの比率を計算 ax_size = get_ax_size(ax) ax_ratio = ax_size[1] / ax_size[0] #units='x'でx軸の長さを基準にする。 #y方向の成分を軸の比率、目盛の比率で補正 ax.quiver(0, 0, 3, 1 * ax_ratio * lim_ratio, units='x',angles='uv',scale=1, color="Black")

 

f:id:Chemstat:20201026013827j:plain




ということで見事に(3,1)に矢印が伸びている。めでたしめでたし。

 

 

参考

matplotlib.pyplot.quiver https://matplotlib.org/3.3.0/api/_as_gen/matplotlib.pyplot.quiver.html

軸の長さの取得:

python - matplotlibの軸サイズをピクセル単位で決定する