matplotlibでlogスケールに補助目盛りを確実に表示する方法

## はじめに

対数スケール（logスケール）でプロットするとき、matplotlib はデフォルトで主目盛り（メイン目盛り, major ticks）と補助目盛り（サブ目盛り, minor ticks）を描画してくれます。

ただし、データの範囲が広かったり表示が詰まっていたりすると、補助目盛りが表示されないこともあります。

そこでこの記事では、**logスケールに補助目盛りを確実に表示する方法**を2つ紹介します。

1. log プロットに補助目盛りを出す
2. imshow（ヒートマップ）に対数スケールを適用したときに colorbar に補助目盛りを出す

<br>

実装コードはGoogle Colab [こちら](https://colab.research.google.com/drive/1LJCR_BpeU4echN8hAPz0Z7APJNuaihaN?usp=sharing) からも閲覧できます。


## log プロットに補助目盛りを出す

まずは log プロットに補助目盛りを出す例です。

```python:logプロット (補助目盛り)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator

x = np.logspace(0, 5, 100)
y = x**3

fig, ax = plt.subplots()
ax.loglog(x, y)

# 対数スケールに補助目盛りを設定
ax.xaxis.set_minor_locator(
    LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1))
)
ax.yaxis.set_minor_locator(
    LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1))
)

plt.show()
````
**出力結果**
![loglog_minorticks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/77ac2933-838b-4fee-a7e2-bfa42032cdfe.png)


- `subs` は、10 のべき乗間に目盛りを打つ相対位置です。
たとえば `subs=np.arange(0.2,1.0,0.1)` の場合、各メイン目盛り間に `[0.2, 0.3, ..., 0.9] * 10^n` の位置に補助目盛りが打たれます。

- `numticks` は、matplotlib 内部がメイン目盛り数に基づいて補助目盛りを計算する際に使うヒント値です。この値が小さいと補助目盛りが表示されないことがあるので、十分大きな値（例えば 99 など）に設定しておくと安心です。


## imshow + colorbar に補助目盛りを出す

ヒートマップや画像に対数スケールを適用する場合も同じです。colorbar にも `LogLocator` を設定することで補助目盛りがきちんと入ります。

```python:imshow + colorbar (補助目盛り)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator
from matplotlib.colors import LogNorm

# データ
data = np.random.rand(100, 100) * 1e5

fig, ax = plt.subplots()
im = ax.imshow(
    data,
    norm=LogNorm(vmin=1e-1, vmax=1e12),
    cmap='viridis'
)
cbar = fig.colorbar(im)

# カラーバーに補助目盛りを設定
cbar.ax.yaxis.set_minor_locator(
    LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1))
)

plt.show()
```
**出力結果**
![logimshow_minorticks.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/4148cdf7-8fc5-4a86-acf4-71760cbedef9.png)


これだけで、colorbar にも対数に沿った補助目盛りがきちんと入ります。


## 補足：主目盛りを一桁ごとに表示して併用する場合

もし「主目盛りも10⁰, 10¹, 10² … のように一桁ごとにきちんと出したい」というときには、`set_major_locator` を併用すると簡単に実装できます。

もしさらに詳細な実装に興味がある方は、以下をご覧ください。

<details><summary>主目盛りを一桁ごとの表示を含めたコード</summary>

```python:logプロット (主目盛り+補助目盛り)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator

x = np.logspace(0, 5, 100)
y = x**3

fig, ax = plt.subplots()
ax.loglog(x, y)

# メイン目盛りを設定
ax.xaxis.set_major_locator(LogLocator(numticks=99))
ax.yaxis.set_major_locator(LogLocator(numticks=99))

# 対数スケールに補助目盛りを設定
ax.xaxis.set_minor_locator(LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1)))
ax.yaxis.set_minor_locator(LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1)))

plt.show()
```
**出力結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/54dd6111-1cbc-441c-a771-0eadca6c3b8d.png)


```python:imshow + colorbar (主目盛り+補助目盛り)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import LogLocator
from matplotlib.colors import LogNorm

# データ
data = np.random.rand(100, 100) * 1e5

fig, ax = plt.subplots()
im = ax.imshow(data, norm=LogNorm(vmin=1e-1, vmax=1e12), cmap='viridis')
cbar = fig.colorbar(im)

# カラーバーにメイン目盛りと補助目盛りを設定
cbar.ax.yaxis.set_major_locator(LogLocator(numticks=99))
cbar.ax.yaxis.set_minor_locator(
    LogLocator(numticks=99, subs=np.arange(0.2, 1.0, 0.1))
)

plt.show()
```
**出力結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1599258/224242e7-8280-4806-a79c-452215d7f6cb.png)

</details>


## 参考記事

* [Pythonでmatplotlibパッケージを使って描いた対数グラフの補助目盛が表示されない件](https://www.sonohigurashi.blog/20210922-python-matplotlib-logarithmic-graph-minor-ticks/)
* [How to display all minor tick marks on a semi-log plot? - Stack Overflow](https://stackoverflow.com/questions/44078409/how-to-display-all-minor-tick-marks-on-a-semi-log-plot)


## おわりに

この記事では、logスケールに補助目盛りを出す2つの手法を紹介しました。

図に込められた意図はさまざまです。  
それは単に見やすさや形式的な見せ方にとどまらず、ときに見る者に新たな気づきをもたらすきっかけとなることもあります。

たとえば logスケールは、一度立ち止まって値同士の関係に目を凝らす、  
そんな静かな時間を私たちに与えてくれるものだと思います。  
それは、直感的に読み取れる線形スケールにはない深みです。

けれど、図に込められる役割や目的もまたひとつではありません。  
ときには対数スケール上で、値そのものを精確に読み取る場面もありますよね。  
もし線形スケールのような直感性を対数スケールでもそのまま活かせたら……便利だと思いませんか。

線形スケールで自然とできていた「10等分」のような感覚。  
対数スケールでは、補助目盛りがそれを担ってくれます。

広大なスケールを一望できる見通しのよさに満足せず、  
その中にひそむ繊細な変化に気づかせてくれる技術に感謝すること——

対数と補助目盛りが織りなす美しいグラフには、  
きっとまだ見ぬかけがえのない世界がひそかに広がっているのでしょう。

それに気づけたとき、それはまた一歩、真理へと近づけた証なのかもしれません。