可視化ライブラリのBokehとHoloViewsを比較してみた

これまでインタラクティブなグラフの作成ではHoloViewsを使用していたのですが、会社で使用するのであれば有名どころを抑えておいた方が良さそう、という理由でBokehについても色々勉強してみました。
折角なので、各ライブラリの特徴や使用感などを比較しながら解説してみようと思います。

#### 目次
[１．BokehとHoloViews](#１bokehとholoviews)
[２．Bokehの基本](#２bokehの基本)
[３．HoloViewsの基本](#３holoviewsの基本)
[４．実際に書いて比較してみる 散布図編](#４実際に書いて比較してみる-散布図編)
　　[4a．Bokehによる散布図の作成](#4abokehによる散布図の作成)
　　　　[4a-1．Bokehでデータ系列を指定する](#4a-1bokehでデータ系列を指定する)
　　　　[4a-2．Bokehでグラフ外観の設定をする](#4a-2bokehでグラフ外観の設定をする)
　　　　[4a-3．Bokehで色の指定をする](#4a-3bokehで色の指定をする)
　　　　[4a-4．Bokehで要素（カラーバー）を追加する](#4a-4bokehで要素カラーバーを追加する)
　　[4b．HoloViewsによる散布図の作成](#4bholoviewsによる散布図の作成)
　　　　[4b-1．HoloViewsでデータ系列を指定する](#4b-1holoviewsでデータ系列を指定する)
　　　　[4b-2．HoloViewsでグラフ外観の設定をする（色指定、カラーバー追加含む）](#4b-2holoviewsでグラフ外観の設定をする色指定カラーバー追加含む)
[５．実際に書いて比較してみる 折れ線グラフ編](#５実際に書いて比較してみる-折れ線グラフ編)
　　[5a．Bokehによる折れ線グラフの作成](#5abokehによる折れ線グラフの作成)
　　　　[5a-1．Bokehで多系列の折れ線グラフを作成する](#5a-1bokehで多系列の折れ線グラフを作成する)
　　　　[5a-2．Bokehでカラーパレットをカスタマイズして適用する](#5a-2bokehでカラーパレットをカスタマイズして適用する)
　　[5b．HoloViewsによる折れ線グラフの作成](#5bholoviewsによる折れ線グラフの作成)
　　　　[5b-1．HoloViewsで多系列の折れ線グラフを作成する](#5b-1holoviewsで多系列の折れ線グラフを作成する)
　　　　[5b-2．HoloViewsでカラーパレットをカスタマイズして適用する](#5b-2holoviewsでカラーパレットをカスタマイズして適用する)
　　　　[5b-3．HoloViewsの複合グラフに各種オプションを設定する](#5b-3holoviewsの複合グラフに各種オプションを設定する)
[６．まとめ](#６まとめ)

# １．BokehとHoloViews

どちらも**グリグリ動かせるインタラクティブなグラフ:point_down_tone2:を作成できるPythonの可視化ライブラリ**です。
![holoviewデモ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/ae9b8954-c330-6fe0-a41b-161812e8483a.gif)
（これは[過去にHoloViewsを紹介した記事](https://qiita.com/hisakichi95/items/8be61af4e141d11a77d9)内で作成したグラフです）

公式ドキュメント
Bokeh　→　[https://docs.bokeh.org/en/latest/](https://docs.bokeh.org/en/latest/)
HoloViews　→　[https://holoviews.org/](https://holoviews.org/)

**Bokehはインタラクティブなグラフ作成ツールの有名どころ**で、**HoloViewsはBokeh等の可視化ライブラリをより簡単に使用できるようにつくられたラッパーツール**です。
尚、グラフ作成ライブラリの有名どころではPlotlyもありますが、こちらは有料プランがある等ビジネスの香りがするため、候補から外しています（会社で使用するのが怖い）。


# ２．Bokehの基本

Bokehは、最初に**figureというグラフの下地を用意して、そこにグラフや凡例などのオブジェクトを次々と書き足していきます**。
イメージとしてはMatplotlibに近い感じでしょうか。

例：折れ線グラフと散布図の描画
（ライブラリのインポートなどは省略）

```python
p = bokeh.plotting.figure(width=300, height=200)  # 描画領域生成

p.scatter(x=[1, 2, 3, 4], y=[1, 4, 9, 16])    # 散布図描画
p.line(x=[1, 2, 3, 4], y=[2, 4, 7, 11])       # 折れ線グラフ描画

display(show(p))    # 表示
```

　　　　↓
![02_Bokeh_basic_graph.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/090bffb7-f3eb-bc3d-7016-a43e45ded327.jpeg)
（何故かグラフの後にNoneと表示されてしまいます。現時点ではこれの消し方がまだ分かっていません）
デフォルト設定のデザインは、後述のHoloViewsよりやや洗練されている印象を持ちました。

# ３．HoloViewsの基本

（今回の記事ではライブラリインストールの説明は省いているのですが、HoloViewsの場合はバックエンドに指定するライブラリ（今回の場合Bokeh）もインストールが必要な点は一応注意が必要です）

HoloVeiwsでは、Scatter（散布図）やCurve（折れ線グラフ）をオブジェクトとして生成し、それらを重ね合わせて扱うイメージです。
**重ね合わせはオブジェクト同士を*（乗算記号）で掛け合わせるだけなので、シンプルで分かりやすい**のが特徴です。
（今回触れませんが、+（加算記号）で複数のグラフを並べて表示することもできます）

例：折れ線グラフと散布図の描画
（ライブラリのインポートやバックエンド指定などは省略）

```python
scatter = hv.Scatter(data=([1, 2, 3, 4], [1, 4, 9, 16]))  # 散布図のインスタンス生成
curve = hv.Curve(data=([1, 2, 3, 4], [2, 4, 7, 11]))      # 折れ線グラフのインスタンス生成

scatter = scatter.opts(width=300, height=200)    # 表示設定（片方のみでOK）

scatter * curve    # 表示
```

　　　　↓
![03_HoloViews_basic_graph.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/addb5312-b992-d7c2-1587-6904b6e92a34.jpeg)
デフォルトではグリッドが表示されないためか、デザイン的にちょっと物足りない印象です。
ただ、この辺りは簡単に設定可能です。詳細はこの後の実例を参照。

# ４．実際に書いて比較してみる 散布図編

ここからはPandasのDataFrameからグラフを作成するやり方で書いていきます。
<details><summary>使用するデータについて（スクレイピングあり）</summary><div>

Scatter編では、ポケモン（第8世代）の種族値でグラフを作成します。
今回の記事の本質ではないですが、スクレイピングのコードを自分用に残しておきます。

```python
# スクレイピング用
#ライブラリのインストール
import requests, bs4
import pandas as pd

# URLの指定
url=r"https://wiki.ポケモン.com/wiki/%E7%A8%AE%E6%97%8F%E5%80%A4%E4%B8%80%E8%A6%A7_(%E7%AC%AC%E5%85%AB%E4%B8%96%E4%BB%A3)"
# サーバにリクエストを送りレスポンスを取得
res = requests.get(url=url)
res.encoding = "utf-8"
# HTML解析
soup = bs4.BeautifulSoup(res.text, "html.parser")

# 先頭のテーブルデータ取得
table = soup.find_all("table",class_="bluetable")[0]
# テーブルからすべての行を取得
rows = table.find_all("tr")

table_data = []
for row in rows:

  # 各行のtd、thタグをすべて取得し、改行文字（\n）を削除した内包表記リストをテーブルデータに追加
  tmp_row = [cell.text.replace("\n","") for cell in row.find_all(["td", "th"])]
  table_data.append(tmp_row)

# リストからデータフレーム作成（一行目をカラム名に指定）
df = pd.DataFrame(table_data[1:], columns=table_data[0])

# int型へ変換可能なものは変換
for i, a_col in enumerate(df.iteritems()):
  try:
    df.iloc[:, i] = a_col[1].astype(int)
  # エラー名が分からない場合
  # except Exception as e:
  #   print(type(e))
  # 変換できなかった場合の処理
  except ValueError:
    print(f"列：{a_col[0]} はint型へ変換しませんでした。")

# 種族合計から散布図の各点のサイズを規定する系列sizeを算出・追加
df["size"] = 5 + ((df["合計"] - df["合計"].min())/(df["合計"].max() - df["合計"].min()) * 10)
```
　　　　↓
![04_scatter_data_image.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/bd6f9a65-c02a-b9ff-9c9a-4e48a0b125e0.jpeg)
</div></details>

## 4a．Bokehによる散布図の作成

コード

```python
# ライブラリのインポート
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, push_notebook
from IPython.display import display
from bokeh.transform import linear_cmap
from bokeh.palettes import Viridis256
from bokeh.models import LinearColorMapper, ColorBar

# GoogleColab（Jupyter）でグラフ表示するためのおまじない
output_notebook()

# 素早さの値から色に変換するカラー変換関数定義（グラフ描画時の色指定に必要）
linear_transform_color = linear_cmap(field_name="素早さ", palette=Viridis256, low= df["素早さ"].min(), high= df["素早さ"].max())

# グラフ描画領域生成
p = figure(title="ポケモン種族値", x_axis_label="攻撃", y_axis_label="防御",
           width=600, height=400)
# Scatter描画
p.scatter(x="攻撃", y="防御", source=df,
          fill_color=linear_transform_color,
          line_width=0, size="size", alpha=0.8)

# カラーマッパー定義（カラーバー表示のため必要、前述のカラー変換関数とは別物）
# color_mapper = linear_cmap["transform"]
color_mapper = LinearColorMapper(palette=Viridis256, low= df["素早さ"].min(), high= df["素早さ"].max())

# カラーバーの定義と表示
color_bar = ColorBar(color_mapper=color_mapper, title="素早さ")
p.add_layout(obj=color_bar, place="right")

# 表示
display(show(p))
```

　↓
![04_1_scatter_with_bokeh.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/5434d10c-a64b-2f34-a8c4-f79b62948ddb.jpeg)

モジュールのインポートやら、カラーパー表示回りやらでちょっと冗長なコードの印象でしょうか。
以下、ポイントの解説です。

#### 4a-1．Bokehでデータ系列を指定する

Bokehの場合、figureに対してメソッドでグラフを描画する際にデータ系列を指定します。


> \# Scatter描画
> p.scatter(<font color="Red">x="攻撃", y="防御", source=df,</font>
> 　　　　fill_color=linear_cmap,
> 　　　　line_width=0, <font color="Red">size="size"</font>, alpha=0.8)
> 　　:

**sourceに使用するDataFrameを指定し、xとyに表示するカラム名を指定**しています。
また、今回は各点のサイズにもDataFrameから参照しているので、ここでもカラム名を指定しています。
（sizeは種族値合計をいい感じに計算してサイズ値に変換したもので、事前にdfに追加しています）

#### 4a-2．Bokehでグラフ外観の設定をする

figureを生成する際や、メソッドでグラフを描画する際の引数で各種設定を行ないます。

> \# グラフ描画領域生成
> p = figure(<font color="Red">title=</font>"ポケモン種族値", <font color="Red">x_axis_label=</font>"攻撃", <font color="Red">y_axis_label=</font>"防御",
> 　　　　<font color="Red">width=</font>600, <font color="Red">height=</font>400)
> \# Scatter描画
> p.scatter(x="攻撃", y="防御", source=df,
> 　　　　<font color="Red">fill_color=</font>linear_cmap,
> 　　　　<font color="Red">line_width=</font>0, <font color="Red">size=</font>"size", <font color="Red">alpha=</font>0.8)
> 　　:

引数名はシンプルなので、細かい解説は省略します。

#### 4a-3．Bokehで色の指定をする

ここがBokehが面倒くさいと感じたポイントの一つです。
まず、「どの数値」を「どのように」変換して「どのカラーパレット」に適用するかを定めた色変換関数を定義する必要があります。
今回の場合

+ どの数値 ⇒ <font color="BlueViolet">素早さ</font>
+ どのように ⇒ <font color="Crimson">素早さのminを下限、maxを上限</font>とした<font color="DarkOrange">線形変換</font>
+ どのカラーパレット ⇒ <font color="ForestGreen">Viridis256</font>

で定義します。具体的には下記のように定義します。（↑と色合わせてみました）

> from <font color="DarkOrange">bokeh.transform</font> import <font color="DarkOrange">linear_cmap</font>
> from <font color="ForestGreen">bokeh.palettes</font> import <font color="ForestGreen">Viridis256</font>
> 　　：
> \# 素早さの値から色に変換するカラーマップ定義（グラフ描画時の色指定に必要）
> linear_transform_color = <font color="DarkOrange">linear_cmap(</font><font color="BlueViolet">field_name="素早さ"</font>, <font color="ForestGreen">palette=Viridis256</font><font color="Crimson">, low= df["素早さ"].min(), high= df["素早さ"].max()</font><font color="DarkOrange">)</font>
> 　　：

Palettesモジュールにどのようなカラーパレットがあるかは公式リファレンスの[bokeh.Palettes](https://docs.bokeh.org/en/latest/docs/reference/palettes.html)を参照。
後にも触れますが、パレット名は色系列+使用する色数を指定する必要があります。
そのため、使用する色数が変わればコードを書き直す必要があるなど、**Bokehは色に関してかなり不自由な印象**を持ちました。（一応それを回避する手段もあるようです）

あとは、定義した色変換関数をグラフ描画時に色のオプション（今回の場合fill_color）として指定するだけです。

> \# Scatter描画
> p.scatter(x="攻撃", y="防御", source=df,
> 　　　　<font color="Red">fill_color=linear_transform_color,</font>
> 　　　　line_width=0, size="size", alpha=0.8)

#### 4a-4．Bokehで要素（カラーバー）を追加する

要素を追加するには、**定義したオブジェクトをfigureにadd_layoutで追加**します。

ただ、カラーバーの場合少しややこしく、事前にカラーマッパーを定義する必要があります。このカラーマッパーは前述の色変換関数と似ていますが、別物のようです。
下記のようにbokeh.modelsからカラーマッパーをインポートしたのち、各種設定を引数に指定して定義します。

> from <font color="Red">bokeh.models</font> import <font color="Red">LinearColorMapper, </font>ColorBar
> 　　：
> \# カラーマッパー定義（カラーバー表示のため必要、前述のカラー変換関数とは別物）
> <font color="Red">color_mapper = LinearColorMapper(</font>palette=Viridis256, low= df["素早さ"].min(), high= df["素早さ"].max()<font color="Red">)</font>
> 

次にColorBarオブジェクトを生成します。このときcolor_mapper引数に先ほどのカラーマッパーを指定します。

> from <font color="Red">bokeh.models</font> import LinearColorMapper, <font color="Red">ColorBar</font>
> 　　：
> \# カラーバーの定義と表示
> <font color="Crimson">color_bar</font> = ColorBar(color_mapper=<font color="Red">color_mapper</font>, title="素早さ")

あとは、add_layoutメソッドで生成したカラーバーをグラフに追加するだけです。

> p<font color="Red">.add_layout(</font>obj=<font color="Crimson">color_bar</font>, place="right"<font color="Red">)</font>

カラーバーはBokehで要素を追加する例として紹介するにはやや複雑な例かもしれません。
どちらかというと折れ線グラフ編の凡例追加の方がシンプルで分かりやすいと思います。

次はHoloViewsでのグラフ作成実例です。
## 4b．HoloViewsによる散布図の作成

コード

```python
# ライブラリのインポート
import holoviews as hv

# GoogleColab（Jupyter）でグラフ表示するためのおまじない
hv.extension("bokeh")

# Scatterインスタンスの生成
scatter = hv.Scatter(data=df, kdims=["攻撃"], vdims=["防御", "素早さ", "size"])
# Scatterの描画オプション指定
scatter = scatter.opts(title="ポケモン種族値", show_grid=True, width=600, height=400, 
                       size="size", color="素早さ", cmap="viridis", alpha=0.8, 
                       colorbar=True, colorbar_opts={"title":"素早さ", "bar_line_color":None, "major_tick_line_color":"white"})

# 表示 
scatter
```

　　　　↓
![04_2_scatter_with_holoviews.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/d8818185-4a3a-621c-2e73-5f7266be60c4.jpeg)
Bokehと比べるとコードがかなりスッキリしています。

#### 4b-1．HoloViewsでデータ系列を指定する

HoloViewsでの**データ系列の指定は、x, yの代わりにkdims, vdimsをそれぞれ指定**します。


> \# Scatterインスタンスの生成
> scatter = hv.Scatter(<font color="Red">data=df, kdims=["攻撃"], vdims=["防御", "素早さ", "size"]</font>)
> 　　：

**kdims**
key_dimensionsの略。キーとなる要素。
Scatterの場合、**x軸に何を指定するかと捉えておいてOK**です。
dataに指定したデータのカラム名をリストで指定します。

**vdims**
value_dimensionsの略。キーに対応した値となる要素。
Scatterの場合、**y軸にあたる要素**はvdimsに指定します。
kdims同様、dataに指定したデータのカラム名を指定しますが、**vdimsに指定した値は色やサイズにも使用**できます。
リストで格納された要素のうち、先頭の要素がyとして扱われるようです。

#### 4b-2．HoloViewsでグラフ外観の設定をする（色指定、カラーバー追加含む）

HoloViewsにおける**グラフの各種設定には.optsメソッドを使用**します。

> \# Scatterの描画オプション指定
> scatter = scatter<font color="Red">.opts</font>(<font color="Red">title=</font>"ポケモン種族値", show_grid=True, <font color="Red">width=</font>600, <font color="Red">height=</font>400,
> 　　　　　　<font color="Red">size=</font>"size", <font color="Red">color=</font><font color="BlueViolet">"素早さ", </font><font color="ForestGreen">cmap="viridis", </font><font color="Red">alpha=</font>0.8, 
> 　　　　　　<font color="Red">colorbar=True, </font>colorbar_opts={<font color="Red">"title":</font>"素早さ", "bar_line_color":None, "major_tick_line_color":"white"})
> 　　：

上記のようにBokehでは面倒だった色やカラーバーの追加・設定も非常に簡素な記述で済みます。
基本的な使い方であれば**非常にシンプルに書けるのがHoloViewsのメリット**ですね。

# ５．実際に書いて比較してみる 折れ線グラフ編

<details><summary>使用するデータについて（スクレイピングなし）</summary><div>
折れ線グラフでは、厚労省が発表している日本の喫煙率の推移を描画します。
[https://www.health-net.or.jp/tobacco/product/pd100000.html](https://www.health-net.or.jp/tobacco/product/pd100000.html)
本当はコチラのデータもスクレイピングで取得したかったのですが、うまくいかず断念。素直にデータをエクセルにコピペして加工 ⇒ csvに保存してDataFrame（インスタンス名：df2）として読み込んでいます。
![05_lineplot_data_image.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/bfd0d8b2-2acc-cc03-8b77-c0b3a2de23bd.jpeg)
厚労省のサイトにあるグラフは、色に脈絡がなくて見づらいと感じたので、色系統と濃淡で色に意味合いを与えながら、グラフを描画していきたいと思います。
</div></details>

## 5a．Bokehによる折れ線グラフの作成

コード

```python
# ライブラリのインポート
from bokeh.plotting import figure, show
from bokeh.io import output_notebook, push_notebook
from IPython.display import display
from bokeh.models import Legend
from bokeh.palettes import Blues7, Reds7

# GoogleColab（Jupyter）でグラフ表示するためのおまじない
output_notebook()

# グラフ描画領域生成
p = figure(title="喫煙率の推移", x_axis_label="年次", y_axis_label="喫煙率", width=600, height=400)

# 凡例（グラフ外）の設定と描画
legend = Legend(location="center")
p.add_layout(obj=legend, place="right")

# 色の設定（palettesの中身はタプルのため結合可）
# 試用する色は6色*2だが、最薄の色が見づらいため7色用意してスライスで最薄を除外
color_list = Blues7[:-1] + Reds7[:-1]

# カラム名と色を同時に読み込み、カラム数分折れ線グラフ描画を繰り返す
for column_name, color in zip(df2.columns, color_list):
  p.line(x="index", y=column_name , source=df2, legend_label=column_name, line_width=2, color=color)

# 表示
display(show(p))
```

　　　　↓
![05_1_lineplot_with_bokeh.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/b34eb248-2043-0a0a-f371-cef245abcaae.jpeg)
モジュールインポート部は相変わらず冗長ですが、それ以外は比較的スッキリしたコードで書けます。

#### 5a-1．Bokehで多系列の折れ線グラフを作成する

複数の折れ線グラフの描画は、**for文で同じfigureに繰り返しプロット**するだけです。

> \# グラフ描画領域生成
> p = figure(title="喫煙率の推移", x_axis_label="年次", y_axis_label="喫煙率", width=600, height=400)
> 　　：
> \# カラム名と色を同時に読み込み、カラム数分折れ線グラフ描画を繰り返す
> <font color="Red">for column_name</font>, color in zip(<font color="Red">df2.columns</font>, color_list):
> 　　　　<font color="Red">p.line</font>(x="index", <font color="Red">y=column_name</font> , source=df2, <font color="Red">legend_label=column_name</font>, line_width=2, color=color)
> 　　：

legend_nameにもカラム名を指定することで、凡例の表示にカラム名をそのまま使用できます。

#### 5a-2．Bokehでカラーパレットをカスタマイズして適用する

Bokehで複数系統のカラーパレットを使用したい場合、あらかじめ意図したカラーパレット作成しておき、
そのパレットに基づいてグラフを描画する必要があります。

パレットの中身は表示してみると分かるのですが、`('#084594', '#2171b5', …)`のように16進数の色情報のタプルです。そのため、そのため普通のタプルと同様に結合したり、スライスで一部情報を取り出すことができます。

> 　　：
> from bokeh.palettes import <font color="Red">Blues7, Reds7</font>
> 　　：
> \# 色の設定（palettesの中身はタプルのため結合可）
> \# 試用する色は6色*2だが、最薄の色が見づらいため7色用意してスライスで最薄を除外
> <font color="Red">color_list = Blues7[:-1] + Reds7[:-1]</font>
> 　　：

そのようにして作成したパレットを、前述のfor文の中で取り出し、引数colorに一つずつ設定しています。

> \# カラム名と色を同時に読み込み、カラム数分折れ線グラフ描画を繰り返す
> for column_name, <font color="Red">color</font> in zip(df2.columns, <font color="Red">color_list</font>):
> 　　　　p.line(x="index", y=column_name , source=df2, legend_label=column_name, line_width=2, <font color="Red">color=color</font>)
> 　　：

このやり方の場合、**難点はやはり色の柔軟性が無い**こと。具体的には下記のようなデメリットがあります。

+ DataFrameのカラムの順番を把握したうえでカラーパレットを作らないといけない
+ カラムの数が変動したら呼び出すカラーパレット名から変更しないといけない
+ Bluesなどは最大でも9色のパレットしかない。

複雑なデータなどを可視化するときは工夫が必要そうです…。

## 5b．HoloViewsによる折れ線グラフの作成

コード

```python
# ライブラリのインポート
import holoviews as hv
from holoviews import opts

# GoogleColab（Jupyter）でグラフ表示するためのおまじない
hv.extension("bokeh")

# 各カラムをもとにした折れ線グラフを男女別に辞書作成（keyはカラム名）
dict_male = {col_name:hv.Curve(df2[col_name]) for col_name in df2.columns if "男性" in col_name}
dict_female = {col_name:hv.Curve(df2[col_name]) for col_name in df2.columns if "女性" in col_name}
# 辞書のグラフを男女それぞれ合算
male_graph = hv.NdOverlay(dict_male)
female_graph = hv.NdOverlay(dict_female)

# 男女それぞれのグラフに色を指定
# Palettesのrangeは色のどこからどこまでを使用するか。0-1の範囲のタプルで指定。
male_graph = male_graph.opts(opts.Curve(color=hv.Palette("Blues", range=(0, 0.8))))
female_graph = female_graph.opts(opts.Curve(color=hv.Palette("Reds", range=(0, 0.8))))

# 男女のグラフを合算
all_graph = male_graph * female_graph
# グラフの設定
all_graph = all_graph.opts(title="喫煙率の推移", xlabel="年次", ylabel="喫煙率",
                           width=600,height=400,legend_position="right", show_grid=True)

# 表示
all_graph
```

　　　　↓
![05_2_lineplot_with_holoviews.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/642673/bb48f2a2-8583-c262-f2ad-4d985851b4a5.jpeg)
こちらはBokehよりコードがやや長くなってしまいました。
ただ、一部は男女のデータで同じことを繰り返しているだけのため、実際には見た目ほどコード記述量はありません。

#### 5b-1．HoloViewsで多系列の折れ線グラフを作成する

Bokehのときと同じアプローチ（forで繰り返す）も可能ですが、今回はHoloViews独自の**NdOverlayを使って作成**します。

まず、重ねたいグラフをvalue、その時のグラフ名（系列名）をkeyにした辞書を作成します。
今回は辞書型の内包表記を使用しました。
（可読性が下がっている気がするので、正直for文で作ってしまっても良いと思います）

> \# 各カラムを系列にした折れ線グラフを男女別に辞書作成（keyはカラム名）
> dict_male = <font color="Red">{col_name:hv.Curve(df2[col_name]) for col_name in df2.columns if "男性" in col_name}</font>
> dict_female = <font color="Crimson">{col_name:hv.Curve(df2[col_name]) for col_name in df2.columns if "女性" in col_name}</font>
> 　　：

そして、作成した辞書をhv.NdOverlayの引数に指定します。すると、辞書のvalueがすべて合算された複合グラフが生成されます。

> \# 辞書のグラフを男女それぞれ合算
> male_graph = <font color="Red">hv.NdOverlay(</font>dict_male<font color="Red">)</font>
> female_graph = <font color="Crimson">hv.NdOverlay(</font>dict_female<font color="Crimson">)</font>
> 　　：

そして、生成したグラフはさらに重ねることができるので、最後に男女それぞれのグラフを乗算記号*で重ねています。

> \# 男女のグラフを合算
> all_graph = male_graph <font color="Red">*</font> female_graph


#### 5b-2．HoloViewsでカラーパレットをカスタマイズして適用する

color引数にパレット名を指定するだけでもOKなのですが、hv.Paletteメソッドを使用すればパレットのカスタマイズも可能です。
今回はrangeを指定して、カラーパレットの一部領域のみを使用するようにしています。
（例えばタプルで`(0,0.8)`を与えれば、全体を0～1とした場合の0～0.8を使用するようになる）

> \# 男女それぞれのグラフに色を指定
> \# Palettesのrangeは色のどこからどこまでを使用するか。0-1の範囲のタプルで指定。
> male_graph = male_graph.opts(opts.Curve(color=<font color="Red">hv.Palette("Blues", range=(0, 0.8))</font>))
> female_graph = female_graph.opts(opts.Curve(color=<font color="Crimson">hv.Palette("Reds", range=(0, 0.8))</font>))
> 　　：

#### 5b-3．HoloViewsの複合グラフに各種オプションを設定する

前述のNdOverlayや\*で合算したグラフは素の折れ線グラフとは扱いの異なる複合グラフとなります。
これらに前項で指定したような線の色など折れ線グラフ特有のオプションを設定しようとするとエラーになります。
**複合グラフに対し線の色などの各グラフ特有の設定を行なう場合、モジュールのoptsを使用**します。

> <font color="Red">from holoviews import opts</font>
> 　　：
> \# 男女それぞれのグラフに色を指定
> \# Palettesのrangeは色のどこからどこまでを使用するか。0-1の範囲のタプルで指定。
> male_graph = male_graph.opts(<font color="Red">opts.Curve(</font>color=hv.Palette("Blues", range=(0, 0.8))<font color="Red">)</font>)
> female_graph = female_graph.opts(<font color="Crimson">opts.Curve(</font>color=hv.Palette("Reds", range=(0, 0.8))<font color="Crimson">)</font>)
> 　　：

複合グラフ.optsの引数に、opts.グラフの種類名(オプションの中身）を指定します。

尚、全グラフ共通のような設定（title、widthなど）は複合グラフでもそのままoptsメソッドで指定可能です。

> \# グラフの設定
> all_graph = all_graph<font color="Red">.opts(</font>title="喫煙率の推移", xlabel="年次", ylabel="喫煙率",
> 　　　　　　　　width=600, height=400, legend_position="right", show_grid=True<font color="Red">)</font>

これで一通りのグラフ作成の説明は終了です。お疲れさまでした。
# ６．まとめ

### <font color="MediumBlu">Bokeh</font>

+ figureオブジェクトを生成し、そこにグラフや凡例などの要素を付け足していくイメージ
+ HoloViewsに比べるとコード量は増えてしまうが、それほど難解ではない
+ ただし、<font color="Red">**色回りの設定は何かと面倒**</font>

### <font color="MediumBlu">HoloViews</font>

+ グラフオブジェクトを生成した後、それを乗算記号（*）などで重ね合わせていくイメージ
+ <font color="DodgerBlue">**コードがすごくシンプルに書ける**</font>。色回りで悩むこともあまりない
+ dimensions（kdims、vdims）の概念が少し特殊

###### 所感

+ どちらのライブラリも日本語の情報量が限られており、英語のドキュメントを読み解くのに苦労しました<br>（英語力が乏しいので間違ってたこと書いていたらごめんなさい。。。）
+ HoloViewsで細かい設定をしていくのに、Bokehの知識が必要だったりするので、今回Bokehの理解が進んだことは今後HoloViewsを使っていく上でもプラスに働きそうです。