からっぽのしょこ

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

3D棒グラフの作図

はじめに

 Matplotlibライブラリを利用して3D棒グラフを作図します。

3D棒グラフの作成

 MatplotlibライブラリのPyPlotモジュールのbar3d()を使って3次元の棒グラフを作成します。

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

# 利用するライブラリ
import numpy as np
import matplotlib.pyplot as plt


基本形の確認

 まずは、デフォルトの設定で3D棒グラフを作成します。

 プロットする点の値を設定します。

# 起点を指定
x = 0.0
y = 0.0
z = 0.0

# 変化量を指定
dx = 1.0
dy = 1.0
dz = 1.0

 3次元の空間上の点$(x, y, z)$を指定します。
 また、指定した点からの幅dx、奥行きdy、高さdzを指定します。

 Axes.bar3d()で3D棒グラフを作成します。

# 3D棒グラフを作成
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=dx, dy=dy, dz=dz) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('bar3d', fontsize='20') # タイトル
plt.show() # 描画

f:id:anemptyarchive:20220121155351p:plain
3D棒グラフの基本形

 これが基本形のグラフです。

 補助線を引いて、各引数の影響を確認します。

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

# 3D棒グラフを作成
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.25) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, s=100, color='purple', label='(x, y, z)') # 起点
ax.scatter(xs=x+dx, ys=y, zs=z, s=100, color='red', label='(x+dx, y, z)') # x軸方向に変化した点
ax.scatter(xs=x, ys=y+dy, zs=z, s=100, color='pink', label='(x, y+dy, z)') # y軸方向に変化した点
ax.scatter(xs=x, ys=y, zs=z+dz, s=100, color='orange', label='(x, y, z+dz)') # z軸方向に変化した点
ax.scatter(xs=x+dx, ys=y+dy, zs=z+dz, s=100, color='springgreen', label='(x+dx, y+dy, z+dz)') # 全ての軸で変化した点
ax.quiver(x, y, z, dx, 0.0, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x, y, z, 0.0, dy, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x, y, z, 0.0, 0.0, dz, color='purple', linestyle='--', arrow_length_ratio=0.1) # z軸の変化量
ax.quiver(x, y, z, dx, dy, dz, color='purple', arrow_length_ratio=0.1) # 全ての軸の変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('bar3d', fontsize='20') # タイトル
fig.legend() # 凡例
plt.show() # 描画


f:id:anemptyarchive:20220121155429p:plain
引数の影響

 x・y・z軸それぞれの方向に変化した点が赤・ピンク・オレンジ色の点、全ての方向に変化した点が黄緑色の点です。各点がバーの先端になっているのを確認できます。

 全ての方向への変化を表す実線のベクトルは、各軸への変化を表す破線のベクトルを足したものです。

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

# 3D棒グラフを作成
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.25) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, s=100, color='purple', label='(x, y, z)') # 起点
ax.scatter(xs=x+dx, ys=y, zs=z, s=100, color='red', label='(x+dx, y, z)') # x軸方向に変化した点
ax.scatter(xs=x+dx, ys=y+dy, zs=z, s=100, color='aqua', label='(x+dx, y+dy, z)') # x軸とy軸方向に変化した点
ax.scatter(xs=x+dx, ys=y+dy, zs=z+dz, s=100, color='springgreen', label='(x+dx, y+dy, z+dz)') # 全ての軸で変化した点
ax.quiver(x, y, z, dx, 0.0, 0.0, color='purple', linestyle=':', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x+dx, y, z, 0.0, dy, 0.0, color='purple', linestyle=':', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x+dx, y+dy, z, 0.0, 0.0, dz, color='purple', linestyle=':', arrow_length_ratio=0.1) # z軸の変化量
ax.quiver(x, y, z, dx, dy, dz, color='purple', arrow_length_ratio=0.1) # xの変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('bar3d', fontsize='20') # タイトル
fig.legend() # 凡例
plt.show() # 描画


f:id:anemptyarchive:20220121155445p:plain
引数の影響

 足す順番に関わらず黄緑色の点を指します。

 dx, dy, dzがそれぞれバーの幅・奥行き・高さの値なのを確認できます。
 入力$x, y$に対応する出力$z$のようなデータの場合は、$x, y$がx, yで$z$がdzに対応しています。

起点の調整

 バーの底面の中心に点$(x, y, z)$がくるように調整します。

 バーのサイズを指定します。

# 起点を指定
x = 0.0
y = 0.0
z = 0.0

