【matplotlib】カラーバの目盛数などを色々と調整したい

# はじめに
matplotlibでカラーバの表示される目盛を少し減らしたい場面があり、その際に色々と調べたので備忘録も兼ねて本記事を書きました。
本記事は、**カラーバの目盛間隔を適当な数に指定したい、任意の等間隔にしたい、とにかくいじりたい**方向けにまとめたつもりですので、もし参考になれば幸いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/a444373e-65cb-7923-b953-0e09f5f87a39.png)

# 実装
Google Colabで作成した本記事のコードは、[こちら](https://colab.research.google.com/drive/18KiA_Myo3zaBbSuOzNuX7yJauGXPDPon?usp=sharing)にあります。

## 各種インポート
```Python:各種インポート
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import numpy as np
```

実行時の各種バージョン

| Name | Version |
|:-:|:-:|
| numpy | 1.22.4 |
| matplotlib | 3.5.3 |


## 目盛数の指定
いい感じに目盛数を決めて表示するには、[MaxNLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.MaxNLocator)が便利です。**とりあえず、目盛数の簡易的に調整したいだけの場合は、この関数を使えば事足りると思います。**
実装例では、3個の目盛を表示をいい感じに表示するようにしています。この関数の``nbins``に表示個数を指定します。
※この関数は``nbins``よりも少なくor多く表示されるケースがあるので、詳しくは[補足のMaxNLocator()の注意事項](#maxnlocatorの注意事項)をご覧ください。
```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.MaxNLocator(nbins=3))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/3abee3ea-867d-369a-5428-86499530a766.png)

## 等間隔な目盛の指定
等間隔に目盛を表示するには、[MultipleLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.MultipleLocator)が便利です。
実装例では、``22``置きに目盛を表示するようにしています。この関数の``base``に表示間隔を指定します。

```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.MultipleLocator(base=22))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/0ea855b7-65a8-d66b-5d21-bea52a484301.png)

## 基準値からの等間隔な目盛の指定
等間隔に目盛を表示するには、[IndexLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.IndexLocator)が便利です。
実装例では、値``5``を基準にして、``25``置きに目盛を表示するようにしています。この関数の``base``, ``ofset``はそれぞれ、間隔と基準値を指定する引数です。

```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.IndexLocator(base=25, offset=5))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/15ad14c5-3d93-b08f-d227-01a3824dacc0.png)

## 任意の目盛に指定
任意の目盛を表示するには、以下のように書きます。
```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=[33, 59, 72])
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/cdead4af-16bd-7e46-e9cf-667981a0d39a.png)

## 等分割な目盛に指定
[LinearLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.LinearLocator)を使うと、カラーバの最小値と最大値の間を等分割に目盛を指定できます。引数の``numticks``は分割数を表し、実装例では5分割しています。
```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.LinearLocator(numticks=5))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/298ad08a-ee15-c716-3eb0-646ee8aacb6a.png)

※MaxNLocator()とLinearLocator()の棲み分け

## 対数スケールで目盛を指定
[LogLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.LogLocator)を使うと、カラーバの最小値と最大値の間を対数スケールで目盛を指定できます。引数の``base``は基底を表し、実装例では基底は``2``にしているので、2の累乗が表示されています。
※この関数は全ての目盛が表示されない場合があるので、詳しくは[補足のLogLocator()の注意事項](#loglocatorの注意事項)をご覧ください。
```python:実装例
data = np.arange(1, 101).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.LogLocator(base=2))
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/63ab66a6-a4c5-37e6-ef76-ca4102d349b1.png)




