（matplotlib）グラフの体裁調整のためのTips【軸・図枠・目盛・目盛文字編】

# 概要

matplotlibで生成したグラフの体裁調整・微調整のためのTips（～軸・図枠・目盛・目盛文字～編）です。<br>![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/fc09e7f9-4879-2e7b-3384-75869413f04e.png)

matplotlib 3.2.2、GoogleColab.（Python 3.6.9）により動作確認をしています。

# 準備・共通

グラフのなかで**日本語**が使えるように `japanize-matplotlib` ライブラリをインストールします。GoogleColab.の場合、コードセルで以下を実行することでインストールできます。

```:japanize-matplotlibのインストール（GoogleColab.）
!pip install japanize-matplotlib
```

各種ライブラリをインポートをします。

```py:準備・共通コード
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
import matplotlib.transforms as transforms
import matplotlib.patheffects as patheffects
import japanize_matplotlib
import numpy as np
```

基本形として、特に体裁調整していないグラフを出力してみます。

```py:基本
x = np.linspace(-6,6,21)
y = 1/(1+np.exp(-x))
fig,ax = plt.subplots(figsize=(4,3),facecolor='white',dpi=150)
ax.plot(x,y,marker='.')
plt.show()
```

以下のようなグラフが出力できればOKです。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/7241d0f7-ad17-6c8d-54cd-792b11228c40.png)

# 目盛文字（TickLabel）の書式設定

X軸の主目盛の書式を**小数第1位まで表示する形式**に、Y軸の主目盛の書式を「**2.0×10<sup>-1</sup>のような形式**」に設定してみます。

`import matplotlib.ticker as ticker` を忘れないように。

```py:目盛文字の書式設定
x = np.linspace(-6,6,21)
y = 1/(1+np.exp(-x))
fig,ax = plt.subplots(figsize=(4,3),facecolor='white',dpi=150)
ax.plot(x,y,marker='.')

ax.xaxis.set_major_formatter(ticker.FuncFormatter(lambda x,_: f'{x:.1f}'))
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y,_: f'{y*10:.1f}'+ r'$\times 10^{-1}$'))

plt.show()
```
![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/c4eea03a-d528-5c82-e87b-5900b20d4d68.png)

# 目盛文字（TickLabel）の位置調整

後述する方法で目盛を「内向き」に設定すると、X軸とY軸の目盛文字が近すぎて見苦しくなる場合があります。これを解決するために、X軸の主目盛の文字位置を下方向に微調整してみます。`matplotlib.transforms as transforms` を忘れないように。

```py:目盛文字の位置調整
x = np.linspace(1000,1010,11)
y = (x-1000)*0.5

fig,axes = plt.subplots(1,2,figsize=(8,3),facecolor='white',dpi=150)
axes = axes.ravel()
for ax in axes:
  ax.plot(x,y,marker='.')
  ax.set_xlim(1000,1010)
  ax.set_ylim(0,5)
  ax.tick_params(direction='in')

# 右側（axes[1]）のみ xticklabels 位置の調整
mv = transforms.ScaledTranslation(0,-0.07,fig.dpi_scale_trans)
for label in axes[1].xaxis.get_majorticklabels() :
    label.set_transform(label.get_transform() + mv)

axes[1].text(0.95,0.075,'x_tick_labels 位置の調整あり',ha='right',c='tab:red',transform=ax.transAxes)
plt.show()
```

もっと簡単に移動する方法がありました。

```py:目盛文字の位置調整（更新版）
x = np.linspace(1000,1010,11)
y = (x-1000)*0.5

fig,axes = plt.subplots(1,2,figsize=(8,3),facecolor='white',dpi=150)
axes = axes.ravel()
for ax in axes:
  ax.plot(x,y,marker='.')
  ax.set_xlim(1000,1010)
  ax.set_ylim(0,5)
  ax.tick_params(direction='in')

# 右側（axes[1]）のみ xticklabels 位置の調整
ax = axes[1]
ax.get_xaxis().set_tick_params(pad=8)
ax.text(0.95,0.075,'x_tick_labels 位置の調整あり',ha='right',c='tab:red',transform=ax.transAxes)

plt.show()
```

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/6f573b5a-daf7-815f-5d84-2a6f832a62a4.png)