# バーのハーフサイズの値を指定
a = 0.5

# 変化量を指定
dx = a * 2.0
dy = a * 2.0
dz = 1.0

 バーの幅・奥行きのサイズの半分の値をaとして指定します。幅・奥行きのサイズdx, dyaの2倍の値とします。

 aを使って起点をズラして3D棒グラフを作成します。

# デフォルトの設定
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.5) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, s=100, color='purple', label='(x, y, z)') # 起点
ax.quiver(x, y, z, dx, 0.0, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x, y, z, 0.0, dy, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x, y, z, 0.0, 0.0, dz, color='purple', linestyle='--', arrow_length_ratio=0.1) # z軸の変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('(x, y, z)', fontsize='20') # タイトル
fig.legend() # 凡例
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画

# 起点をズラす
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.5) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, color='purple', s=100, label='(x, y, z)') # 元の起点
ax.scatter(xs=x-a, ys=y-a, zs=z, color='green', s=100, label='(x-' + str(a) + ', y-' + str(a) + ', z)') # 調整後の起点
ax.quiver(x-a, y-a, z, dx, 0.0, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x-a, y-a, z, 0.0, dy, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x-a, y-a, z, 0.0, 0.0, dz, color='green', linestyle='--', arrow_length_ratio=0.1) # z軸の変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('(x-a, y-a, z)', fontsize='20') # タイトル
fig.legend() # 凡例
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画

f:id:anemptyarchive:20220121155519p:plainf:id:anemptyarchive:20220121155523p:plain
プロット位置の調整

 Axes.bar3d()の引数x, yaを引いた値x-a, y-aを指定します。

 元の図では紫色の点$(x = 0, y = 0, z = 0)$が底面の角だったのに対して、調整後の図は底面の中心になっています。代わりに、緑色の点$(x = -a, y = -a, z = 0)$が角になっています。

 続いて、複数の場合を考えます。

 バーの起点を作成します。

# 値を作成
vals = np.arange(3)

# 格子点を作成
X, Y = np.meshgrid(vals, vals)

# 起点を設定
x = X.flatten()
y = Y.flatten()
z = np.zeros_like(x)
print(x)
print(y)
print(z)
[0 1 2 0 1 2 0 1 2]
[0 0 0 1 1 1 2 2 2]
[0 0 0 0 0 0 0 0 0]

 簡単な例として、x・y軸は0, 1, 2、z軸は0の点とします。

 1つの点のときと同様に、バーの半分のサイズを指定して幅・奥行き・高さを作成します。

# バーのハーフサイズの値を指定
a = 0.5

# 変化量を指定
dx = np.repeat(a=a * 2.0, repeats=len(x))
dy = np.repeat(a=a * 2.0, repeats=len(y))
dz = np.arange(len(z))
print(dx)
print(dy)
print(dz)
[1. 1. 1. 1. 1. 1. 1. 1. 1.]
[1. 1. 1. 1. 1. 1. 1. 1. 1.]
[0 1 2 3 4 5 6 7 8]

 dx, dyは、aの2倍の値を起点と同じ数に複製します。dzは、簡単な例として0から1ずつ増える値とします。

 さきほどと同じコードで作図できます。

f:id:anemptyarchive:20220121155611p:plainf:id:anemptyarchive:20220121155613p:plain
引数の影響

f:id:anemptyarchive:20220121155624p:plainf:id:anemptyarchive:20220121155627p:plain
プロット位置の調整

 全ての点$(x, y, z)$がそれぞれのバーの底面の中心になっているのを確認できます。

 aの値を小さくするとバーに間隔を空けられます。

f:id:anemptyarchive:20220121155647p:plainf:id:anemptyarchive:20220121155650p:plain
バーのサイズと間隔

 aを0.4にするとバーが細くなり、バーの間に0.2の間隔を空けられます。

 幅を持たない棒グラフも作成できます。

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

# バーのハーフサイズの値を指定
a = 0.4

# 変化量を指定
dx = np.repeat(a=a * 2.0, repeats=len(x))
dy = np.repeat(a=0.0, repeats=len(y))

# y軸方向の幅が0のグラフ
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.5) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, color='purple', s=100, label='(x, y, z)') # 元の起点
ax.scatter(xs=x-a, ys=y, zs=z, color='green', s=100, label='(x-' + str(a) + ', y, z)') # 調整後の起点
ax.quiver(x-a, y, z, dx, 0.0, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x-a, y, z, 0.0, dy, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x-a, y, z, 0.0, 0.0, dz, color='green', linestyle='--', arrow_length_ratio=0.1) # z軸の変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('(x-a, y, z)', fontsize='20') # タイトル
fig.legend() # 凡例
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画
# バーのハーフサイズの値を指定
a = 0.4

