【可視化】Plotlyで綺麗なグラフが描きたい


## これは
Plotlyは少ないコードで綺麗なグラフがかけますが, matplotlibばかり使っていてあまりよく知らなかったので調べながら入門してみた人の備忘録です

## 環境
Google colaboratory

## インストール
colabであればインストールはありませんが、必要な場合はpipでインストールできます

```python
pip install plotly
```

## データの準備
seabornですぐ使える時系列データがあったのでそれを使ってみます. 以下はデータの準備なので飛ばしてください



```python
import seaborn as sns
import pandas as pd
from calendar import month_name

month_name_mappings = {name[:3]: n for n, name in enumerate(month_name)}

# ただのデータの準備
df = sns.load_dataset('flights')
df["month"] = df.month.apply(lambda x: month_name_mappings[x])
df["year-month"] = pd.to_datetime(df.year.astype(str) + "-" + df.month.astype(str))
ts_data = df[["passengers", "year-month"]].set_index("year-month")["passengers"]
ts_data
```




    year-month
    1949-01-01    112
    1949-02-01    118
    1949-03-01    132
    1949-04-01    129
    1949-05-01    121
                 ... 
    1960-08-01    606
    1960-09-01    508
    1960-10-01    461
    1960-11-01    390
    1960-12-01    432
    Name: passengers, Length: 144, dtype: int64



月ごとの乗客数のシンプルなデータです. 

## 基本操作

グラフを作成してみます


```python
import plotly.graph_objects as go

fig = go.Figure()
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/f973cdec-dc95-3ea8-980d-d33e70b39523.png)



グラフができた. ここに色々追加していくのですね


```python
# 折れ線グラフ
fig = go.Figure()
fig.add_trace(go.Scatter(x=ts_data.index, y=ts_data.values, name="passengers"))
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/9ffd65a8-0499-20cc-8f44-f5bcfd5ece6b.png)


メモリを対数表示にしてみる


```python
# 折れ線グラフ
fig = go.Figure()
fig.add_trace(go.Scatter(x=ts_data.index, y=ts_data.values, name="passengers"))

# 見た目のカスタマイズはlayoutをいじる
fig.update_layout(yaxis_type="log")
fig.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/fd653383-1643-2934-a2b5-28ff2cb04dd9.png)

## 豪華なグラフを描きたい

もっと豪華にしたくなってきたので, 無理やり色々可視化してみます

- 月ごとにデータを分割?して, 各データの変化を折れ線グラフにする
- 年ごとの合計乗客数を棒グラフにしてみる


```python
# 少しデータをこねこねする
ts_data_monthly = df.groupby("month")["passengers"].apply(list)
ts_sum_yearly = df.groupby("year")["passengers"].sum()
```


```python
# 複数の折れ線グラフにしたい
ts_data_monthly
```




    month
    1     [112, 115, 145, 171, 196, 204, 242, 284, 315, ...
    2     [118, 126, 150, 180, 196, 188, 233, 277, 301, ...
    3     [132, 141, 178, 193, 236, 235, 267, 317, 356, ...
    4     [129, 135, 163, 181, 235, 227, 269, 313, 348, ...
    5     [121, 125, 172, 183, 229, 234, 270, 318, 355, ...
    6     [135, 149, 178, 218, 243, 264, 315, 374, 422, ...
    7     [148, 170, 199, 230, 264, 302, 364, 413, 465, ...
    8     [148, 170, 199, 242, 272, 293, 347, 405, 467, ...
    9     [136, 158, 184, 209, 237, 259, 312, 355, 404, ...
    10    [119, 133, 162, 191, 211, 229, 274, 306, 347, ...
    11    [104, 114, 146, 172, 180, 203, 237, 271, 305, ...
    12    [118, 140, 166, 194, 201, 229, 278, 306, 336, ...
    Name: passengers, dtype: object




```python
# 棒グラフにしたい
ts_sum_yearly
```




    year
    1949    1520
    1950    1676
    1951    2042
    1952    2364
    1953    2700
    1954    2867
    1955    3408
    1956    3939
    1957    4421
    1958    4572
    1959    5140
    1960    5714
    Name: passengers, dtype: int64




```python
# ラベル(x軸)
ts_labels = ts_sum_yearly.index
ts_labels
```




    Int64Index([1949, 1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959,
                1960],
               dtype='int64', name='year')



可視化してみます. layoutを少しいじってみました


```python
fig = go.Figure()