## 目盛の非表示
目盛を表示したくない場合は、[NullLocator()](https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.NullLocator)を使います。
```python:実装例
data = np.arange(100).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.NullLocator())
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/59a6e8da-fedb-c409-87e7-06d3fe561f8b.png)


# 補足
## MaxNLocator()の注意事項
ある特定の条件を満たす場合は**目盛は0を基準**として定義されます(区切りのいい数字が表示されるようにされるための仕様だと思います)。以下のような2つ例では、``nbins=3``と指定しても**2つしか目盛が表示されない**ので注意しましょう。また、0基準でないケースでは、``nbins=3``と指定しても4つ表示されるといった場合もあるので、あくまで表示されるカラーバを確認しながら調整しましょう。

<details><summary>特定の条件とは</summary><div>

特定の条件とは、matplotlibのソースコードを追うと理解できます。matplotlib本家の``MaxNLocator()``の中身を見ると、基準を決めている箇所があり、そこでは、ロカールで使われた``scale_range()``と呼ばれる関数が呼び出されます。
この関数の中身では0を基準とする条件は以下のようにしています。カラーバの最大値(Max), 最小値(Min)が
```math:
\frac{abs(Max+Min)}{abs(Max-Min)} < 200
```
を満たす場合は、0を基準としています。この式の解釈は、**カラーバの最大値と最小値が非常に大きくかつ近い値**のときにこの条件を満たさない場合があり、その場合は基準が0ではない別で定義されたものが使われます。それ以外は**基準は0**となります。詳しくはmatplotlibのソースコードmatplotlib/lib/matplotlib/ticker.pyをご覧ください。

</div></details>

```python:-60~39のデータでも0基準の目盛
data = np.arange(-60, 40).reshape(10, 10)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.MaxNLocator(nbins=3))
plt.show()
```
``nbins=3``ですが、2個しか表示されていません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/e240d06b-1d5a-b2ab-879b-395fa3e8fbe6.png)



```python:100~109のデータでは0基準の目盛
data = np.arange(100, 110).reshape(5, 2)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.MaxNLocator(nbins=3))
plt.show()
```
目盛が0基準であることがわかります。0から等間隔に引かれた目盛のため、スタートが102(3で割り切れる数)となっています。このケースを10倍にして同じように表示してみたのが次のカラーバです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/c779c5bf-90c0-8b59-f2ad-41558092be65.png)

```python:1000~1009のデータでは0基準ではない目盛
data = np.arange(1000, 1010).reshape(5, 2)
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.MaxNLocator(nbins=3))
plt.show()
```
``nbins=3``ですが、**4個も表示されています**。上記で触れた特定の条件より、0基準でないです。0基準かどうかは、0から等間隔に計算された目盛を使っているといるかどうかで判断できます。以下のカラーバを見ると、3の倍数の等間隔な目盛ですが、1000は3の倍数ではないのでこの目盛は0基準では作られていないことが分かります。そして、このように0基準で作られないカラーバは、しばしば``nbins``よりも大きくなることもあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/0bd44e6e-5a70-3b89-545d-f90d61fa493b.png)

## LogLocator()の注意事項
この関数は負の値は使用できず、また**0~1の値を含む場合は、全ての目盛を表示しない場合があります**。以下では、例を2つ紹介し、その挙動を確認します。

```python:最小値0を含む
data = np.arange(0, 100).reshape(10, 10)  # 0~99の範囲のデータ
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.LogLocator(base=2))
plt.show()
```
``1, 2, 4, 8, 16``が表示されていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/c171687b-bad8-98d7-a544-7c727867de08.png)

```python:最小値0.2を含む
data = np.arange(0, 100, dtype=np.float64).reshape(10, 10)
data += 0.2  # 0.2~99.2の範囲のデータ
fig, ax = plt.subplots()
heatmap = ax.imshow(data)
cbar = plt.colorbar(heatmap, ticks=mticker.LogLocator(base=2))
plt.show()
```
``1, 4, 16, 64``が表示されていない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/d3bf92fb-bede-1946-14b9-b76768c93cdc.png)

# まとめ
matplotlibでとにかく色々とカラーバをいじりたい方向けの記事を書いてみました。筆者は全てのカラーバで使えそうな関数を網羅できたていないので、詳しい情報はmatplotlibの本家のページをご覧ください。上記の関数を理解し使いこなしていきましょう。

# 参考資料
https://matplotlib.org/stable/api/ticker_api.html#matplotlib.ticker.MaxNLocator