# 変化量を指定
dx = np.repeat(a=0.0, repeats=len(x))
dy = np.repeat(a=a * 2.0, repeats=len(y))

# x軸方向の幅が0のグラフ
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y-a, z=z, dx=dx, dy=dy, dz=dz, color='white', alpha=0.5) # 3D棒グラフ
ax.scatter(xs=x, ys=y, zs=z, color='purple', s=100, label='(x, y, z)') # 元の起点
ax.scatter(xs=x, ys=y-a, zs=z, color='green', s=100, label='(x, y-' + str(a) + ', z)') # 調整後の起点
ax.quiver(x, y-a, z, dx, 0.0, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # x軸の変化量
ax.quiver(x, y-a, z, 0.0, dy, 0.0, color='green', linestyle='--', arrow_length_ratio=0.1) # y軸の変化量
ax.quiver(x, y-a, z, 0.0, 0.0, dz, color='green', linestyle='--', arrow_length_ratio=0.1) # z軸の変化量
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('(x, y-a, z)', fontsize='20') # タイトル
fig.legend() # 凡例
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画


f:id:anemptyarchive:20220121155734p:plainf:id:anemptyarchive:20220121155736p:plain
バーのサイズ

f:id:anemptyarchive:20220121155750p:plainf:id:anemptyarchive:20220121155753p:plain
バーのサイズ

 この図は、x軸とy軸のサイズを分けてax, ayとして片方を0にすることでも再現できます。

 ここまでは、3D棒グラフのバーについて確認しました。以降は装飾について確認します。この資料の作図コードは概ねスカラ・複数どちらでも処理できます。

shade引数

 shade引数は、棒グラフの陰影を設定します。

 shade=Trueを指定します。

# 影を付ける:(デフォルト)
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=dx, dy=dy, dz=dz, shade=True) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('shade=True', fontsize='20') # タイトル
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画

f:id:anemptyarchive:20220121155809p:plain
陰影あり

 陰影が入ることで立体的に見えます。Trueがデフォルトの設定なので、shade引数を指定しなくても陰影が付きます。

 shade=Falseを指定します。

# 影を消す
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, shade=False) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('shade=False', fontsize='20') # タイトル
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画

f:id:anemptyarchive:20220121155822p:plain
陰影なし

 濃淡のないフラットな色合いになります。

color引数

 color引数は、棒グラフの色を設定します。これまでのように、色名を指定することでバーの色を設定できます。ここでは、カラーマップによる設定を行います。

 まずは、z軸の値をカラーマップに応じた色に変換する方法を確認します。この処理は、理解しなくても使えるので、飛ばしても問題ありません。

# カラーマップを指定
cm = plt.get_cmap('jet')

# RGBA情報に変換
print(cm(0.5))
(0.4901960784313725, 1.0, 0.4775458570524984, 1.0)

 pyplot.get_cmap()にカラーマップ名を指定すると、0から1の値を対応する色に変換する関数を返します。その関数をcm()として利用します。
 cm()は、入力した値を4つの値に変換して出力します。4つの値は、RGBA情報(色情報)と言い、赤色(R)・緑色(G)・青色(B)・透過度(A)を表します。

 zを最大値で割ることで0から1の値に正規化してから、cm()で変換します。

# RGB情報に変換
print(dz / np.max(dz))
print(cm(dz / np.max(dz)))
[0.    0.125 0.25  0.375 0.5   0.625 0.75  0.875 1.   ]
[[0.         0.         0.5        1.        ]
 [0.         0.00196078 1.         1.        ]
 [0.         0.50392157 1.         1.        ]
 [0.08538899 1.         0.88235294 1.        ]
 [0.49019608 1.         0.47754586 1.        ]
 [0.89500316 1.         0.07273877 1.        ]
 [1.         0.58169935 0.         1.        ]
 [1.         0.11692084 0.         1.        ]
 [0.5        0.         0.         1.        ]]

 dzの要素ごとに4つの値に変換されます。

 この結果をcolor引数に指定します。

# カラーマップを指定
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title("cmap='jet'", fontsize='20') # タイトル
plt.show() # 描画

