matplotlibで軸上にerrorbarをプロットする

環境

  • python 3.6.2
  • matplotlib 2.2.2

概要

matplotlibで軸上にerrorbarをプロットするとき、何も気にせずコードを書くと軸の上にerrorbarが乗らず微妙に上手くプロット出来ない。 これを回避するには、 set_on_clipzorder を適切に使用すると良い。

詳細

いろいろグダグダ書いてますが、結論としては最後に貼ったコードのように set_on_clipzorder を書けば良いです。

以下のようなコードを書いたとすると、

from matplotlib import pyplot as plt
import numpy as np

plt.style.use('ggplot')

# Generate data
num = 10
x = np.linspace(0, 1, num)
y = np.exp(x) + 1.4
std_y = [1 for i in range(num)]

# Plot
fig, ax = plt.subplots(figsize=(8, 6))
ax.errorbar(x, y, std_y, fmt='-^', elinewidth=1.5, capsize=5, clip_on=False)
ax.set_xlim([0, 1])

以下のようなグラフ(これは分かりやすいように拡大している)が得られる。 f:id:t-tatsukawa:20200622104735p:plain

実際は軸の上にerrorbarの線なりマーカーをプロットしたいわけで、今のままだと上手くいっていない。

これを解決するためには現状、

  1. 枠の外に出ていないこと
  2. 軸の上にプロットできていないこと

の2つの問題がある。

まず1つ目の問題を解決する。 ax.errorbar() の仕様( matplotlib.pyplot.errorbar — Matplotlib 3.1.2 documentation)を確認すると、この戻り値は ErrorbarContainer というオブジェクトを返すようである。

Returns: 
container : ErrorbarContainer
The container contains:

plotline: Line2D instance of x, y plot markers and/or line.
caplines: A tuple of Line2D instances of the error bar caps.
barlinecols: A tuple of LineCollection with the horizontal and vertical error ranges.

ErrorbarContainer は3つの要素

  1. plotline(マーカー・線)
  2. caplines(エラーバーのcaps(横線))
  3. barlinecols(エラーバーの縦線)

から構成されている。これらは全てmatplotlibの Artist と呼ばれるオブジェクトから構成されており(というか殆ど全てそうなのでこれはちょっと良くない記述だが)、Artist オブジェクトは set_on_clip と呼ばれるメソッドを持つので(matplotlib.artist.Artist.set_clip_on — Matplotlib 3.1.2 documentation)、caplinesとbarlinecolsについて artist.set_on_clip(False)とすれば枠の外に出ないという問題は解決する。

2つ目の軸の上にプロットされないという問題は、 ax.plot()zorder オプションを適切に設定すれば良い。 zorderの仕様(Zorder Demo — Matplotlib 3.2.1 documentation)を確認すると、

Artist Z-order
Images (AxesImage, FigureImage, BboxImage) 0
Patch, PatchCollection 1
Line2D, LineCollection (including minor ticks, grid lines) 2
Major ticks 2.01
Text (including axes labels and titles) 3
Legend 5

と書かれており、Z-orderの値が大きいほど上に表示される。軸(Major ticks)は2.01なのでこれより大きく値を設定すれば良い。

以上の2つの問題点を解決したコードは以下のようになる。

from matplotlib import pyplot as plt
import numpy as np

plt.style.use('ggplot')

# Generate data
num = 10
x = np.linspace(0, 1, num)
y = np.exp(x) + 1.4
std_y = [1 for i in range(num)]

# Plot
fig, ax = plt.subplots(figsize=(8, 6))
plotline, caplines, barlinecols = ax.errorbar(x, y, std_y, fmt='-^', elinewidth=1.5, zorder=5, capsize=5, clip_on=False)

for artist in caplines:
    artist.set_clip_on(False)
for artist in barlinecols:
    artist.set_clip_on(False)

ax.set_xlim([0, 1])

f:id:t-tatsukawa:20200622111703p:plain

参考