早く知っておきたかったmatplotlibの基礎知識、あるいは見た目の調整が捗るArtistの話

English version available on [dev.to](https://dev.to/skotaro/artist-in-matplotlib---something-i-wanted-to-know-before-spending-tremendous-hours-on-googling-how-tos--31oo)

# はじめに
matplotlibで作ったグラフの細かい調整は大変です。何をどういじったらいいのかを調べるのにアホみたいに時間がかかることもあります[^1]。「何を」の部分の名前さえわからないこともあります。解決の糸口を掴んだ後も希望通りの見た目を実現するまでの最後のアレンジに苦労することが多いです[^2]。これらの問題は __matplotlibのグラフがどういう要素で構成されていて、それらに対してどういうことができるかを知る__ ことでいくらか改善されます。私はひたすらStack Overflowの回答を読むことでいろんなつまづきを時間をかけて乗り越えてきましたが、最近になってようやく[公式チュートリアル](https://matplotlib.org/tutorials/index.html)にこの苦労を回避できたはずのヒントが書いてあることに気づきました。初期にざっと目を通したのですが「なるほど、よくわからん」と判断して読み込まなかった`Artist`に関する簡単な説明です。この記事で、新しいユーザーが私の経験したような無駄な苦労を回避できれば、あるいはすでにある程度なんとなくわかってきたユーザーが理解を深めてもらえたら嬉しいです。

# この記事の目的と内容
「こうしたい時はこうする」といった細かいノウハウではなく、いわゆる釣りの仕方（検索の際のキーワード選び）や釣った後のさばき方一般（検索で見つけた近い解法を自分向けにアレンジする際のヒント）に役立つアレコレについて述べます。ウェブに散らばる無数の断片的で対症療法的なメモ、tips、処方箋、レシピの内容がクリアになると思います。matplotlibベースであるSeabornやPandasのプロット機能を使っている人にとっても、グラフの細かな調整をする際に役立つはずです。

本記事の大部分はmatplotlib公式チュートリアルの[Artist tutorial](https://matplotlib.org/tutorials/intermediate/artists.html)と[Usage Guide](https://matplotlib.org/tutorials/introductory/usage.html)（執筆時バージョンは2.1.1）を簡単に日本語でまとめたものです。`Artist`に関して言及した日本語の記事は

- [matplotlib入門 - りんごがでている](http://bicycle1885.hatenablog.com/entry/2014/02/14/023734)、
- [基本テク — 読書ノート v1.4.0dev](https://showa-yojyo.github.io/notebook/python-matplotlib/basic.html)
- [matplotlibでのオブジェクト指向的な描画 - なまもの備忘録](http://purple-apple.hatenadiary.jp/entry/2017/02/05/204703)

などがありましたが、あまりわかりやすいものではなかったのでもう少し噛み砕いて書いてみました。

# こんな人向け
- [matplotlib入門 - りんごがでている](http://bicycle1885.hatenablog.com/entry/2014/02/14/023734) などをざっと読んで手元のデータをプロットできるようになったけれど、報告書や論文用の図を作るに当たって体裁を整えたくなってきた。その都度ググってなんとか自分のやりたいことを解決してくれるStack Overflowなどの回答を探し出せているが、__自分がやりたいことによく似てるけど少し違う回答しか見つからず、かゆいところの手前までしか届かない孫の手にむきーっとなっている。__
- この、なに、軸？枠？の太さを変えたいんだけど __名前がわからなくてやり方を探すのに苦労したことがある。あるいはあきらめた。__
- 言われるがままにやってみたら期待通りの図になったけど、__何をやっているか理解しきれていなくて気持ち悪いし応用が利かない。__
- 複数のやり方があるっぽいけど、__なんでそんなことになっているのかよくわからないしどれを採用すればいいのかわからない。__

# 釣りはどうでもいいからかゆいところまで届く孫の手が欲しい
あなたのかゆいところがどこかにもよりますが、Qiitaにはこういった孫の手がありました。

 - [matplotlibでジャーナルに投稿可能な図を作るためのメモ - Qiita](https://qiita.com/HajimeKawahara/items/03f29367744d5209be42)
 - [[Python] matplotlib: 論文用に図の体裁を整える - Qiita](https://qiita.com/rnhd/items/325d21621cfe9e553c17)

また、英語かつver. 1.3ベースの情報ですが、こんなのもありました。

 - [Handout for the tutorial "Creating publication-quality figures with matplotlib"](https://github.com/jbmouret/matplotlib_for_papers)

ただし、この後の内容を読むとこれらの孫の手も伸び縮みさせたり曲げられるようになるはずです。

# 準備
Python 3.6、matplotlib 2.1.1を使っています。

```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
```
Jupyter notebookのinlineコマンドを使っていることを前提にしているので、この後の例では`plt.show()`は省略されています。

# matplotlibにはグラフを作る際の二つの流儀がある
`Artist`の話の前に、新しいユーザーが絶対に知っておくべき`plt.plot`と`ax.plot`の違いについて述べます。公式チュートリアルでも [A note on the Object-Oriented API vs Pyplot](https://matplotlib.org/tutorials/introductory/lifecycle.html#a-note-on-the-object-oriented-api-vs-pyplot) や [Coding Styles](https://matplotlib.org/tutorials/introductory/usage.html#coding-styles) で言及されていますが、matplotlibでグラフを作るには二つの流儀（インターフェース）があります。公式ドキュメントを含めてネット上に大量にあるmatplotlibのコードにはこの二つが混在していますが、これらの違いについて明記してる例はあまり多くないように思えます。また、意味もなく二つを混ぜて使っている例も多く、これが多くの初心者のつまづきの原因になっていると思います。初めて"matplotlib プロット 方法"で検索した時のことを思い出してください。 __「`plt.なんとか`で全部済んでる例があるのになんで`ax.plot`とか変なのがいろいろ出てくる例もあるの？ていうかこの`ax`使ってなくない？バカなの？」__ と思いませんでしたか？私は思いました。ここではそれぞれの流儀の違いについて述べるだけです。具体的な使い方を知りたい方は以下を参照してください。

 - [Tutorials > Introductory > The Lifecycle of a plot](https://matplotlib.org/tutorials/introductory/lifecycle.html)
 - [Tutorials > Introductory > Pyplot tutorial](https://matplotlib.org/tutorials/introductory/pyplot.html)
 - [matplotlib入門 - りんごがでている](http://bicycle1885.hatenablog.com/entry/2014/02/14/023734)

## オブジェクト指向インターフェース
`fig, ax = plt.subplots()`などの後に`ax.plot`などを使う流儀です。`fig`や`ax`はこの記事で説明する`Artist`と呼ばれるオブジェクトの一種です。一番シンプルな例はこんな感じです。

```python
fig, ax = plt.subplots()
ax.plot(x,y)
```
```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(x, y)
```

`fig = plt.gcf()`や`ax = plt.gca()`もありますが、これらは主にPyplotインターフェースからオブジェクト指向インターフェースに切り替える時に使います。前述した「二つの流儀を混ぜて使っている例」はこれらを無自覚に使っているコードのことです。ユーザー自身に二つの流儀を切り替えているという自覚があるなら問題ないのですが、二つの流儀について認識していない初心者が読むと余計な疑問が生まれてしまいます。`plt.subplots`や`fig.add_subplot`を使って最初からオブジェクト指向インターフェースを使うことを勧めます。

## Pyplotインターフェース
`plt.なんとか`で全部済ませる流儀です。matplotlibの元となったMATLABを模した流儀だそうです。オブジェクト指向方式のように何を操作の対象にするか明示的に指定しなくとも、current figureやcurrent axes[^3]と呼ばれるオブジェクトを自動で設定してくれます[^4]。

```python
# https://matplotlib.org/tutorials/introductory/pyplot.html より
def f(t):
    return np.exp(-t) * np.cos(2*np.pi*t)

t1 = np.arange(0.0, 5.0, 0.1)
t2 = np.arange(0.0, 5.0, 0.02)

plt.figure(1)
plt.subplot(211)
plt.plot(t1, f(t1), 'bo', t2, f(t2), 'k')

plt.subplot(212)
plt.plot(t2, np.cos(2*np.pi*t2), 'r--')
plt.show()
```
<img width="440" alt="sphx_glr_pyplot_007.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/b44d1d6f-3d96-f0ba-7220-973310425140.png">

[Tutorials > Pyplot tutorial](https://matplotlib.org/tutorials/introductory/pyplot.html) で作ってる図を見るとわかる通り、運良くデフォルト設定でもグラフの細かい部分の見栄えに問題ない場合はそこそこのものができます。ただ、オブジェクト指向という概念についてよく理解していない段階でこの流儀に慣れてしまうと、のちほど必ず混乱すると思います。私はしました。また、ある程度の段階までPyplotで図を作っても、いざ細かい調整をしようとすると、結局オブジェクト指向インターフェースのやり方に従うことになります。__細かい調整が必要ない非常に簡素なグラフで事足りる時、あるいはちゃちゃっと可視化して何かを確認したい時には使えますが、人に見せる図を作る際は必ず微調整したい部分がでてくるので、早いうちからオブジェクト指向インターフェースに慣れたほうが良いです。__

# `Figure`, `Axes`, `Axis`は階層構造になっている
やりたいことがでてくるたびにググっていると、オブジェクト指向についてよく知らなくてもなんとなくmatplotlibには`fig`, `ax`などと表記される階層構造のようなものがあることがわかってくると思います。最新のドキュメントからは削除されてしまったようですが、[Matplotlib 1.5.1のFAQ > Usage](https://matplotlib.org/1.5.1/faq/usage_faq.html) にあった以下の図が最低限把握しておきたいmatplotlibの階層構造を簡潔に表しています。

<img width="250" alt="fig_map.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/1e675340-79ff-4aa1-1891-b79d3c369eb3.png">

後ほど述べる`Tick`が含まれていない図ですが、階層構造の理解には十分です。この図から以下の二点が読み取れます。
 - `Figure`オブジェクトに`Axes`オブジェクトが属している
 - `Axes`オブジェクトには`Axis`オブジェクトが属している

この辺の話は日本語だと以下のページでも述べられています。

 - [matplotlibのArtistの階層構造 - keisukeのブログ](http://kaisk.hatenadiary.com/entry/2014/12/01/005809)
 - [matplotlibの階層構造を知ると幸せになれる(かもしれない) - Qiita](https://qiita.com/ceptree/items/5fb5e9e6f29d214153c9)

この階層構造を知っていると前述したシンプルな例で最初にやっていることの意味が理解できます[^5]。

```python
fig, ax = plt.subplots() # Figureオブジェクトとそれに属する一つのAxesオブジェクトを同時に作成
ax.plot(x,y)
```

```python
fig = plt.figure() # Figureオブジェクトを作成
ax = fig.add_subplot(1,1,1) # figに属するAxesオブジェクトを作成
ax.plot(x, y)
```

試しに何もプロットせずに`Figure`と`Axes`のみを作って、それぞれの関係を示す属性を`print`で表示して見ます。

```python
fig = plt.figure()
ax = fig.add_subplot(1,1,1) # 何もプロットしていないAxesでもAxisは自動的に作られる
print('fig.axes:', fig.axes)
print('ax.figure:', ax.figure)
print('ax.xaxis:', ax.xaxis)
print('ax.yaxis:', ax.yaxis)
print('ax.xaxis.axes:', ax.xaxis.axes)
print('ax.yaxis.axes:', ax.yaxis.axes)
print('ax.xaxis.figure:', ax.xaxis.figure)
print('ax.yaxis.figure:', ax.yaxis.figure)
print('fig.xaxis:', fig.xaxis)
```

```:output
fig.axes: [<matplotlib.axes._subplots.AxesSubplot object at 0x1167b0630>]
ax.figure: Figure(432x288)
ax.xaxis: XAxis(54.000000,36.000000)
ax.yaxis: YAxis(54.000000,36.000000)
ax.xaxis.axes: AxesSubplot(0.125,0.125;0.775x0.755)
ax.yaxis.axes: AxesSubplot(0.125,0.125;0.775x0.755)
ax.xaxis.figure: Figure(432x288)
ax.yaxis.figure: Figure(432x288)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-21-b9f2d5d9fe09> in <module>()
      9 print('ax.xaxis.figure:', ax.xaxis.figure)
     10 print('ax.yaxis.figure:', ax.yaxis.figure)
---> 11 print('fig.xaxis:', fig.xaxis)

AttributeError: 'Figure' object has no attribute 'xaxis'
```

ここからわかるのは以下の関係です。`AxesSubplot`は`plt.subplots`で作った場合の`Axes`オブジェクトと思って問題ありません。

 - `Figure`は自分の下の`Axes`を知っているけどその下の`Axis`は知らない
 - `Axes`は自分の上の`Figure`と下の`Axis`を知っている
 - `Axis`は自分の上の`Axes`とその上の`Figure`を知っている
 - `Figure`は複数の`Axes`を持てる（`print`で表示されているのが[]で囲まれたリストなので）
 - `Axes`は一つの`Figure`にしか属せない（`print`で表示されているのがリストではないので）
 - `Axes`は`XAxis`と`YAxis`を一つずつしか持てない（同上）
 - `XAxis`と`YAxis`は一つの`Axes`、一つの`Figure`にしか属せない（同上）

# グラフに表示されているものは全て`Artist`
執筆時の最新版2.1.1の [Tutorials > Introductory > Usage Guide](https://matplotlib.org/tutorials/introductory/usage.html) には、階層構造の代わりにグラフの構成要素を細かく示した"解剖図"[^6]が掲載されています[^7]。これを見ると「この、なに、軸？枠？」の部分はSpinesと呼ばれていることがわかります。

<img width="440" alt="anatomy1.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/fb5b0e8d-88f4-eb9f-11cc-8509006fdbe9.png">

データを示す線や点、x軸やy軸、描画領域を表す枠や文字などグラフに表示される全ての要素は`Artist`と呼ばれます。`Artist`はcontainer（容器）とprimitive（原始的なものという意味）の二種類に分類されます。先の階層構造で示した`Figure`、`Axes`、`Axis`はcontainerに、プロットの線(`Line2D`)や点(`PathCollection`)あるいは文字(`Text`)はprimitiveに相当します。解剖図にあるtickやlabelは名前こそ違いますが実体は`Line2D`と`Text`オブジェクトです。これらの関係を簡単に図示すると以下のようになります。各primitiveを生成する`Axes`メソッドの例を右側に示しました。

<img width="440" alt="スクリーンショット 2018-01-11 1.00.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/a0699098-f2f1-7585-6e46-91b400048912.png">

前項のサンプルコードの結果を見るとわかりますが、`Axis`は実際には`XAxis`と`YAxis`という名前です。`Axis`の下にはさら`Tick`という目盛り関連の線や文字のためのcontainerがあります。containerはその名の通りprimitiveを入れる箱を持っています。このとき、containerの階層構造にあった「一つしか持てない」という制限はなく、いくつでもおなじprimitiveを持つことができます。例えば何もプロットしていない`ax`で`Line2D`オブジェクトを入れる箱`ax.lines`を見ると空リストが表示されます。`ax.plot`は他の細々した設定とともにこのリストに`Line2D`オブジェクトを追加していきます。

```python
x = np.linspace(0, 2*np.pi, 100)

fig = plt.figure() # Figureを作成
ax = fig.add_subplot(1,1,1) # Axesを作成
print('ax.lines before plot:\n', ax.lines) # Axes.linesは空リスト
line1, = ax.plot(x, np.sin(x), label='1st plot') # Axes.linesにLine2Dを追加+その他の設定
print('ax.lines after 1st plot:\n', ax.lines)
line2, = ax.plot(x, np.sin(x+np.pi/8), label='2nd plot') # Axes.linesにLine2Dを追加+その他の設定
print('ax.lines after 2nd plot:\n', ax.lines)
ax.legend()
print('line1:', line1)
print('line2:', line2)
```

```:output
ax.lines before plot:
 []
ax.lines after 1st plot:
 [<matplotlib.lines.Line2D object at 0x1171ca748>]
ax.lines after 2nd plot:
 [<matplotlib.lines.Line2D object at 0x1171ca748>, <matplotlib.lines.Line2D object at 0x117430550>]
line1: Line2D(1st plot)
line2: Line2D(2nd plot)
```

![download-9.png](https://qiita-image-store.s3.amazonaws.com/0/222849/d018f05c-db51-f999-796e-82a73603f7a6.png)

四つのcontainerが持てる`Artist`を以下にまとめます。

## `Figure`
ほかのcontainerでも同じですが`Figure`に属する`Artist`はそれぞれに対応する箱に入っていると考えるとよいです。箱は属性（attribute）を呼ぶことで中身を見れます。

|`Figure`属性|内容|
|:----------|:------|
|`fig.axes`   |`Axes`オブジェクトのリスト（`AxesSubplot`も含む）|
|`fig.patch`  |`Figure`全体の背景となる`Rectangle`オブジェクトの長方形一つだけ。|
|`fig.images` |`FigureImages`のリスト （useful for raw pixel displayとあるけどよくわからない）|
|`fig.legends`|`Figure`に属する`Legend`オブジェクトのリスト（`Axes.legends`とは異なる。下の記述参照。）|
|`fig.lines`  |`Figure`に属する`Line2D`オブジェクトのリスト （あまり使わない。`Axes.lines`参照）|
|`fig.patches`|`Figure`に属するpatchのリスト（ようは図形。ほとんど使わない。`Axes.patches`参照)|
|`fig.texts`  |`Figure`に属する`Text`オブジェクトのリスト|

`fig.axes`だけは意味合いが若干異なりますが、属性名が単数形だと一つだけ、複数形だとリストになっていることがわかります。これはこの後の例でも同じです。上の表にわざわざ「`Figure`に属する」と書いたのには理由があります。グラフに図形やテキストなどの`Artist`を追加するときに、 __`Figure`に追加するか`Axes`に追加するかによって座標の原点が異なる__ からです。どちらを使うのが適切かはやりたいことや実装の方法によって変わるでしょう。

### `fig.legend`と`ax.legend`
ところで、`fig.legends`は[`fig.legend`メソッド](https://matplotlib.org/api/_as_gen/matplotlib.figure.Figure.html#matplotlib.figure.Figure.legend)によって追加された凡例を入れる箱です。「`ax.legend`があるじゃん」と思いますよね。凡例を追加する際によく使われる`ax.legend`メソッドは、その`ax`に所属する`Artist`からしか凡例を作ってくれません。一方、 __`fig.legend`メソッドは所属する全ての`Axes`の`Artist`から凡例をかき集めて一括表示してくれます__。例えば左右の軸に異なるスケールのプロットをした時に素直に`ax.legend`を使うと凡例が二つできていしまいます。ちなみに`c='C1'`については[こちら](https://qiita.com/skotaro/items/5c9893d186ccd31f459d)を参照してください。

```python
x = np.linspace(0, 2*np.pi, 100)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(x, np.sin(x), label='sin(x)')
ax1 = ax.twinx()
ax1.plot(x, 2*np.cos(x), c='C1', label='2*cos(x)') 

ax.legend()
ax1.legend()
```
![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/976663e9-0273-c254-dfb4-27284b57bd41.png)

これは多くの場合望ましくはないでしょう。凡例を一つにまとめるには、最後の`ax.legend()`の代わりに、`ax`と`ax1`の凡例のhandlerとlabelのリストを結合して`ax.legend`に渡すという有名な解決法があります。

```python
handler, label = ax.get_legend_handles_labels()
handler1, label1 = ax1.get_legend_handles_labels()
ax.legend(handler+handler1, label+label1, loc='upper center', title='ax.legend')
# ax1.legendは更新されないのでそのまま残る
fig
```
![download.png](https://qiita-image-store.s3.amazonaws.com/0/222849/7f269d86-3e6f-658e-70f3-d2e75b239d2a.png)


実は、[ver 2.1から](https://matplotlib.org/users/whats_new.html#figure-legend-can-be-called-without-arguments)は引数なしの`fig.legend`メソッドを使ってhandlerやlabelを気にせずもっとシンプルに書けるようになっています[^8]。デフォルトでは`Figure`の座標が使われますが、凡例の配置は`Axes`座標ベースの方が都合の良い場合が多いです。下の例のように`bbox_transform`オプションを使うと`ax`に対する座標系に変更できます。

```python
fig.legend(loc='upper right', bbox_to_anchor=(1,1), bbox_transform=ax.transAxes, title='fig.legend\nax.transAxes')
fig
```
![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/19b95779-d445-b09e-2ad9-7f0fb3386313.png)

## `Axes`
グラフの見た目の調整は`Axes`とその下の`Axis`と`Tick`が主な舞台です。これらについて例を挙げながら述べます。以下は`Axes`が`Artist`を保持する箱です。「`Axes`に属する」のは自明なので省略しました。

| `Axes` 属性 | 説明 |
|:--|:--|
| `ax.artists` | `Artist`オブジェクトのリスト|
| `ax.patch` | `Axes`の背景となる`Rectangle`オブジェクト一つ|
| `ax.collections` | `Collection`オブジェクトのリスト|
| `ax.images` | `AxesImage`オブジェクトのリスト|
| `ax.legends` | `Legend`オブジェクトのリスト（2.1.1ではなくなった模様）|
| `ax.lines` | `Line2D`オブジェクトのリスト|
| `ax.patches` | `Patch`オブジェクトのリスト|
| `ax.texts` | `Text`オブジェクトのリスト|
| `ax.xaxis` | `XAxis`オブジェクト一つ|
| `ax.yaxis` | `YAxis`オブジェクト一つ|

プロットしたり二次元データを描画する際に使う`ax.plot`や`ax.imshow`などは、これらのcontainerに対応するオブジェクトを追加すると同時にいろいろな処理も一緒にしてくれるヘルパーメソッドと呼ばれてます。主なプロット関連のヘルパーメソッドは以下です。

|ヘルパーメソッド|生成される`Artist`|格納場所|
|:--|:--|:--|
|`ax.annotate`|`Annotate`|`ax.texts`|
|`ax.bar`|`Rectangle`|	`ax.patches`|
|`ax.errorbar`|`Line2D` & ~~`Rectangle`~~ `LineCollection`|`ax.lines` & ~~`ax.patches`~~ `ax.collections`|
|`ax.fill`|`Polygon`|	`ax.patches`|
|`ax.hist`|`Rectangle`|	`ax.patches`|
|`ax.imshow`|`AxesImage`|`ax.images`|
|`ax.legend`|`Legend`|	`ax.legends`|
|`ax.plot` |`Line2D`|	`ax.lines`|
|`ax.scatter`|`PathCollection`|	`ax.collections`|
|`ax.text`|`Text`|	`ax.texts`|

以下の例では`ax.plot`と`ax.scatter`で`Line2D`オブジェクトと`PathCollection`オブジェクトが対応したリストに追加される様子がわかります。

```python
x = np.linspace(0, 2*np.pi, 100)

fig = plt.figure() # Figureを作成
ax = fig.add_subplot(1,1,1) # Axesを作成
print('ax.lines before plot:\n', ax.lines) # Axes.linesは空リスト
line1, = ax.plot(x, np.sin(x), label='1st plot') # Axes.linesにLine2Dを追加
print('ax.lines after 1st plot:\n', ax.lines)
line2, = ax.plot(x, np.sin(x+np.pi/8), label='2nd plot') # Axes.linesにLine2Dを追加
print('ax.lines after 2nd plot:\n', ax.lines)
print('ax.collections before scatter:\n', ax.collections)
scat = ax.scatter(x, np.random.rand(len(x)), label='scatter') # Axes.collectionsにPathCollectionを追加
print('ax.collections after scatter:\n', ax.collections)
ax.legend()
print('line1:', line1)
print('line2:', line2)
print('scat:', scat)
ax.set_xlabel('x value')
ax.set_ylabel('y value')
```
```:output
ax.lines before plot:
 []
ax.lines after 1st plot:
 [<matplotlib.lines.Line2D object at 0x1181d16d8>]
ax.lines after 2nd plot:
 [<matplotlib.lines.Line2D object at 0x1181d16d8>, <matplotlib.lines.Line2D object at 0x1181d1e10>]
ax.collections before scatter:
 []
ax.collections after scatter:
 [<matplotlib.collections.PathCollection object at 0x1181d74a8>]
line1: Line2D(1st plot)
line2: Line2D(2nd plot)
scat: <matplotlib.collections.PathCollection object at 0x1181d74a8>
```

![download.png](https://qiita-image-store.s3.amazonaws.com/0/222849/85205e9e-8a62-4501-127e-d16fc6b4871b.png)

### プロットしたオブジェクトの使いまわしは非推奨
「プロットで生成されたオブジェクトはリストに追加される」ということを知ると、「ある`Axes`でプロットした`Line2D`オブジェクトを他の`Axes`の`lines`属性に追加すればプロット（線）を使いまわせるのかな？」と思いがちですが、これはArtist tutorialの [Axes container](https://matplotlib.org/tutorials/intermediate/artists.html#axes-container) の項目でわざわざ明記されている通りやるべきではありません。`ax.plot`は`ax.lines`に`Line2D`オブジェクトを追加する他にもいろいろとやっているからです。試しにやってみてもうまく表示されません。

```python
x = np.linspace(0, 2*np.pi, 100)

fig = plt.figure()
ax1 = fig.add_subplot(2,1,1) # 上のsubplot
line, = ax1.plot(x, np.sin(x), label='ax1 line') # Line2Dオブジェクト
ax1.legend()

ax2 = fig.add_subplot(2,1,2) # 下のsubplot
ax2.lines.append(line) # 空リストに要素を追加
```

![download-1.png](https://qiita-image-store.s3.amazonaws.com/0/222849/9d204e49-dda9-81f6-4e50-61b04f193922.png)

また、`Line2D`を`Axes`に追加する際に必要な設定をいろいろやってくれる`Axes.add_line`というメソッドを試してみるとエラーになります。

```python
ax2.add_line(line)
```
```:output
ValueError: Can not reset the axes.  You are probably trying to re-use an artist in more than one Axes which is not supported
```

このエラーメッセージから、おそらく __一度どれかのcontainerに格納された`Artist`を他のcontainerに使い回すことはできない__ のだろうことが予想されます。これは`Figure`と`Axes`と`Axis`の階層構造のところで調べた上下関係とも矛盾しませんし、各`Artist`には所属するcontainerが属性として登録（紐付け）されていることとも合致します。

```python
print('fig:', id(fig)) # FigureのオブジェクトID
print('ax1:', id(ax1)) # AxesのオブジェクトID
print('line.fig:', id(line.figure)) # lineが属するFigure
print('line.axes:', id(line.axes)) # lineが属するAxes
```
```:output
fig: 4707121584
ax1: 4707121136
line.fig: 4707121584
line.axes: 4707121136
```

この辺を全てクリアできるなら使いまわしも可能でしょうが、当初の「リストに追加すればよい？」という思いつきの手順からは相当逸脱するので避けるのが妥当でしょう。

## `Axis`
`Axis`は`Axes`ほどいろんなものを描画する必要がないので、軸のラベルの文字とtick関連の情報程度しか持っていません。しかし、 __デフォルトのままでは微調整したくなる見た目であることが多いわりに、細かい設定の仕方がわかりにくいグラフのパーツの代表格__ とも言ってよいでしょう。この説明を読むといろいろなレシピの理解がいくらか深まると思います。

[公式チュートリアル](https://matplotlib.org/tutorials/intermediate/artists.html#axis-containers)には`Figure`や`Axes`にあったような表がないので、同様のものを自分で作って見ました。

| `Axis` 属性 | 説明 |
|:--|:--|
| `Axis.label` | 軸のラベルになる`Text`オブジェクト一つ|
| `Axis.majorTicks` | major tickのラベルや位置、線を管理する`Tick`オブジェクトのリスト|
| `Axis.minorTicks` | minor tickのラベルや位置、線を管理する`Tick`オブジェクトのリスト|

`Axes`の例の最後で`ax.set_xlabel`と`ax.set_ylabel`で軸のラベルを設定しました。これは`ax`をいじっているように見えますが、実は`ax`に属する`XAxis`の`label`属性を変更しています。先ほどのplot2回とscatter1回を描画した例の`XAxis`で上の表の内容を確認します。

```python
xax = ax.xaxis # XAxisオブジェクトを取得
print('xax.label:', xax.label) # ax.set_xlabelで設定したラベル
print('xax.majorTicks:\n', xax.majorTicks) # 0から6の7本+図の外側の見えない2本
print('xax.minorTicks:\n', xax.minorTicks) # 図の外側の見えない2本
```
```:output
xax.label: Text(0.5,17.2,'x value')
xax.majorTicks:
 [<matplotlib.axis.XTick object at 0x117ae4400>, <matplotlib.axis.XTick object at 0x117941128>, <matplotlib.axis.XTick object at 0x11732c940>, <matplotlib.axis.XTick object at 0x1177d0470>, <matplotlib.axis.XTick object at 0x1177d0390>, <matplotlib.axis.XTick object at 0x1175058d0>, <matplotlib.axis.XTick object at 0x1175050b8>, <matplotlib.axis.XTick object at 0x117bf65c0>, <matplotlib.axis.XTick object at 0x117bf6b00>]
xax.minorTicks:
 [<matplotlib.axis.XTick object at 0x117ab5940>, <matplotlib.axis.XTick object at 0x117b540f0>]
```

### `ax.set_***`はその場しのぎの調整

`Axes`にはその下の`Axis`や`Tick`を設定するための`ax.set_***`系のヘルパーメソッドがたくさんあります。ただ、どれも __一度設定するとその後の変更に自動的に対応してくれない静的な変更__ になります。`xlabel`の場合は特に問題ありませんが、例えば、以下のように`ax.set_xticks`で一本目のプロットのx範囲に合わせてtickの位置を変えたあとに、その範囲を超えるプロットを追加してもxticksはいい感じに調整されません。

```python
x = np.linspace(0, 2*np.pi, 100)

fig = plt.figure()
ax = fig.add_subplot(1,1,1)
line1, = ax.plot(x, np.sin(x), label='')
ax.set_xticks([0, 0.5*np.pi, np.pi, 1.5*np.pi, 2*np.pi]) # line1のxの範囲に合わせてxticksを設定
line2, = ax.plot(1.5*x, np.sin(x), label='') # line1よりも広いx範囲にプロット
```

![download-2.png](https://qiita-image-store.s3.amazonaws.com/0/222849/0eec7ffa-9f20-9520-590f-9e6d41e9b1a9.png)


### 軸の見た目の調整はできれば`Formatter`と`Locator`で
`Axis`は`Artist`の他に`Formatter`と`Locator`と呼ばれる軸の見た目の調整には欠かせない地味に重要なものが設定されています。これらはプロットするデータに応じて軸の範囲をスケールする際にtickの位置やtickラベルのフォーマットを自動的に調整する役割を担っています[^9]。`ax.set_xticks`を使った上の例で`XAxis`と`YAxis`の`Formatter`と`Locator`を確認してみます。

```python
xax = ax.xaxis
yax = ax.yaxis
print('xax.get_major_formatter()', xax.get_major_formatter())
print('yax.get_major_formatter()', yax.get_major_formatter())
print('xax.get_major_locator():',  xax.get_major_locator())
print('yax.get_major_locator():',  yax.get_major_locator())
```
```:output
xax.get_major_formatter() <matplotlib.ticker.ScalarFormatter object at 0x118af4d68>
yax.get_major_formatter() <matplotlib.ticker.ScalarFormatter object at 0x118862be0>
xax.get_major_locator(): <matplotlib.ticker.FixedLocator object at 0x1188d5908>
yax.get_major_locator(): <matplotlib.ticker.AutoLocator object at 0x118aed1d0>
```

`Formatter`はxとyどちらもデフォルトの`ScalarFormatter`です。一方`Locator`は`ax.set_xticks`でticksの場所を指定したxでは`FixedLocator`に、何もしていないyではデフォルトの`AutoLocator`になっています。`Formatter`や`Locator`の種類とそれぞれの効果については以下の公式ドキュメントを見るとなんとなくわかると思います。
[Gallery > Tick formatters](https://matplotlib.org/gallery/ticks_and_spines/tick-formatters.html)
[Gallery > Tick locators](https://matplotlib.org/gallery/ticks_and_spines/tick-locators.html)

試しに先ほどの`ax.set_xticks`の例で`Formatter`と`Locator`を使ってみます。

```python
import matplotlib.ticker as ticker # FormatterとLocatorはTickerモジュールが必要

ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5*np.pi)) # 0.5*piごとにtickを書く
fig # jupyter notebookでは一度作ったFigureを再度表示させるにはfigを呼ぶ
```

![download-3.png](https://qiita-image-store.s3.amazonaws.com/0/222849/8ff04b41-4ccf-1eb4-c24d-b750fb9edfda.png)

`ax.set_xticks`の例とは違い、描画範囲全体にtickが配置(locate)されています。次はtick labelのフォーマットを変更します。

```python
@ticker.FuncFormatter # FuncFormatterだけはデコレータを使える。
def major_formatter_radian(x, pos):
    return '{}$\pi$'.format(x/np.pi) # おそらく良い書き方ではない

ax.xaxis.set_major_formatter(major_formatter_radian)
fig
```

![download-4.png](https://qiita-image-store.s3.amazonaws.com/0/222849/ef285dac-01a9-7761-3b07-2a553f345c99.png)

あまりよい仕上がりではないですが、`Formatter`と`Locator`が担っている役割ははっきりしたと思います。

ちなみに、`ax.plot`には[マニュアルには明記されていない](https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.plot.html#matplotlib.axes.Axes.plot)単位を指定できるオプション`xunits`があります。公式ドキュメントの [Gallery > Radian ticks](https://matplotlib.org/gallery/units/radian_demo.html) の指示通りに`basic_unit`モジュールをimportして`matplotlib.units.ConversionInterface`クラスを定義してやると、`Formatter`や`Locator`をわざわざ指定せずに理想に近いradianやdegree単位の軸が書けます。

```python
import numpy as np
from basic_units import radians, degrees, cos
from matplotlib.pyplot import figure, show

x = [val*radians for val in np.arange(0, 15, 0.01)]

fig = figure()
fig.subplots_adjust(hspace=0.3)

ax = fig.add_subplot(211)
line1, = ax.plot(x, cos(x), xunits=radians)

ax = fig.add_subplot(212)
line2, = ax.plot(x, cos(x), xunits=degrees)
```
<img width="440" alt="sphx_glr_radian_demo_001.png" src="https://qiita-image-store.s3.amazonaws.com/0/222849/a6438183-7554-a5a1-bc87-ab716b15dc48.png">

## `Tick`
ようやく一番下の階層に到達しました。`Tick`は軸上の小さな線(tick)とそこに添えられる文字(tick label)しか持ちません。

| `Tick` 属性 | 説明 |
|:--|:--|
| Tick.tick1line | 1st ticklineの`Line2D`オブジェクト |
| Tick.tick2line | 2nd ticklineの`Line2D`オブジェクト |
| Tick.gridline | `Line2D`オブジェクト |
| Tick.label1 | 1st tick labelの`Text`オブジェクト |
| Tick.label2 | 2nd tick labelの`Text`オブジェクト |
| Tick.gridOn | グリッドを書くかのboolean |
| Tick.tick1On | 1st ticklineを書くかのboolean|
| Tick.tick2On | 2nd ticklineを書くかのboolean|
| Tick.label1On | 1st tick labelを書くかのboolean|
| Tick.label2On | 2nd tick labelを書くかのboolean|

`Axis`のときと同様に、`Tick`オブジェクトは実際だと`XTick`と`YTick`という名前になっています。1stや2nd tickなどは`XTick`ではグラフ下と上、`YTick`では左と右のtickのことを指しています。おそらくx軸のtickのラベルをグラフ上部にも表示したい場合などに使うものです。

`Axes`にも`Axis`にも`Tick`をいじるための`set_***`系のヘルパーメソッドがたくさんあるので、`Tick`を直接触る必要はほとんどないでしょう。あるいは[`Axes.tick_params`](https://matplotlib.org/api/_as_gen/matplotlib.axes.Axes.tick_params.html)でもtickの向きや表示の有無などを一括設定できます。tickの位置やラベルは前述の通り`Axis`の`Formatter`と`Locator`で設定します。

# 各`Artist`のデフォルト設定のカスタム
ここまで把握できれば [Tutorials > Customizing matplotlib > A sample matplotlibrc file](https://matplotlib.org/tutorials/introductory/customizing.html#a-sample-matplotlibrc-file)
 に羅列された項目にざっと目を通して、何に対してどんな設定ができるのか確認してみると良いと思います。

matplotlibrcファイルを作らなくても一貫したスタイルで作図したい都度に

```python
plt.rcParams['lines.linewidth'] = 2
```

などとしてデフォルト値を設定できます。

# 読める人はぜひ改善された公式ドキュメントへ
おそらくmatplotlibの`Artist`について日本語で言及してる記事は現時点では冒頭で紹介したものと本稿くらいですが[^10]、英語で探すともっとあります。例えば[公式サイトで紹介されている外部サイト](https://matplotlib.org/resources/)は体系だって説明しているものが多いです。しかし、英語が読める人はひとまず公式ドキュメントにさっと目を通してみてください。「matplotlibは公式ドキュメントが散らかっていてわかりにくい」と思っている人が多いでしょうが、実は2017年10月にリリースされた2.1.0でかなり改善されました。執筆時の最新版2.1.1（2017年12月リリース）と改善直前の2.0.2（2017年5月リリース）で対応するページを見比べると一目瞭然です。

|2.1.1|2.0.2|
|:--|:--|
|[Gallery](https://matplotlib.org/2.1.1/gallery/index.html) と [Tutorials](https://matplotlib.org/2.1.1/tutorials/index.html)|[Matplotlib Examples](https://matplotlib.org/2.0.2/examples/index.html) と [Thumbnail gallery](https://matplotlib.org/2.0.2/gallery.html)|
|[Overview](https://matplotlib.org/2.1.1/contents.html)|[Overview](https://matplotlib.org/2.0.2/contents.html)|

改善前はそもそもExamplesとGalleryの違いがよくわからない上に、チュートリアルがなかったのでとりあえずExamplesを一つずつ見てまわりやりたいことに似てるものを探すしかありませんでした。改善後は基本的な項目がレベル別とカテゴリ別にTutorialsにまとめられ、象徴的な図とタイトルも添えられ一覧性が良くなっています。ドキュメントの改善に関しては現在の開発リーダーであるThomas Caswell氏が2015年からmatplotlibに資金援助しているNumFOCUSのインタビューで述べています。
[Matplotlib Lead Dev on Why He Can't Fix the Docs | NumFOCUS](https://www.numfocus.org/blog/matplotlib-lead-developer-explains-why-he-cant-fix-the-docs-but-you-can/)


[^1]: [このインタビュー](https://www.numfocus.org/blog/matplotlib-lead-developer-explains-why-he-cant-fix-the-docs-but-you-can/)ではmatplotlibの開発リーダー自身もたまにドキュメントから情報を探すのに苦労してることを告白しています。
[^2]: おそらく現在の詳しさの度合いに関係なく大半のmatplotlibユーザーが通ったことのある道ではないでしょうか。
[^3]: なぜaxisでなくaxesなのかと思う人も多いでしょう。後述している通り、matplotlibにおいては両者は異なるオブジェクトを意味します。
[^4]: `gcf`と`gca`は get current figure/axesの略です。
[^5]: ほとんどの場合はどちらでも好きな方を使えばよいと思いますが、GUIでいじれるインタラクティブなグラフを別ウィンドウで出すなどする場合は後者の例の方がよいようです。
[^6]: 解剖図を作成するコードもあります。tick関連の設定が参考になります。 https://matplotlib.org/gallery/showcase/anatomy.html
[^7]: もちろんこの他にも`Artist`はあります。全体像は [artist Module — Matplotlib 2.1.1 documentation](https://matplotlib.org/api/artist_api.html) を参照してください。四角で囲まれた名前をクリックするとそれぞれのドキュメントに移動できます。
[^8]: [2.0.2のマニュアル](https://matplotlib.org/2.0.2/api/figure_api.html?highlight=figure%20legend#matplotlib.figure.Figure.legend)を見るとhandlerとlabelが必須だったので、あまり便利ではなかったようです。
[^9]: 軸の見た目の調整について検索すると必ず遭遇するやつです。この記事で述べているようなことを知らないと、なかなか何をやっているか理解できず最後の一歩の調整に難儀するやつです。私は大嫌いでした。
[^10]: これにはちょっとびっくりしました。雰囲気でmatplotlibを使ってる人もけっこう多いのでしょう。