[Python] Plotlyでぐりぐり動かせるグラフを作る

#はじめに
**2020年7月、Plotly 4.8.2 時点の情報で全面的に書き直しました。**
Pythonから利用できるライブラリ、[Plotly](https://plot.ly/python/)の初歩的な使い方、とりわけ特に頻繁に用いる折れ線グラフ（散布図）の作り方を記述します。Plotlyを用いると、下図に示すように**ぐりぐり動かせる**グラフを作ることができます。

![aa2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61468/90ced9e3-7d95-340d-95c6-846b07ef6272.gif)

この記事では扱っていませんが、Plotlyは、折れ線グラフ以外にも[様々な種類のグラフ](https://plotly.com/python/)を描くことができます。また、[Plotly Express](https://plotly.com/python/plotly-express/)というPlotlyの高レベルのAPIや、[Dash](https://plot.ly/dash)というPlotlyを用いたWebアプリケーションフレームワークもあります（[例](https://dash-gallery.plotly.host/Portal/)）。

* [令和時代のPython作図ライブラリのデファクトスタンダードPlotlyExpressの基本的な描き方まとめ](https://qiita.com/hanon/items/d8cbe25aa8f3a9347b0b)
* [可視化ツールDashのチュートリアル - Part1: インストール～描画 -](https://qiita.com/shimopino/items/ddc46adcbd6332511b92)


# インストール
```pip install plotly```でPlotlyをインストールします。

# データの用意
てきとうに、サインカーブとランダムな系列を用意します。

```python
import numpy as np
xs = np.linspace(0, 10, 100)
sins = np.sin(xs)
randoms = np.random.rand(100)
```

# 折れ線グラフの作成
`go.Figure()`の引数の`data`に、散布図（`go.Scatter()`）の系列の配列を入れて、`fig.show()`するだけです。簡単ですね！

```python
import plotly.graph_objects as go
fig = go.Figure(data=[
    go.Scatter(x=xs, y=sins, name="sin"),
    go.Scatter(x=xs, y=randoms, name="random"),
])
fig.show()
```

系列の指定は、上のように`go.Figure(data=[])`で指定してもいいですし、ひとまず`go.Figure()`を作っておいて、`fig.add_trace()`してもOKです。

```python
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=xs, y=sins, name="sin"))
fig.add_trace(go.Scatter(x=xs, y=randoms, name="random"))
fig.show() # 上と同じ結果
```

グラフ上での簡単な操作方法はこちら


* グラフ内でドラッグすると、X軸方向、Y軸方向、矩形に拡大
* X軸、Y軸上でドラッグすると、軸方向に移動
* X軸、Y軸の端でドラッグすると、軸方向に拡大縮小
* ダブルクリックすると、初期表示に戻る


# グラフのカスタマイズ

## グラフ全体、各軸の設定
グラフ全体の描画オプションは`fig.update_layout()`を、各軸は`fig.update_xaxes()`, `fig.update_yaxes()`を呼んで変更しましょう。オプション名を忘れた場合は都度ググってもいいですが、`fig.update_layout(foo=0)`などとわざとエラーを出すと有効なオプション一覧を示してくれます。

```python
fig.update_xaxes(title="x") # X軸タイトルを指定
fig.update_yaxes(title="y") # Y軸タイトルを指定

fig.update_xaxes(range=(1,3)) # X軸の最大最小値を指定
fig.update_xaxes(rangeslider={"visible":True}) # X軸に range slider を表示（下図参照）

fig.update_yaxes(scaleanchor="x", scaleratio=1) # Y軸のスケールをX軸と同じに（plt.axis("equal")）

fig.update_layout(title="Title") # グラフタイトルを設定
fig.update_layout(font={"family":"Meiryo", "size":20}) # フォントファミリとフォントサイズを指定
fig.update_layout(showlegend=True) # 凡例を強制的に表示（デフォルトでは複数系列あると表示）
fig.update_layout(xaxis_type="linear", yaxis_type="log") # X軸はリニアスケール、Y軸はログスケールに
fig.update_layout(width=800, height=600) # 図の高さを幅を指定
fig.update_layout(template="plotly_white") # 白背景のテーマに変更
```

テーマは下記から選択することができます。[テーマの一覧はこちら](https://plotly.com/python/templates/)

```python
['ggplot2', 'seaborn', 'simple_white', 'plotly',
 'plotly_white', 'plotly_dark', 'presentation', 'xgridoff',
 'ygridoff', 'gridon', 'none']
```


![rangeslider.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61468/e6cbf193-112c-46e5-5b58-64e8b04915b1.png)


## 系列の設定
系列のオプション`mode`に、`"lines"`, `"markers"`, `"lines+markers"`を指定すると、線だけ、マーカだけ、線とマーカに描画することができます。

```python
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scatter(x=xs, y=sins, name="lines", mode="lines")) # 線だけ
fig.add_trace(go.Scatter(x=xs, y=sins + 1, name="markers", mode="markers")) # マーカだけ
fig.add_trace(go.Scatter(x=xs, y=sins + 2, name="lines+markers", mode="lines+markers")) # 線とマーカ
fig.show()
```
![linemarker.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61468/42054f4e-9b95-720e-1366-504f9b6d0cf0.png)

## 第2Y軸
`secondary_y`を有効にした`make_subplots`で`fig`を作り、`fig.add_trace()`や`fig.update_yaxes()`に`secondary_y`を設定すればOKです。

```python
import plotly.graph_objects as go
from plotly.subplots import make_subplots

fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter(x=xs, y=sins, name="sin"), secondary_y=False)
fig.add_trace(go.Scatter(x=xs, y=randoms, name="random"), secondary_y=True)
fig.update_yaxes(title_text="y1", secondary_y=False)
fig.update_yaxes(title_text="y2", secondary_y=True) 
fig.show()
```
![secondary_y.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61468/32610974-7a47-6fe6-2837-7d05b1d0e474.png)


## 複数プロット
こちらも`rows`と`cols`を指定した`make_subplots`で`fig`を作り、`fig.add_trace()`や`fig.update_layout()`などに`row`,`col`を設定すればOKです。

```python
import plotly.graph_objects as go
from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2)
fig.add_trace(go.Scatter(x=xs, y=sins, name="sin"), row=1, col=1)
fig.add_trace(go.Scatter(x=xs, y=randoms, name="random"), row=1, col=2)
fig.show()
```
![rowcol.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61468/0e9696e5-9593-0d4b-f3f6-057f5c034fe1.png)


## そのほか
```python
fig.write_html("a.html") # 単独HTMLファイルにぐりぐりできる状態で出力
fig.write_image("a.png") # 画像に出力（グラフ上のアイコンの"download as a png"でもOK）
```