からっぽのしょこ

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

傾いた2次元格子の作図【Matplotlib】

はじめに

 Matplotlibライブラリを利用して、傾いた2次元の格子(平行四辺形の平面)を作成します。

【前の内容】

www.anarchive-beta.com

【目次】

傾いた2次元格子の作図

 前回は、Matplotlibライブラリを利用して、2次元空間(平面)上に長方形の格子のグラフを作成しました。
 今回は、平面上に平行四辺形の格子のグラフを作成します。前回の記事も参照してください。

 利用するライブラリを読み込みます。

# 利用ライブラリ
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation


2次元格子の描画

 2次元の格子(平行四辺形の平面)の作図方法を確認します。

 x軸・y軸の値を作成します。(説明に数式を使いますが、特に重要なわけではないので読み飛ばしてください。)

# 点の座標を指定
p = np.array([2.0, 1.0])
q = np.array([-1.0, 1.0])

# 平面用の係数を指定
a = np.arange(start=-2.0, stop=2.1, step=0.5)
b = np.arange(start=-1.5, stop=1.6, step=0.5)
print(a)
print(b)
print(a.shape)
print(b.shape)

# 係数の格子点を作成
A, B = np.meshgrid(a, b)
print(A.shape)
print(B.shape)

# 平面の座標を計算
X = A * p[0] + B * q[0]
Y = A * p[1] + B * q[1]
print(X[:5, :5])
print(Y[:5, :5])
[-2.  -1.5 -1.  -0.5  0.   0.5  1.   1.5  2. ]
[-1.5 -1.  -0.5  0.   0.5  1.   1.5]
(9,)
(7,)
(7, 9)
(7, 9)
[[-2.5 -1.5 -0.5  0.5  1.5]
 [-3.  -2.  -1.   0.   1. ]
 [-3.5 -2.5 -1.5 -0.5  0.5]
 [-4.  -3.  -2.  -1.   0. ]
 [-4.5 -3.5 -2.5 -1.5 -0.5]]
[[-3.5 -3.  -2.5 -2.  -1.5]
 [-3.  -2.5 -2.  -1.5 -1. ]
 [-2.5 -2.  -1.5 -1.  -0.5]
 [-2.  -1.5 -1.  -0.5  0. ]
 [-1.5 -1.  -0.5  0.   0.5]]

 点 \mathbf{p} = (p_0, p_1)・点 \mathbf{q} = (q_0, q_1)の座標(x軸の値 p_0, q_0とy軸の値 p_1, q_1)をp, qとして指定します。Pythonのインデックスに合わせて、添字を0から割り当てています。
 平面の座標計算に使う係数 a, bの値をa, bとして作成して、格子状の点(全ての組み合わせ)をnp.meshgrid()で作成してA, Bとします。
 平面のx軸の値 x = a p_0 + b q_0とy軸の値 y = a p_1 + b q_1を計算して、X, Yとします。

 X, Yを用いて、散布図を作成してプロット位置を確認します。

# グラフサイズ用の値を設定
x_min = np.floor(X.min())
x_max =  np.ceil(X.max())
y_min = np.floor(Y.min())
y_max =  np.ceil(Y.max())

# 2D格子点を作図
fig, ax = plt.subplots(figsize=(8, 6), facecolor='white')
ax.scatter(X, Y) # 格子点
ax.scatter(0, 0, 
           color='orange', s=100, label='O=(0, 0)') # 原点
ax.scatter(p[0], p[1], 
           color='limegreen', s=100, 
           label='p=('+str(p[0])+', '+str(p[1])+')') # 点p
ax.scatter(q[0], q[1], 
           color='burlywood', s=100, 
           label='q=('+str(q[0])+', '+str(q[1])+')') # 点q
ax.set_xticks(ticks=np.arange(x_min, x_max+1))
ax.set_yticks(ticks=np.arange(y_min, y_max+1))
ax.grid()
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.suptitle('Axes.scatter(X, Y)', fontsize=20)
ax.legend()
ax.set_aspect('equal')
plt.show()

傾いた格子点

 対角線方向にそれぞれ等間隔の点が平行四辺形に並びます。傾きは基準となる点p, q、間隔と個数は係数a, bによって決まります。
 原点(オレンジ色の点)からx軸方向にp[0], q[0]、y軸方向にp[1], q[1]移動した点が点p(黄緑色の点)・点q(ベージュ色の点)です。

 X, Yを用いて、直線を描画します。

# 直線を作図
fig, ax = plt.subplots(figsize=(8, 6), facecolor='white')
ax.scatter(X, Y) # 格子点
ax.scatter(0, 0, 
           color='orange', s=100, label='O=(0, 0)') # 原点
ax.scatter(p[0], p[1], 
           color='limegreen', s=100, 
           label='p=('+str(p[0])+', '+str(p[1])+')') # 点p
ax.scatter(q[0], q[1], 
           color='burlywood', s=100, 
           label='q=('+str(q[0])+', '+str(q[1])+')') # 点q
ax.plot(X, Y, color='burlywood') # qに関する直線
ax.set_xticks(ticks=np.arange(x_min, x_max+1))
ax.set_yticks(ticks=np.arange(y_min, y_max+1))
ax.grid()
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.suptitle('Axes.plot(X, Y)', fontsize=20)
ax.legend()
ax.set_aspect('equal')
plt.show()

点q方向の直線

 X, Yの列ごとの値を繋いだ直線が引かれます。
 原点と点qを結ぶ直線と平行な直線です。

 同様に、転置したX, Yを用いて、直線を描画します。