# 月ごとの時系列データは折れ線グラフにする
for month, passengers in ts_data_monthly.iteritems():
    fig.add_trace(go.Scatter(x=ts_labels, y=passengers, name=str(month)+"月", yaxis='y2'))

# 年ごとの合計は棒グラフにする
fig.add_trace(go.Bar(x=ts_labels, y=ts_sum_yearly.values, name="合計", yaxis="y1"))

# 見た目のカスタマイズはlayoutをいじる
# 軸が２つあるのでメモリは無しにする
fig.update_layout(
    title={
        "text": "乗客数時系列データ",
        "x":0.5,
        "y": 0.9
    },
    legend={
        "orientation":"h",
        "x":0.5,
        "xanchor":"center"
    },
    yaxis1={
        "title": "乗客数(合計)",
        "side": "left",
        "showgrid":False
    },
    yaxis2={
        "title": "乗客数(月ごと)",
        "side": "right",
        "overlaying": "y",
        "showgrid":False
    }
)
fig.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/ac899c33-3c3e-2b75-4568-f4b67c6e2529.png)

凡例をぽちぽちしたりできます. こんな少ないコードでちょっと動かせるグラフが書けるのは感動です...


## その他

ドーナッツ(かわいい)


```python
import numpy as np

ts_sum_monthly = df.groupby("month")["passengers"].sum()
# 一番乗客が多い月を切ったピザみたいにする
pull=[0]*12
pull[np.argmax(ts_sum_monthly.values)] = 0.2

fig = go.Figure()
fig.add_trace(go.Pie(
    labels=[str(month) + "月" for month in ts_sum_monthly.index],
    values=ts_sum_monthly.values,
    hole=.3,
    pull=pull
    )
)
fig.update_layout(
    title={
        "text": "乗客数の割合(1949~1960年の合計)",
        "x":0.5,
        "y": 0.9
    }
)
fig.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/bc830e00-fcdc-b555-6125-390c90daa778.png)

年ごとに合計乗客数求めて円グラフを作成


```python
from plotly.subplots import make_subplots

specs = [[{'type':'domain'}, {'type':'domain'}, {'type':'domain'}, {'type':'domain'}] for _ in range(3)]
# 3*4のグリッドにグラフを分割する
fig = make_subplots(rows=3, cols=4, specs=specs)

# ラベルの各円グラフのラベルのタイトルの位置
pos_x = [0.09, 0.37, 0.63, 0.91]
pos_y = [0.9, 0.5, 0.1]
annotations = []  

# (row, col)の位置に円グラフを載せる
row = 0
for i, (year, df_yearly) in enumerate(df.groupby(["year"])[["month","passengers"]]):
    pull=[0]*12
    pull[np.argmax(df_yearly.passengers.values)] = 0.2
    col = i%4+1
    if col == 1:
        row += 1
    annotations.append({
        "text": str(year)+"年",
        "x": pos_x[col-1],
        "y": pos_y[row-1],
        "font_size": 10,
        "showarrow":False
    })
    fig.add_trace(go.Pie(
        labels=[str(month) + "月" for month in df_yearly.month.values],
        values=df_yearly.passengers.values,
        name=str(year)+"年",
        hole=.3,
        pull=pull
    ),
    row, col
    )

fig.update_layout(
    title={
        "text": "乗客数の割合(年ごと)",
        "x":0.5,
        "y": 0.9
    },
    annotations=annotations
)
fig.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533290/661c1d04-aacd-7c31-9619-f4627929ce7b.png)


## 終わり
グラフが動くと嬉しいのは私だけでしょうか. 次回はplotlyでダッシュボードが作れる[dash](https://dash.plotly.com/)というWebフレームワークについてまとめたいと思います.

## 参考にさせていただいたサイト
+ [Python: 時系列データの交差検証と TimeSeriesSplit の改良について](https://blog.amedama.jp/entry/time-series-cv)
+ [[Python] Plotlyでぐりぐり動かせるグラフを作る](https://qiita.com/inoory/items/12028af62018bf367722)