f:id:anemptyarchive:20220121155859p:plainf:id:anemptyarchive:20220121155902p:plain
カラーマップの設定

 jetは青色から赤色に変化し、vridisだと紫色から黄色に変化します。利用できるカラーマップについては、「Choosing Colormaps in Matplotlib — Matplotlib 3.5.1 documentation」を参照してください。

edgecolor引数

 edgecolor引数は、棒グラフの辺の色を設定します。

 colorに指定した色情報を、そのままedgecolorに指定してみます。

# カラーマップを指定
cm = plt.get_cmap('rainbow')

# デフォルトの設定
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), edgecolor=cm(dz / np.max(dz)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('edgecolor=cm(...)', fontsize='20') # タイトル
plt.show() # 描画

f:id:anemptyarchive:20220121155917p:plain
辺の色の設定:(失敗例)

 面の色と辺の色が異なっています。また、1つのバーでも辺の色が違うのが分かります。

 面と辺の色を一致させるには、1つのバーに対して6個の色情報を設定する必要があります。

# 面と辺の色を一致させる
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), edgecolor=cm(np.repeat(dz / np.max(dz), 6)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('edgecolor=cm(np.repeat(..., 6))', fontsize='20') # タイトル
plt.show() # 描画

f:id:anemptyarchive:20220121155946p:plain
辺の色の設定

 np.repeat()で色情報を6個ずつ複製して引数に渡します。

 詳しく確認してみます。飛ばしても問題ありません。

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

# カラーマップを指定
cm = plt.get_cmap('rainbow')

# カラーマップを確認
plt.figure(figsize=(9, 8)) # 図の設定
plt.scatter(np.arange(6), np.arange(6), color=cm(np.arange(6) / 5), s=250) # 散布図
plt.grid() # グリッド線
plt.title("cmap='rainbow'", fontsize=20) # タイトル
plt.show() # 描画


f:id:anemptyarchive:20220121160004p:plain
カラーマップの色の確認

 Rainbowからこの6つの色を使います。

 1つの点に対して6つの色を指定してみます。

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

# 辺の色付け順を確認
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=-0.5, y=-0.5, z=0.0, dx=1.0, dy=1.0, dz=1.0, 
         color='white', edgecolors=cm(np.arange(6) / 5), linewidth=5, alpha=0.5) # 3D棒グラフ
ax.text(x=0.0, y=0.0, z=0.0, s='-Z', color=cm(0 / 5), fontsize=20) # -Zの面
ax.text(x=0.0, y=0.0, z=1.0, s='+Z', color=cm(1 / 5), fontsize=20) # +Zの面
ax.text(x=0.0, y=-0.5, z=0.5, s='-Y', color=cm(2 / 5), fontsize=20) # -Yの面
ax.text(x=0.0, y=0.5, z=0.5, s='+Y', color=cm(3 / 5), fontsize=20) # +Yの面
ax.text(x=-0.5, y=0.0, z=0.5, s='-X', color=cm(4 / 5), fontsize=20) # -Xの面
ax.text(x=0.5, y=0.0, z=0.5, s='+X', color=cm(5 / 5), fontsize=20) # +Xの面
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('edgecolors=[-Z, +Z, -Y, +Y, -X, +X]', fontsize='20') # タイトル
#ax.view_init(elev=0, azim=270) # 表示アングル
plt.show() # 描画


f:id:anemptyarchive:20220121160027p:plain
辺の色付け順の確認

 色々な向きから確認できるようにアニメーションで確認します。

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

# 追加で利用するモジュール
from matplotlib.animation import FuncAnimation

# 作図用の角度を作成
v_vals = np.arange(start=-90.0, stop=270.0, step=3.0)
h_vals = np.arange(start=0.0, stop=360.0, step=3.0)

# カラーマップを指定
cm = plt.get_cmap('rainbow')

# 図を初期化
fig = plt.figure(figsize=(9, 8))
ax = fig.add_subplot(projection='3d') # 3D用の設定