# 軸／図枠を非表示に設定

`imshow(...)` などを使用した場合、軸／図枠を表示させたくない場合があります。軸／図枠を非表示にしてみます。

```py:軸／図枠を非表示
x = np.linspace(-6,6,21)
y = 1/(1+np.exp(-x))
fig,ax = plt.subplots(figsize=(4,3),facecolor='white',dpi=150)
ax.plot(x,y,marker='.')

for p in ['left','top','right','bottom']:
  ax.spines[p].set_visible(False)

ax.grid() # グリッド
plt.show()
```

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/dc55de91-0c6d-bfbd-6297-13b37b61c162.png)

# 目盛（Tick）の向きと長さを調整

軸の目盛の向きを「内側」「外側」「両側」に変更してみます。また、目盛の「長さ」を調整してみます。長さをゼロに設定すれば、実質的に目盛を非表示にすることができます。

```py:目盛の向きと長さを調整
x = np.linspace(-6,6,21)
y = 10/(1+np.exp(-x))-5
fig,axes = plt.subplots(2,2,figsize=(8,6),facecolor='white',dpi=150)
axes = axes.ravel()

for ax in axes :
  ax.plot(x,y,marker='.')

axes[0].tick_params(direction='in')
axes[0].text(0.1,0.9,"direction='in'",c='tab:red',va='top',transform=axes[0].transAxes)
axes[1].tick_params(direction='inout')
axes[1].text(0.1,0.9,"direction='inout'",c='tab:red',va='top',transform=axes[1].transAxes)
axes[2].tick_params(direction='inout',length=8)
axes[2].text(0.1,0.9,"direction='inout'\nlength=8",c='tab:red',va='top',transform=axes[2].transAxes)
axes[3].tick_params(length=0)
axes[3].text(0.1,0.9,"length=0",c='tab:red',va='top',transform=axes[3].transAxes)

plt.show()
```

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/1503b8bd-1521-8152-34c2-8a9d45c7cf91.png)

# 特定の目盛文字（TickLabel）だけ色を変更する

相手に注目してもらうために TickLabel のうち特定の項目だけ色を変えたい場合があります。ここでは、`xticklabels` の2番目の要素である Carol だけを赤文字にしてみます。

```py:特定の目盛文字（TickLabel）だけ色を変更
x = ['Alice','Bob','Carol','Dave','Ellen']
y = [40,20,25,40,20]

fig,ax = plt.subplots(figsize=(4,3),facecolor='white',dpi=150)
bars = ax.bar(x,y)
bars[2].set_facecolor('tab:red') # おまけ：バーも赤色に変更
#print(type(bars[2])) # <class 'matplotlib.patches.Rectangle'>

labels = ax.get_xticklabels()
labels[2].set_color('tab:red') # 2番目の要素を赤文字に変更
#print(type(labels[2])) # <class 'matplotlib.text.Text'>

plt.show()
```

![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/ddb85bf4-e764-0787-4052-409493776e0a.png)

# Y軸の上下を反転

Y軸の TickLabel は、デフォルトでは下から上に向かって並びます。これを、反転させ上から下に向かって並ぶようにしてみます。

```py:Y軸の上下を反転
x = ['Alice','Bob','Carol','Dave','Ellen']
y = [40,20,25,40,20]

fig,axes = plt.subplots(1,2,figsize=(8,3),facecolor='white',dpi=150)
axes = axes.ravel()

for ax in axes:
  ax.barh(x,y)
  ax.tick_params(length=0)
  ax.grid(axis='x')
  ax.set_axisbelow(True)

axes[0].set_title('デフォルト（下から上）')
axes[1].set_title('反転（上から下）')

# 右図で上下を反転
ax = axes[1]
ax.invert_yaxis()
t = axes[1].text(0.95,0.05,'ax.invert_yaxis( )',c='tab:red',ha='right',transform=ax.transAxes)
t.set_path_effects([patheffects.Stroke(linewidth=3,foreground='white'),patheffects.Normal()])

plt.show()
```

![11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/565fcc99-7810-72a6-02b4-d8d0da35c7aa.png)

# Y軸の目盛ラベルの配置を調整（右揃え、左揃え、中央揃え）

