【Matplotlib】軸ラベルは傾けない

## はじめに
* 最近、時系列のグラフを多く見かけますが、一部の軸ラベルは傾いています。
* 恐らく見栄えを弄る時間が無かったか？もしくは対処法が見つからなかったかもしれません。
* 私自身も、軸ラベルを傾けるかあるいは横向けのグラフで妥協して作図していました。

## 背景
* [「データ視覚化のデザイン#4」](https://note.com/goando/n/neb6ea35f1da3)で**軸ラベルは傾けない**という記事をTwitterで見かけました。
* その記事では、軸ラベルを傾ければ文字の認識に要する時間が伸びていくという研究の紹介がありました。
* ではどうすればと、軸ラベルを傾ない方法を調べました。

## サンプルデータ
```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as ticker

# サンプルデータ
data = pd.DataFrame({'MAU':np.random.randint(5000, 8500,(10,))},
                     index=pd.date_range('2019/12/25', freq='d', periods=10))
```
|		|MAU|
|:--------:|-------:|
|2019-12-25 	|6831|
|2019-12-26 	|6829|
|2019-12-27 	|5477|
|2019-12-28 	|8204|
|2019-12-29 	|8227|
|2019-12-30 	|6257|
|2019-12-31 	|5828|
|2020-01-01 	|5868|
|2020-01-02 	|5355|
|2020-01-03 	|5548|

## いつも通りのグラフ
```python
fig = plt.figure(figsize=(4,3), dpi=144)
ax = fig.add_subplot(111)

ax.bar(data.index, data['MAU'])
ax.set_xticklabels(data.index, rotation=45)

plt.tight_layout()
plt.savefig('test01.svg')
plt.show()
```
<img width="288" alt="test01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/a555e3fc-78af-2f6a-aaed-efbdd836abf6.png">

* 確かに傾いたラベルに合わせて、頭を傾けている自分がいる。

## こんな便利なのがあったのか！
```python
# Locatorとformatterを使って見栄えよく
fig = plt.figure(figsize=(4,3), dpi=144)
ax = fig.add_subplot(111)

locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)

ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)

ax.bar(data.index, data['MAU'])

plt.savefig('test02.svg')
plt.show()
```

<img width="288" alt="test02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/acc81f49-f688-ddf0-50cc-c0799acf52b6.png">

* 上記のグラフの軸ラベルの見え方でOKならば、以下の4行を追加するべし！たったこれだけ！

```python
locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)

ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
```
## もうちょっとカスタマイズしたい！
* 先ほどのグラフの軸ラベルは、年・月・日情報がすべて含まれているで十分なのですが、年をまたぐ場合は、頭の中で引き算が必要です。
* 例えば、軸ラベルの最初には年情報が欲しい！など、どうする？

### 軸ラベルを自ら作成する
```python
fmt = []
yy = ''
for x in data.index:
    years = x.year
    days = x.day
    if yy != x.year:
        fmt.append(str(days)+'\n'+str(years))
    else:
        fmt.append(str(days))
    yy = x.year

fig = plt.figure(figsize=(4,3), dpi=144)
ax = fig.add_subplot(111)

ax.set_xticklabels(fmt)
ax.bar(data.index, data['MAU'])

plt.savefig('test03.png')
plt.show()
```
<img width="288" alt="test03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/d83b482f-3f50-bce6-0c4d-707c1ca90978.png">

* 単一のグラフや軸ラベルが少ない場合に適していると思います。

### matplotlibのformatterで`FixedFormatter`を使ってみる。
``` python
fig = plt.figure(figsize=(4,3), dpi=144)
ax = fig.add_subplot(111)

# positions = [0,1,2,3,4,5,6,7,8,9]
labels = ['Dec\n2019','26','27','28','29','30','31','Jan\n2020','2','3']
# ax.xaxis.set_major_locator(ticker.FixedLocator(positions))
ax.xaxis.set_major_formatter(ticker.FixedFormatter(labels))

ax.bar(data.index, data['MAU'])

plt.savefig('test04.png')
plt.show()
```
<img width="288" alt="test04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/b94100e0-1ffa-a355-4764-412fc2181d04.png">

* 使い方は、リスト形式で軸ラベルを作成して、それをformatterに設定するだけです。
* Locatorは設定すると、軸ラベルが表示されなかったのでコメントアウトしています。
* これも、独自に作成する点は同じですが、複数グラフに適してると思います。

### matplotlibのformatterで`FuncFormatter`を使ってみる。
```python
# matplotlibはグレゴリオ暦を使用する
fig = plt.figure(figsize=(4,3), dpi=144)
ax = fig.add_subplot(111)

YY = ''
@ticker.FuncFormatter
def major_fmt(x, pos):
    global YY
    y = mdates.num2date(int(x)).year
    d = mdates.num2date(int(x)).day
    if YY != y:
        t = '{}\n{}'.format(d, y)
    else:
        t = '{}'.format(d)
    YY = y
    return t

ax.xaxis.set_major_formatter(major_fmt)

ax.bar(data.index, data['MAU'])

plt.savefig('test05.png')
plt.show()
```
<img width="288" alt="test05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126042/35043b57-990f-e38d-859b-c4c8c70a03a4.png">

* ユーザー定義の関数を使って軸ラベルを作成します。
* @ticker.FuncFormatterから関数が自動的に実行されます。
* この記事を書くまで、matplotlibはグレゴリオ暦が基準と知りませんでした。:sweat_smile:
* なので、`num2date(int(x))`で変換が必要です。

## locatorとFormatterについて
* Locatorは軸ラベルをどのように配置するか
* Formatterは軸ラベルをどのような表示形式で表示するか
* 以下の公式マニュアルを読み込んでみれば、**軸ラベル力**が各段に上達します。

>[Matplotlib]公式サイト
* [Tick locators](https://matplotlib.org/3.2.0/gallery/ticks_and_spines/tick-locators.html)
* [Tick formatters](https://matplotlib.org/3.2.0/gallery/ticks_and_spines/tick-formatters.html)

## まとめ
* **「軸ラベル」を傾けない**について、`Matplotlib`の`Formatter`を使ってカスタマイズする。
* 1)自力で軸ラベルを作成する。2)自動設定を使う。3)FixedFormatterを使う。4)FuncFormatterを使う。
* この記事を書いて**データ視覚化のデザイン力**が少し上がった気分になりました。