# 作図処理を関数として定義
def update(i):
    # 前フレームのグラフを初期化
    plt.cla()
    
    # i回目の角度を取得
    #v = 0.0
    h = 270.0
    v = v_vals[i]
    #h = h_vals[i]
    
    # 3D棒グラフを作成
    ax.bar3d(x=-0.5, y=-0.5, z=0.0, dx=1.0, dy=1.0, dz=1.0, 
             color='white', edgecolors=cm(np.arange(6) / 5), linewidth=5, alpha=0.5) # 3D棒グラフ
    ax.text(x=0.0, y=0.0, z=0.0, s='-Z', color=cm(0 / 5), fontsize=20) # -Zの面
    ax.text(x=0.0, y=0.0, z=1.0, s='+Z', color=cm(1 / 5), fontsize=20) # +Zの面
    ax.text(x=0.0, y=-0.5, z=0.5, s='-Y', color=cm(2 / 5), fontsize=20) # -Yの面
    ax.text(x=0.0, y=0.5, z=0.5, s='+Y', color=cm(3 / 5), fontsize=20) # +Yの面
    ax.text(x=-0.5, y=0.0, z=0.5, s='-X', color=cm(4 / 5), fontsize=20) # -Xの面
    ax.text(x=0.5, y=0.0, z=0.5, s='+X', color=cm(5 / 5), fontsize=20) # +Xの面
    ax.set_xlabel('x') # x軸ラベル
    ax.set_ylabel('y') # y軸ラベル
    ax.set_zlabel('z') # z軸ラベル
    if v < 0: # (270 < v < 360の範囲で軸が反転する対策)
        ax.set_title('v=' + str(np.round(360.0 + v)) + ', h=' + str(np.round(h))) # タイトル
    elif v >= 0:
        ax.set_title('v=' + str(np.round(v)) + ', h=' + str(np.round(h))) # タイトル
    ax.view_init(elev=v, azim=h) # 表示アングル

# gif画像を作成
anime_bar3d = FuncAnimation(fig, update, frames=len(v_vals), interval=100)

# gif画像を保存
anime_bar3d.save('bar3d.gif')

 $270^{\circ} < v < 360^{\circ}$の範囲でx・y軸が反転するようで、$-90^{\circ} \leq v < 270^{\circ}$の範囲で描画します(負の値の角度はプログラム上$-90^{\circ} = 270^{\circ}$なはず(?)ですが、こちらだと軸が反転しないようです(謎))。


f:id:anemptyarchive:20220121192402g:plainf:id:anemptyarchive:20220121160229g:plain
辺の色付け順の確認

 edgecolorに指定した6つの色情報は、6つの面-Z, +Z, -Y, +Y, -X, +Zの順に割り当てられます。-Zは点$(0, 0, 0)$がある面を表し、同様に+Zは$(0, 0, dz)$・-Yは$(0, -a, \frac{dz}{2})$・+Yは$(0, a, \frac{dz}{2})$・-Xは$(-a, 0, \frac{dz}{2})$・+Xは$(a, 0, \frac{dz}{2})$です。
 各面を構成する4つの辺が同じ色になります。また、1つの辺は2つの面に接しているので、2つの色が割り当てられています。そのため、アニメーションから分かるように、表示する角度によって色が変わります。

軸目盛の設定

 軸目盛を設定します。

 Axes.set_*ticks()で各軸の目盛を表示する値(位置)を設定できます。

# 軸目盛の表示位置を指定
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('ticks', fontsize='20') # タイトル
ax.set_xticks(ticks=vals) # x軸目盛
ax.set_yticks(ticks=vals) # y軸目盛
plt.show() # 描画

f:id:anemptyarchive:20220121160326p:plain
軸目盛の設定

 ticks引数に表示する値を指定します。

 さらに、Axes.set_*ticklabels()で軸目盛のラベルを設定できます。

# 軸目盛の表示位置と文字列を指定
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('ticks, ticklabels', fontsize='20') # タイトル
ax.set_xticks(ticks=vals) # x軸目盛
ax.set_yticks(ticks=vals) # y軸目盛
ax.set_xticklabels(labels=['a', 'b', 'c']) # x軸目盛ラベル
ax.set_yticklabels(labels=['A', 'B', 'C']) # y軸目盛ラベル
plt.show() # 描画

f:id:anemptyarchive:20220121160342p:plain
軸目盛ラベルの設定

Axes.set_*ticks()で設定した位置に、labels引数に指定した文字列が表示されます。

 これまでは、x, y引数に指定する値を調整することでバーの中心を調整しました。軸目盛を調整することでも再現できます。

