【matplotlib入門】3dプロットで遊んでみた♬

何度も描いてきたような気がするが、今回も探したので、3dプロットの簡単な例をまとめておきます。
###やったこと
・欲しかった図
・オリジナルのデータ
・カラーバーの合成
・hexbinで描画
・自前関数の欲しかった図を作成
###・欲しかった図
なんの変哲もない以下の右の図を探しました。
![orijinal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/731feea7-005e-54d2-632f-11746f0ddbdc.png)

ありそうで、matplotlibのexampleからもなかなか見つからないので、以下の過去記事から見つけて、`ax4.pcolormesh(f(x1, y1), cmap='hsv')`を採用。
(追記)以下のThe mplot3d Toolkitとpcolormeshに例題がありました。
【参考】
・[【matplotlib基本】動的グラフを書いてみる♬~動画出力；Gifアニメーション](https://qiita.com/MuAuan/items/acee29099a98c5cfb020)
・[The mplot3d Toolkit](https://matplotlib.org/stable/tutorials/toolkits/mplot3d.html#the-mplot3d-toolkit)
・[pcolormesh](https://matplotlib.org/stable/gallery/images_contours_and_fields/pcolormesh_levels.html)

<details><summary>pcolormeshの使い方のコード</summary><div>

```py
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D 

#---
from mpl_toolkits.mplot3d.axes3d import get_test_data
X, Y, Z = get_test_data(0.05)

fig = plt.figure(figsize=(12, 6))
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
hs = ax1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='hsv') # cm.viridis
#fig.colorbar(hs, ax=ax1)

ax2 = fig.add_subplot(1, 2, 2)
hv = ax2.pcolormesh(X,Y,Z, cmap='hsv')
fig.colorbar(hv, ax=ax2)
plt.pause(1)
plt.savefig('orijinal.png')
plt.close()
```
</div></details>

【参考】
・[Source code for mpl_toolkits.mplot3d.axes3d](https://matplotlib.org/stable/_modules/mpl_toolkits/mplot3d/axes3d.html)

<details><summary>get_test_dataのコード(参考より)</summary><div>

```py
def get_test_data(delta=0.05):
    """Return a tuple X, Y, Z with a test data set."""
    x = y = np.arange(-3.0, 3.0, delta)
    X, Y = np.meshgrid(x, y)

    Z1 = np.exp(-(X**2 + Y**2) / 2) / (2 * np.pi)
    Z2 = (np.exp(-(((X - 1) / 1.5)**2 + ((Y - 1) / 0.5)**2) / 2) /
          (2 * np.pi * 0.5 * 1.5))
    Z = Z2 - Z1

    X = X * 10
    Y = Y * 10
    Z = Z * 500
    return X, Y, Z
```
</div></details>


###・オリジナルのデータ
![orijinal2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/ba96f9a3-a633-3075-f5bb-8e6b57d73fe9.png)
参考は、3dプロットをsubplotとして並べられる（配置アレンジできる）ことを有意義としているようだが、今では普通にいろいろなところで使われている。むしろ、オリジナルデータの使い方が美味しそうです。
【参考】
・[What's New 1 Subplot3d](https://matplotlib.org/stable/gallery/pyplots/whats_new_1_subplot3d.html#sphx-glr-gallery-pyplots-whats-new-1-subplot3d-py)

<details><summary>オリジナルデータの使い方のコード</summary><div>

```py
fig = plt.figure(figsize=(12, 6))

ax = fig.add_subplot(1, 2, 1, projection='3d')
X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis,
                       linewidth=0, antialiased=False)
ax.set_zlim3d(-1.01, 1.01)
fig.colorbar(surf, shrink=0.5, aspect=5)

ax = fig.add_subplot(1, 2, 2, projection='3d')
#X, Y, Z = get_test_data(0.05)
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)

plt.pause(1)
plt.savefig('orijinal2.png')
plt.close()
```
</div></details>

###・カラーバーの合成
ここでは、負値と正値で一度描画して、それを合成したものを示している。
単純な合成だと思っていたが、3つ目の絵を見ると再計算しているようだ。
因みに、pos; cmap='Blues', neg; cmap='Reds_r', 最後のものはcmap='RdBu'で描画している。
単純に'hsv'で描画したものが、上のように書けているので異なっているようだ。
![AddClolorbar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/60392e8e-5e27-4656-f066-15a52de78f35.png)
![AddClolorbar3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/dc0e3248-5440-36d3-95a6-b2fbef52c513.png)
![AddClolorbar2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/e612507b-865a-ab78-e006-4bc0c1696eff.png)
【参考】
・[Colorbar](https://matplotlib.org/stable/gallery/color/colorbar_basics.html#sphx-glr-gallery-color-colorbar-basics-py)

<details><summary>カラーバーの合成のコード</summary><div>

```py
#https://matplotlib.org/stable/gallery/color/colorbar_basics.html#sphx-glr-gallery-color-colorbar-basics-py

import numpy as np
import matplotlib.pyplot as plt

# setup some generic data
N = 37
#X, Y, Z = get_test_data(0.05)
#x, y = X, Y #np.mgrid[:N, :N]
#Z = Z #(np.cos(x*0.2) + np.sin(y*0.3))
x, y = np.mgrid[:N, :N]
Z = (np.cos(x*0.2) + np.sin(y*0.3))


# mask out the negative and positive values, respectively
Zpos = np.ma.masked_less(Z, 0)
Zneg = np.ma.masked_greater(Z, 0)

fig, (ax1, ax2, ax3) = plt.subplots(figsize=(13, 3), ncols=3)

# plot just the positive data and save the
# color "mappable" object returned by ax1.imshow
pos = ax1.imshow(Zpos, cmap='Blues', interpolation='none')

# add the colorbar using the figure's method,
# telling which mappable we're talking about and
# which axes object it should be near
fig.colorbar(pos, ax=ax1)

# repeat everything above for the negative data
neg = ax2.imshow(Zneg, cmap='Reds_r', interpolation='none')
fig.colorbar(neg, ax=ax2)

# Plot both positive and negative values between +/- 1.2
pos_neg_clipped = ax3.imshow(Z, cmap='RdBu', vmin=-1.2, vmax=1.2,
                             interpolation='none')
# Add minorticks on the colorbar to make it easy to read the
# values off the colorbar.
cbar = fig.colorbar(pos_neg_clipped, ax=ax3, extend='both')
cbar.minorticks_on()
plt.savefig('AddClolorbar.png')
plt.pause(1)
plt.close()
```
</div></details>

###・hexbinで描画
これは、調べていると見つけたので、示しておく。コードは分かるがやっている処理内容は完全には理解できない。以下の参考では、scatterをhexbinで置き換えるとと言っているが、上記のX,Y,Zで描画しても綺麗な絵は得られない。
![hexbin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/f3bdb184-4282-7378-5b19-dda215fd1f66.png)

【参考】
・[Hexbin Demo](https://matplotlib.org/stable/gallery/statistics/hexbin_demo.html#sphx-glr-gallery-statistics-hexbin-demo-py)

<details><summary>hexbinで描画のコード</summary><div>

```py
#https://matplotlib.org/stable/gallery/statistics/hexbin_demo.html#sphx-glr-gallery-statistics-hexbin-demo-py

import numpy as np
import matplotlib.pyplot as plt

# Fixing random state for reproducibility
np.random.seed(19680801)

n = 100000
x = np.random.standard_normal(n)
y = 2.0 + 3.0 * x + 4.0 * np.random.standard_normal(n)
xmin = x.min()
xmax = x.max()
ymin = y.min()
ymax = y.max()

fig, axs = plt.subplots(ncols=2, sharey=True, figsize=(7, 4))
fig.subplots_adjust(hspace=0.5, left=0.07, right=0.93)
ax = axs[0]
hb = ax.hexbin(x, y, gridsize=50, cmap='inferno')
ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
ax.set_title("Hexagon binning")
cb = fig.colorbar(hb, ax=ax)
cb.set_label('counts')

ax = axs[1]
hb = ax.hexbin(x, y, gridsize=50, bins='log', cmap='inferno')
ax.set(xlim=(xmin, xmax), ylim=(ymin, ymax))
ax.set_title("With a log color scale")
cb = fig.colorbar(hb, ax=ax)
cb.set_label('log10(N)')

plt.pause(1)
plt.savefig('hexbin.png')
plt.close()
```
</div></details>
###・自前関数の欲しかった図を作成
最後にオリジナルな関数の上記プロットを試みた。
結果は、以下の通り綺麗に描けました。
![orijinal3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/6ef74d1e-ca0a-2d98-5a81-fc31b96c1d52.png)
![orijinal4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/78521788-d1dc-4fd2-46a5-2423d13b78c7.png)


<details><summary>自前関数の描画コード</summary><div>

```py
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D 

#---
from mpl_toolkits.mplot3d.axes3d import get_test_data
def func(x, y):
    return x*x + 0.1*y*y #+x*y

#X, Y, Z = get_test_data(0.05)
X = np.arange(-15, 15, 1)
Y = np.arange(-15, 15, 1)
X, Y = np.meshgrid(X, Y)
R = X*X + 0.1*Y*Y
Z = R
#print(Z)

fig = plt.figure(figsize=(12, 6))
ax1 = fig.add_subplot(1, 2, 1, projection='3d')
hs = ax1.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='hsv') # cm.viridis
fig.colorbar(hs, ax=ax1)

ax2 = fig.add_subplot(1, 2, 2)
hv = ax2.pcolormesh(X,Y,Z, cmap='hsv')
fig.colorbar(hv, ax=ax2)
plt.pause(1)
plt.savefig('orijinal3.png')
plt.close()

#---
fig = plt.figure(figsize=(12, 6))

ax = fig.add_subplot(1, 2, 1)
X = np.arange(-15, 15, 1)
Y = np.arange(-80, 80, 5)
X, Y = np.meshgrid(X, Y)
R = func(X,Y) #X*X + 0.01*Y*Y #np.sqrt(X**2 + Y**2)
Z = R #np.sin(R)
#print(Z)
#surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis,linewidth=0, antialiased=False)
surf = ax.pcolormesh(X,Y,Z, cmap='hsv')
#ax.set_zlim3d(-1.01, 1.01)
fig.colorbar(surf, shrink=0.5, aspect=5)

ax = fig.add_subplot(1, 2, 2, projection='3d')
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)

plt.pause(1)
plt.savefig('orijinal4.png')
plt.close()
```
</div></details>
最後に、描画時の配色が気に入らないので変更する
【参考】
・[Choosing Colormaps in Matplotlib](https://matplotlib.org/stable/tutorials/colors/colormaps.html)
ということで、やはり日本人の感覚だと'jet'ですね♬
![orijinal5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/1bc45915-ae1e-e306-3e07-2752bc060803.png)
![orijinal6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/298c8233-3b12-3500-2436-d775c3a91482.png)

###まとめ
・3dプロットで遊んでみた♬
・自前関数で、3d描画、2dマップ、ワイヤーフレーム、そしてcolorbarを使えるようになった

書いた時点で以下を発見、合わせるといい感じです。、
【参考】
・[[Pythonによる科学・技術計算] 2次元(カラー)等高線等の描画，可視化，matplotlib](https://qiita.com/sci_Haru/items/5b4c34d5330a545001cf)
ということで、等高線追記です。
![orijinal7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233744/9e61ec0e-8375-1535-e2d4-e2cff07982a0.png)

<details><summary>自前関数の等高線追記描画コード</summary><div>

```py
#---
fig = plt.figure(figsize=(12, 6))

ax = fig.add_subplot(1, 2, 1)
X = np.arange(-15, 15, 1)
Y = np.arange(-80, 80, 5)
X, Y = np.meshgrid(X, Y)
R = func(X,Y) #X*X + 0.01*Y*Y #np.sqrt(X**2 + Y**2)
Z = R #np.sin(R)
#print(Z)
#surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis,linewidth=0, antialiased=False)
surf = ax.pcolormesh(X,Y,Z, cmap='jet')
#ax.set_zlim3d(-1.01, 1.01)
fig.colorbar(surf, shrink=0.5, aspect=5)
# 等高線図の生成。
cont=ax.contour(X,Y,Z,  10, Vmax=1,colors=['white'])
cont.clabel(fmt='%1.1f', fontsize=14)

ax = fig.add_subplot(1, 2, 2, projection='3d')
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2)

plt.pause(1)
plt.savefig('orijinal7.png')
plt.close()
```
</div></details>