Matplotlibで凡例を分けて表示する：意味の違いを見せる小さな工夫

## はじめに

Matplotlibでは、`legend()` を使って系列の説明を図中に表示できます。通常は、すべての系列をまとめて1つの凡例に入れることが多いでしょう。

でもときどき、「これ、同じ凡例でいいのかな？」と迷う場面が出てきます。
たとえば：

* 観測データと理論線
* 実データと参照線
* twin axis（複数軸）を使う場合

こうしたとき、同じ箱に入れてしまうことで、かえって意味がぼやけてしまうことがあります。

そこで、本記事では複数の凡例を表示するシンプルな方法を紹介します。

> 本記事のコードはGoogle Colabでも動かせます
> [こちら](https://colab.research.google.com/drive/1eX78ns1LSwDOfd4zGORj9gD2F_Hln75K?usp=sharing)


## なぜ凡例を分けるのか？

凡例を1つにまとめた方がすっきりすることも多いですし、筆者自身、分けることは滅多にありません。

でも、以下のような状況では「分けた方が素直に伝わる」ことがあるように感じます。

* 異なる意味をもつ情報が、1つの図に共存しているとき
* twinxなどで軸が異なるとき
* 系列の位置に応じて補助説明をつけたいとき

“こうすべき”ではなく、“こうする選択肢もある”というスタンスで紹介したいと思います。


## 実装例：グループごとに凡例を分けて表示する

以下は、実線／破線の観測データと、それとは意味の異なる参照線が一緒に描かれたプロットです。

```python
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

# Group A: 観測データ（青）
line1, = ax.plot([0, 1], [0, 1], color='blue', linestyle='-', label='Solid Line')
line2, = ax.plot([0, 1], [1, 0], color='blue', linestyle='--', label='Dashed Line')

# Group B: 参照線（灰色）
line3, = ax.plot([0, 1], [0.5, 0.5], color='gray', linestyle='--', label='Reference Line')

# Group A の凡例（左上）
legend1 = ax.legend(handles=[line1, line2], loc='upper left', title='Group A')
ax.add_artist(legend1)

# Group B の凡例（右下）
ax.legend(handles=[line3], loc='lower right', title='Group B')

plt.show()
```
**出力結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/a8645c3d-f27d-432d-93df-07f9e6fc91e5.png)


`legend()` を複数回使うときは、最初の凡例を [`add_artist()`](https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.add_artist.html) で保持するのがポイントです。




## 実践編：twin axis（複数軸）と組み合わせる

次はもう少し実用的な例。左右の軸に異なる物理量を持つ twin axis のグラフでは、**軸ごとに凡例を分けると視覚的な整理になります**。

```python
import matplotlib.pyplot as plt

fig, ax_sales = plt.subplots()

# 左Y軸：売上（円）
days = [1, 2, 3]
sales = [10000, 15000, 12000]
target = [12000, 12000, 12000]

line1, = ax_sales.plot(days, sales, color='blue', label='Sales (¥)')
line2, = ax_sales.plot(days, target, color='blue', linestyle='--', label='Sales Target')

# 右Y軸：来店者数（人）
ax_customers = ax_sales.twinx()
customers = [100, 120, 90]
line3, = ax_customers.plot(days, customers, color='orange', marker='o', label='Customers')

# 左側の凡例（売上）
legend1 = ax_sales.legend(handles=[line1, line2], loc='upper left', title='Sales')
ax_sales.add_artist(legend1)

# 右側の凡例（顧客数）
ax_customers.legend(handles=[line3], loc='upper right', title='Customers')

# 軸ラベル
ax_sales.set_xlabel('Day')
ax_sales.set_ylabel('Sales (¥)')
ax_customers.set_ylabel('Number of Customers')

plt.show()
```
**出力結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/5e08f6c1-5f70-4dfb-bcd0-2cd30b0ee7e7.png)

左側に「Sales」、右側に「Customers」というように、情報が整理されていて、見た目がすっきりと整っています。


## 凡例を分けることで得られる効果

凡例の分割は、図に含まれた意味の違いを視覚的に示すための手段で、例えば以下のニーズで活用できると考えています。

| 状況                   | 分けるメリット             |
| -------------------- | ------------------- |
| 情報の種類が異なるとき          | 系列の役割が分かりやすくなる      |
| twin axis で軸が違うとき    | どの軸の情報か一目でわかる       |
| 系列が多い                | グループごとにまとまりがつきやすくなる |
| 論文・プレゼンなどで補助説明が必要なとき | 凡例そのものが“注釈”として機能する  |

図は単なるビジュアルだけでなく、**「どう伝えるか」の工夫の余地がある場所**だと最近は感じています。

## 凡例の見せ方は十人十色

ただ、グラフの見せ方は、本当に自由度の塊です。
その中でも凡例は、一見すると地味な存在かもしれませんが、場面によっては主役級の役割を担う、そんな存在だと感じています。

以前、Excelのセルのように行と列で情報がきれいに整理されているものを見て、
「凡例もこんなふうに配置できたらもっと美しく見えるのでは？」と思ったことがありました。

そのときに書いた記事がこちらです。もし興味があればあわせてご覧ください。

https://qiita.com/yusuke_s_yusuke/items/437d5844555903154725


## 参考

* [Matplotlib公式サンプルコード](https://matplotlib.org/stable/gallery/userdemo/simple_legend02.html#sphx-glr-gallery-userdemo-simple-legend02-py)

* [Stack Overflow: How to place two different legends on the same graph](https://stackoverflow.com/questions/12761806/how-to-place-two-different-legends-on-the-same-graph)


## おわりに

凡例は、“図と読み手のあいだにそっと佇む翻訳者”のような存在です。
どう読んでほしいか——プロットだけでは伝えきれない、そんな背景の意図を、
静かに、でも確かに言葉を借りて導いてくれます。

ただ、何事もバランスが大事なんですよね...
情報を一つに詰め込みすぎると、空回りして伝わりにくくなってしまいますし、
逆に分けすぎると、今度は焦点がぼやけてしまうこともありますよね。

これにはやはり正解があるわけではなく、「どんなふうに感じてほしいか」という読み手を意識した視点こそが、グラフをつくるうえで大切なのだと感じています。

<br>

凡例という、一見地味だけれど頼りになる存在について、
少しでも考えるきっかけになれば嬉しいです。