0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

matplotlibでplotとfill_betweenを1つの凡例アイコンにまとめる方法

Posted at

はじめに

matplotlibでグラフを作成するとき、線グラフに信頼区間などの塗り帯(fill_between)を追加することはよくあります。
そうした場面で、「線と塗りを1つの凡例アイテムとしてまとめたい」というニーズが出てきました。

実際に使う機会があり、古い記事では少し冗長な方法が紹介されていましたが、色々と試してみたところシンプルに実装できる方法が見つかったので、備忘録としてまとめておきます。
同じことで悩んでいる方の参考になれば幸いです。

実装コードはGoogle Colab こちら からも閲覧できます。

方法

fill_betweenplot の戻り値をタプルにして legend() に渡すだけです。

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y = np.sin(x)
yerr = 0.2 * np.ones_like(y)

fig, ax = plt.subplots()

# 実際に描画:帯と線
line_patch, = ax.plot(x, y, color='tab:blue', lw=2)
fill_patch = ax.fill_between(x, y - yerr, y + yerr, color='tab:blue', alpha=0.3)

# タプルでまとめて1アイコンに
ax.legend([(fill_patch, line_patch)], ['sin(x) ± 0.2'])

plt.title("line + fill_between in one legend item")
plt.show()

出力画像
plot + fill_between の凡例アイコン例

ポイント

  • plot は線 (Line2D)、fill_between は帯 (PolyCollection)
  • タプルにして legend に渡すと、塗りと線が合成された凡例アイコンになる

補足:他の方法について

凡例で「帯+線」の1つのアイコンを作る方法は他にもあります。
たとえば以下の Stack Overflow では、同様の凡例アイコンの作成例が紹介されています。

Combined legend entry for plot and fill_between – Stack Overflow

ここでは fill_between をそのまま使うのではなく、fill を用いて透明な Patch を新たに作り、それと plot の線をタプルにして legend() に渡す方法が提案されています。

この挙動は matplotlib のバージョンによって微妙に異なる可能性もあり、筆者自身も完全には把握しきれていませんが、参考までに掲載します。

なお、この記事で紹介した方法は、matplotlib version 3.10.0 で動作確認を行いました。


以下では、凡例用にだけ描画オブジェクトを追加することで実現する方法を紹介します。
(先ほどのコードは、matplotlibのバージョンによって挙動が異なる可能性があるためです。)

方法①: mpatches.Patch を使う方法(凡例だけに表示)
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

x = np.linspace(0, 10, 100)
y = np.sin(x)
yerr = 0.2 * np.ones_like(y)

fig, ax = plt.subplots()

# 実際の描画
line, = ax.plot(x, y, color='tab:blue', lw=2)
ax.fill_between(x, y - yerr, y + yerr, color='tab:blue', alpha=0.3)

# 凡例用のダミーPatch
fill_patch = mpatches.Patch(color='tab:blue', alpha=0.3)

# タプルで1つの凡例アイコンに
ax.legend([(fill_patch, line)], ['sin(x) ± 0.2'])

plt.title("Using mpatches.Patch for legend")
plt.show()
方法②: fill で NaN を使ってダミーアーティストを作る方法(グラフには表示されず、凡例だけに反映される)
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 10, 100)
y = np.sin(x)
yerr = 0.2 * np.ones_like(y)

fig, ax = plt.subplots()

# 実際の描画
line, = ax.plot(x, y, color='tab:blue', lw=2)
ax.fill_between(x, y - yerr, y + yerr, color='tab:blue', alpha=0.3)

# 凡例用にfillでダミーを作成
fill_patch, = ax.fill([np.nan], [np.nan], color='tab:blue', alpha=0.3)

# タプルでまとめて凡例に
ax.legend([(fill_patch, line)], ['sin(x) ± 0.2'])

plt.title("Using ax.fill(NaN) for legend")
plt.show()

おわりに

凡例のカスタムって、少し手間がかかる印象がありましたが、実は plotfill_between の戻り値をタプルにするだけで、とてもシンプルに実現できることがわかりました。

グラフを“ちょっと映えさせたいとき”などに、ぜひ試してみてください。


少し話が脱線しますが、line_patch, = ax.plot()line_patch = ax.plot() の違いって、意外と気づきにくいですよね。私も最近まであまり意識していませんでした。

matplotlibって、そういう“小さな気づき”がたくさんあって、
知れば知るほど、さらに深みが見えてくるんですよね。

気がつけば、matplotlibまわりだけで一週間が終わっていたことも珍しくありません(笑)。

ほんと、matplotlibって、いい肴になります。
でも、多すぎて……正直、一人では昇華しきれません。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?