# 起点を変更せずに目盛によって調整する
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=a*2.0, dy=a*2.0, dz=dz, 
         color=cm(dz / np.max(dz)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('ticks, ticklabels', fontsize='20') # タイトル
ax.set_xticks(ticks=vals + a) # x軸目盛
ax.set_yticks(ticks=vals + a) # y軸目盛
ax.set_xticklabels(labels=vals) # x軸目盛ラベル
ax.set_yticklabels(labels=vals) # y軸目盛ラベル
plt.show() # 描画

f:id:anemptyarchive:20220121160421p:plain
プロット位置の設定

 ax.bar3d()の引数x, yにそのままの値x, yを指定し、軸目盛の位置をa分ズラした位置に元の値を表示します。

 「基本形の確認」と同様に確認してみます。

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

# 調整の結果を確認
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
ax.bar3d(x=x, y=y, z=z, dx=a*2.0, dy=a*2.0, dz=dz, color='white', alpha=0.5) # 3D棒グラフ
ax.quiver(x, y, z, dx, 0.0, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # x軸
ax.quiver(x, y, z, 0.0, dy, 0.0, color='purple', linestyle='--', arrow_length_ratio=0.1) # y軸
ax.quiver(x, y, z, 0.0, 0.0, dz, color='purple', linestyle='--', arrow_length_ratio=0.1) # z軸
ax.scatter(xs=x, ys=y, zs=z, s=100, color='purple', label='(x, y, z)') # 元の起点
ax.scatter(xs=x-a, ys=y-a, zs=z, color='green', s=100, label='(x-' + str(a) + ', y-' + str(a) + ', z)') # 実際に調整したときの起点
ax.scatter(xs=x+a, ys=y+a, zs=z, color='mediumblue', s=100, label='(x+' + str(a) + ', y+' + str(a) + ', z)') # 見掛け上の調整後の中心
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('ticks, ticklabels', fontsize='20') # タイトル
ax.set_xticks(ticks=vals + a) # x軸目盛
ax.set_yticks(ticks=vals + a) # y軸目盛
ax.set_xticklabels(labels=vals) # x軸目盛ラベル
ax.set_yticklabels(labels=vals) # y軸目盛ラベル
fig.legend() # 凡例
#ax.view_init(elev=90, azim=270) # 表示アングル
plt.show() # 描画


f:id:anemptyarchive:20220121160808p:plainf:id:anemptyarchive:20220121160811p:plain
プロット位置の確認

 元の値(紫色の点)がバーの角のままで、x軸とy軸方向にaを加えた値(青色の点)がバーの底面の中心になっているのを確認できます。

カラーバーの表示

 カラーバーを設定します。

 Mappableオブジェクトを使ってカラーバーを表示します。

# カラーマップを指定
cm = plt.get_cmap('jet')

# 3D棒グラフの作成
fig = plt.figure(figsize=(9, 8)) # 図の設定
ax = fig.add_subplot(projection='3d') # 3D用の設定
bar = ax.bar3d(x=x-a, y=y-a, z=z, dx=dx, dy=dy, dz=dz, 
         color=cm(dz / np.max(dz)), edgecolor=cm(np.repeat(dz / np.max(dz), 6)), alpha=0.5) # 3D棒グラフ
ax.set_xlabel('x') # x軸ラベル
ax.set_ylabel('y') # y軸ラベル
ax.set_zlabel('z') # z軸ラベル
ax.set_title('bar3d', fontsize='20') # タイトル
mappable = plt.cm.ScalarMappable(cmap='jet') # Mappableオブジェクト
mappable.set_array(dz) # Mappableに値を設定
cbar = plt.colorbar(mappable, shrink=0.75, aspect=10) # カラーバー
cbar.set_label('z') # カラーバーラベル
#ax.view_init(elev=0, azim=300) # 表示アングル
plt.show() # 描画

 Pyplot.cm.ScalarMappable()cmap引数にカラーマップ名を指定すると、対応するMappableオブジェクトが出力されます。
 appableset_array()メソッドにカラーマップと対応付ける値を指定します。
 Pyplot.colorbar()の第1引数にmappableを指定してカラーバーを表示します。サイズは、引数shrink, aspectで調整できます。shrinkは、図全体に対するカラーバーの高さの比率です。aspectは、カラーバーの横幅に対する高さの比です。
 cbarset_label()メソッドでカラーバーにラベルを設定できます。

f:id:anemptyarchive:20220121160826p:plain
カラーバーの設定


 以上で、3D棒グラフを作成できました。

おわりに

 悪名高き3D棒グラフ、だけど多項分布のグラフを描くのに必要だったんだよぅ。で思いのほか面倒だったのでブログにしておきます。目的のこちらも参考にしてみてください。

www.anarchive-beta.com


 さて、さきほど公開された新MVをどうぞ。

 初単独ホールツアーに初単独日本武道館公演決定めでたいっ!行きたいーっ