matplotlibを使用した3次元グラフ描画ができなかった件（解決）

# 記事を書いたきっかけ
下の2つの書籍で機械学習を勉強していたところ、両方で紹介されていたコードをそのまま書いても3次元グラフの描画ができませんでした。

https://www.amazon.co.jp/Python3%E5%B9%B4%E7%94%9F-%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E3%81%97%E3%81%8F%E3%81%BF-%E4%BD%93%E9%A8%93%E3%81%97%E3%81%A6%E3%82%8F%E3%81%8B%E3%82%8B-%E4%BC%9A%E8%A9%B1%E3%81%A7%E3%81%BE%E3%81%AA%E3%81%B9%E3%82%8B-%E5%B7%A7%E5%B0%9A/dp/479816657X

https://www.amazon.co.jp/%E4%BB%8A%E3%81%99%E3%81%90%E8%A9%A6%E3%81%97%E3%81%9F%E3%81%84-%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%83%BB%E6%B7%B1%E5%B1%A4%E5%AD%A6%E7%BF%92-%E3%83%87%E3%82%A3%E3%83%BC%E3%83%97%E3%83%A9%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0-%E7%94%BB%E5%83%8F%E8%AA%8D%E8%AD%98%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%83%AC%E3%82%B7%E3%83%94-%E5%B7%9D%E5%B3%B6/dp/4798056839/ref=sr_1_1?adgrpid=78807001802&hvadid=651142855658&hvdev=c&hvlocphy=1009283&hvnetw=g&hvqmt=e&hvrand=10151192038888833908&hvtargid=kwd-891106954746&hydadcr=9494_13659609&jp-ad-ap=0&keywords=%E7%94%BB%E5%83%8F%E8%AA%8D%E8%AD%98+%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0+%E3%83%AC%E3%82%B7%E3%83%94&qid=1680180566&sr=8-1

（少し古いことを除けば、書籍としては滅茶苦茶おすすめです！）

```Python:[Python]グラフが描画されない
from mpl_toolkits.mplot3d import Axes3D

xx = "sepal width (cm)"
yy = "sepal length (cm)"
zz = "petal length (cm)"

fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)

ax.scatter(df0[xx], df0[yy], df0[zz], color="b")
ax.scatter(df1[xx], df1[yy], df1[zz], color="r")
ax.scatter(df2[xx], df2[yy], df2[zz], color="g")

ax.set_xlabel(xx)
ax.set_ylabel(yy)
ax.set_zlabel(zz)

plt.show()
```

```:出力
<Figure size 500x500 with 0 Axes>
```

本来であれば3d散布図が描画されるはずですが、正常に出力されません
with 0 Axesということで、空の図が作成されているようです

# 描画されなかった原因
https://matplotlib.org/stable/api/prev_api_changes/api_changes_3.4.0.html
>`Matplotlib公式ドキュメント`
>
>Axes3D automatically adding itself to Figure is deprecated
New Axes3D objects previously added themselves to figures when they were created, unlike all other Axes classes, which lead to them being added twice if fig.add_subplot(111, projection='3d') was called.
>
>This behavior is now deprecated and will warn. The new keyword argument auto_add_to_figure controls the behavior and can be used to suppress the warning. The default value will change to False in Matplotlib 3.5, and any non-False value will be an error in Matplotlib 3.6.
>
>In the future, Axes3D will need to be explicitly added to the figure

一言で言うと
「`Axes3D`オブジェクトが自動的に`Figure`オブジェクトに追加される動作は非推奨」
とのこと。

```Python:Axes3DとFigureインスタンス化部分抜粋
fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)
```
上記部分にて、`Axes3D`オブジェクトと`Figure`オブジェクトは作成していますが、Matplotlibのバージョン3.4からは、`Axes3D`オブジェクトを生成しても自動的には`Figure`オブジェクトには追加されなくなったようです。

# 解決方法

公式ドキュメントに書かれている通り、明示的に書けば描画されました。

```Python
# add to Figure
fig.add_axes(ax)
```


```Python:正常にグラフが描画される
from mpl_toolkits.mplot3d import Axes3D

xx = "sepal width (cm)"
yy = "sepal length (cm)"
zz = "petal length (cm)"

fig = plt.figure(figsize=(5, 5))
ax = Axes3D(fig)
#ここを追加↓
fig.add_axes(ax)

ax.scatter(df0[xx], df0[yy], df0[zz], color="b")
ax.scatter(df1[xx], df1[yy], df1[zz], color="r")
ax.scatter(df2[xx], df2[yy], df2[zz], color="g")

ax.set_xlabel(xx)
ax.set_ylabel(yy)
ax.set_zlabel(zz)

plt.show()
```

出力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3299009/39e02b84-9ed6-7782-9931-6c72067bf9e6.png)


# 他にも方法があるらしい
一般的にmatplotlibを使用して3Dグラフを作成するときは、`Axes3D`メソッドではなく、`Figure`クラスで定義されている`add_subplot`メソッドの方がよくつかわれているようです。

```Python:こちらも正常にグラフが描画される
from mpl_toolkits.mplot3d import Axes3D

xx = "sepal width (cm)"
yy = "sepal length (cm)"
zz = "petal length (cm)"

fig = plt.figure(figsize=(5, 5))
#ここ↓
ax = fig.add_subplot(111, projection="3d")

ax.scatter(df0[xx], df0[yy], df0[zz], color="b")
ax.scatter(df1[xx], df1[yy], df1[zz], color="r")
ax.scatter(df2[xx], df2[yy], df2[zz], color="g")

ax.set_xlabel(xx)
ax.set_ylabel(yy)
ax.set_zlabel(zz)

plt.show()
```

`Axes3D`メソッド（with明示的追加）と`add_subplot`メソッドどちらでも同様に描画できるみたいです。