```py:Y軸の目盛ラベルの配置を調整（右揃え、左揃え、中央揃え）
x = ['A','BB','CCC']
y = [40,20,25]

fig,axes = plt.subplots(3,1,figsize=(4,4),facecolor='white',dpi=150)
axes = axes.ravel()

text=['右揃え(デフォルト)','左揃え','中央揃え']
for i, ax in enumerate(axes):
  ax.barh(x,y)
  ax.invert_yaxis()
  ax.tick_params(length=0)
  ax.set_xticklabels([])
  t = ax.text(0.95,0.1,text[i],c='tab:red',ha='right',transform=ax.transAxes)
  t.set_path_effects([patheffects.Stroke(linewidth=3,foreground='white'),patheffects.Normal()])

# 左揃え
ax = axes[1]
ax.set_yticklabels(x,ha ='left')
ax.get_yaxis().set_tick_params(pad=25)

# 中央揃え
ax = axes[2]
ax.set_yticklabels(x,ha ='center')
ax.get_yaxis().set_tick_params(pad=17.5)

plt.show()
```

![10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/29b82fb4-3360-3b51-4e26-58d03bb821fd.png)


# X軸とY軸をゼロ位置にセット

数学の教科書に記載されているようなグラフスタイル（X軸とY軸の目盛文字が「ゼロ」の位置にある）に変更してみます。

```py:X軸とY軸をゼロ位置にセット
x = np.linspace(-6,6,21)
y = 10/(1+np.exp(-x))-5  # 変更

fig,ax = plt.subplots(figsize=(4,3),facecolor='white',dpi=150)
ax.plot(x,y,marker='.')

ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.tick_params(direction='inout')

plt.show()
```

![7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/faba0a13-37a7-b541-5221-5eb0e486eace.png)

さらに、細かな調整を行ないます。

```py:X軸とY軸をゼロ位置にセット2
x = np.linspace(-6,6,21)
y = 10/(1+np.exp(-x))-5  # 変更

fig,ax = plt.subplots(figsize=(4,4),facecolor='white',dpi=150)
ax.plot(x,y,marker='.')

ax.spines['bottom'].set_position('zero')
ax.spines['left'].set_position('zero')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)

ax.set_xlim(6,-6)
ax.set_ylim(6,-6)

# 主目盛：刻み間隔2、0は非表示、文字を白縁取り
ax.set_xticks(np.arange(-6,6+1,2))
labels = ax.set_xticklabels( [ ( x if x!=0 else '') for x in np.arange(-6,6+1,2)] )
for t in labels :
  t.set_path_effects([patheffects.Stroke(linewidth=3,foreground='white'),patheffects.Normal()])

ax.set_yticks(np.arange(-6,6+1,2))
labels = ax.set_yticklabels( [ ( y if y!=0 else '') for y in np.arange(-6,6+1,2)] )
for t in labels :
  t.set_path_effects([patheffects.Stroke(linewidth=3,foreground='white'),patheffects.Normal()])

ax.tick_params(which='major',direction='inout',length=5)

# 補助目盛（minor）：刻み間隔1でグリッド描画するための準備
ax.set_xticks(np.linspace(-6,6,13),minor=True)
ax.set_yticks(np.linspace(-6,6,13),minor=True)
ax.tick_params(which='minor',direction='inout',length=3)

# グリッド（which='both'で、majorとminorの目盛位置にグリッド）
ax.grid(which='both')

plt.show()
```

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152805/fc09e7f9-4879-2e7b-3384-75869413f04e.png)

# 関連
- [（matplotlib）ピクセル単位でサイズを指定したグラフを描きたい](https://qiita.com/code0327/items/43118813b6085dc7e3d1)
- [matplotlibで、途中を波線で省略したグラフを作成したい（印象操作したい）](https://qiita.com/code0327/items/820cc9e239736ed33fdd)
- [相関行列をキレイにカスタマイズしたヒートマップで出力したい。matplotlib編](https://qiita.com/code0327/items/aab375e25475f3d6567a)
- [（相関関係の可視化）散布図では点が重なってしまう場合の代替案](https://qiita.com/code0327/items/24d95b48a14f68f30ac0)