# 直線を作図
fig, ax = plt.subplots(figsize=(8, 6), facecolor='white')
ax.scatter(X, Y) # 格子点
ax.scatter(0, 0, 
           color='orange', s=100, label='O=(0, 0)') # 原点
ax.scatter(p[0], p[1], 
           color='limegreen', s=100, 
           label='p=('+str(p[0])+', '+str(p[1])+')') # 点p
ax.scatter(q[0], q[1], 
           color='burlywood', s=100, 
           label='q=('+str(q[0])+', '+str(q[1])+')') # 点q
ax.plot(X.T, Y.T, color='limegreen') # pに関する直線
ax.set_xticks(ticks=np.arange(x_min, x_max+1))
ax.set_yticks(ticks=np.arange(y_min, y_max+1))
ax.grid()
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.suptitle('Axes.plot(X.T, Y.T)', fontsize=20)
ax.legend()
ax.set_aspect('equal')
plt.show()

点p方向の直線

 X, Yの行ごとの値を繋いだ直線が引かれます。
 原点と点pを結ぶ直線と平行な直線です。

 X, Yを用いて、2方向の直線を描画します。

# 2D平面を作図
fig, ax = plt.subplots(figsize=(8, 6), facecolor='white')
ax.scatter(X, Y) # 格子点
ax.plot(X, Y, color='C0') # qに関する直線
ax.plot(X.T, Y.T, color='C0', 
        label=['$a p + b q$']+['' for _ in range(len(X)-1)]) # qに関する直線
ax.set_xticks(ticks=np.arange(x_min, x_max+1))
ax.set_yticks(ticks=np.arange(y_min, y_max+1))
ax.grid()
ax.set_xlabel('x')
ax.set_ylabel('y')
fig.suptitle('2D grid', fontsize=20)
ax.legend()
ax.set_aspect('equal')
plt.show()

傾いた2次元格子

 2方向の直線を組み合わせることで、2次元格子を描画できました。

 label引数に1つの文字列を指定すると、その文字列が線ごとに凡例に表示されます。ここでは1つだけ表示されればいいので、表示したい文字列を1つと空の文字列''を格納したリストを指定します。リストの要素数は、元の配列X, Yの場合はX, Yの列数(bの要素数)、転置した配列X.T, Y.Tの場合はX, Yの行数(aの要素数)です。

 基準となる2点の値を変化させたアニメーションを作成します。

・作図コード(クリックで展開)

# フレーム数を指定
frame_num = 60

# 固定する点の座標を指定
p = np.array([1.0, 1.0])

# 変化する点の座標用の値(ラジアン)を作成
t_n = np.linspace(start=0.0, stop=2.0*np.pi, num=frame_num+1)[:frame_num]

# グラフオブジェクトを初期化
fig, ax = plt.subplots(figsize=(8, 6), facecolor='white')
fig.suptitle('2D grid', fontsize=20)

# 作図処理を関数として定義
def update(i):
    
    # 前フレームのグラフを初期化
    plt.cla()

    # i番目の値を取得
    t = t_n[i]
    
    # 変化する点の座標を作成
    q = np.array([np.cos(t), np.sin(t)])
    
    # 平面の座標を計算
    X = A * p[0] + B * q[0]
    Y = A * p[1] + B * q[1]
    
    # 2D平面を作図
    ax.scatter(X, Y) # 格子点
    ax.scatter(0, 0, 
               color='orange', s=100, label='O=(0, 0)') # 原点
    ax.scatter(p[0], p[1], 
               color='limegreen', s=100, 
               label='p=('+str(p[0])+', '+str(p[1])+')') # 点p
    ax.scatter(q[0], q[1], 
               color='burlywood', s=100, 
               label='q=('+', '.join(map(str, q.round(2)))+')') # 点q
    ax.plot(X, Y, color='C0') # qに関する直線
    ax.plot(X.T, Y.T, color='C0', 
            label=['$a p + b q$']+['' for _ in range(len(X)-1)]) # pに関する直線
    ax.set_xticks(ticks=np.arange(x_min, x_max+1))
    ax.set_yticks(ticks=np.arange(y_min, y_max+1))
    ax.grid()
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.legend(loc='upper left')
    ax.set_aspect('equal')

# gif画像を作成
ani = FuncAnimation(fig=fig, func=update, frames=frame_num, interval=150)

# gif画像を保存
ani.save('plane_2d.gif')

基準となる2点と格子の関係

 この例では、点qが単位円上を反時計回りに移動し、それに応じて格子の形(平行四辺形の平面)が変化します。

 以上で、2次元の傾いた格子のグラフを作成できました。次は、3次元の格子のグラフを作成します。

おわりに

 ところで傾いた2次元格子って何ですか。平面?平行四辺形?呼び方が分かりません。
 先に「3次元格子の作図」という題の記事を書いて(これはまぁいいとしましょう)、その後「2次元格子の作図」という題にして(2次元の格子って格子ではとなって)、2次元格子を加工したグラフの記事として「傾いた2次元格子の作図」という題になりました。そして現在「傾いた3次元格子の作図」という題で書いてます。
 内容が共通する記事ではそれが分かるようにタイトルの構成を共通化することを優先してよく分からなくなることが稀によくあります。

 途中で出てきた数式が気になってしまったあなたはこの記事を読んでみると良いかもしれません。この記事では基準となる2点と呼びましたが、この2点を2つのベクトルとして扱うと、原点を含めた3点→直線→平面の関係が分かるかもしれません。

www.anarchive-beta.com

 ちなみに↑の記事を書く補助としてこの記事ができました。

【前の内容】

www.anarchive-beta.com