ころがる狸

ころがる狸のデータ解析ブログ

【Matplotlib】図の枠線の太さを調整する方法

こんにちは。今日は、Matplotlibの作図方法について取り上げます。これについては既に多くの日本語記事が存在していますが、最近Matplotlibでの図の枠線の太さの変え方で大いに悩んだため、いくつかのケースについて記事にしてまとめることにしました。論文用の図を作成する際に、このような微調整が必要となる場面は少なくないのではないでしょうか。

単一の図の場合

Matplotlibでの枠線の太さは、axesクラスから辞書型のspinesを呼び出すことで調整できます。また綺麗な図を作ろうと思うとメモリの太さや長さ、向きも変える必要があるかもしれませんが、これはtick_paramsでOKです。実装を見てみましょう。

#ライブラリのimport
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.axes_grid1.inset_locator import inset_axes

####図の調整用パラメータ####
#図の大きさ
figsize = (4, 4)
#散布図の点の大きさ
s = 3
#軸の太さ
spines = 3
#ラベルサイズ
labelsize = 15
#フォントサイズ
fontsize = 18
#メモリの長さ
length = 5
#メモリの太さ
width = 3
###########################

# plt.figure(figsize=figsize)
#単一グラフの表示
fig, ax = plt.subplots(figsize=figsize)
#散布図
ax.scatter(x, y, s=s)
ax.set_xlabel("xtitle", fontsize=fontsize)
ax.set_ylabel("ytitle", fontsize=fontsize)

#軸の太さの調整。方向を辞書のキーとして渡し、set_linewidthで大きさを微調整できる
ax.spines["top"].set_linewidth(spines)
ax.spines["left"].set_linewidth(spines)
ax.spines["bottom"].set_linewidth(spines)
ax.spines["right"].set_linewidth(spines)

#メモリの長さ,太さ, ラベルサイズの調整
ax.tick_params(direction="in", length=length, width=width, labelsize=labelsize)

f:id:Dajiro:20200514192333p:plain
枠線を太くした図その1

複数の図の場合

この場合もほとんど同じですね。

fig = plt.figure(figsize=figsize)
for i in range(1, 5, 1):
    #複数のグラフの表示
    ax = fig.add_subplot(2, 2, i)
    #散布図
    ax.scatter(x, y, s=s)
    ax.set_xlabel("xtitle", fontsize=fontsize)
    ax.set_ylabel("ytitle", fontsize=fontsize)

    #軸の太さの調整
    ax.spines["top"].set_linewidth(spines)
    ax.spines["left"].set_linewidth(spines)
    ax.spines["bottom"].set_linewidth(spines)
    ax.spines["right"].set_linewidth(spines)

    #メモリの長さ,太さ, ラベルサイズの調整
    ax.tick_params(direction="in", length=length, width=width, labelsize=labelsize)

plt.tight_layout()
plt.savefig("quad.png", bbox_inches="tight")

f:id:Dajiro:20200514192400p:plain
枠線を太くした図その2

インセットありの図の場合

インセットありの場合には、from mpl_toolkits.axes_grid1.inset_locator import inset_axesを読み込んだのち inset_axesを使います。
【参考資料】
Inset Locator Demo — Matplotlib 3.2.1 documentation

fig, ax = plt.subplots(figsize=(5, 8))
#親の図の散布図をプロット
ax.scatter(x, y, s=s)
ax.set_xlabel("xtitle", fontsize=fontsize)
ax.set_ylabel("ytitle", fontsize=fontsize)
ax.set_ylim(-4, 7.5)
#親の図の枠線を太くする
ax.spines["top"].set_linewidth(spines)
ax.spines["left"].set_linewidth(spines)
ax.spines["bottom"].set_linewidth(spines)
ax.spines["right"].set_linewidth(spines)
ax.tick_params(direction="in", length=length, width=width, labelsize=labelsize)

#インセット図の生成
axins = inset_axes(ax, width=2.0, height=1.5)
#インセット図に散布図をプロット
axins.scatter(x, y, s=s)
axins.set_xlabel("xtitle", fontsize=fontsize)
axins.set_ylabel("ytitle", fontsize=fontsize)
#インセット図の枠線を太くする
axins.spines["top"].set_linewidth(spines)
axins.spines["left"].set_linewidth(spines)
axins.spines["bottom"].set_linewidth(spines)
axins.spines["right"].set_linewidth(spines)
axins.tick_params(direction="in", length=length, width=width, labelsize=labelsize)

f:id:Dajiro:20200514192430p:plain
枠線を太くした図その3

ヒストグラム付き散布図の場合

ヒストグラム付き散布図の場合は以下のように枠を設定する必要があります。
【参考資料】
Scatter plot with histograms — Matplotlib 3.1.0 documentation

left, width = 0.1, 0.65
bottom, height = 0.1, 0.65
spacing = 0.025

#枠の位置を指定
rect_scatter = [left, bottom, width, height]
rect_histx = [left, bottom + height + spacing, width, 0.2]
rect_histy = [left + width + spacing, bottom, 0.2, height]

plt.figure(figsize=(6, 6))

#枠を作成
ax = plt.axes(rect_scatter)
ax_histx = plt.axes(rect_histx)
ax_histx.tick_params(direction='in', labelbottom=False)
ax_histy = plt.axes(rect_histy)
ax_histy.tick_params(direction='in', labelleft=False)

#親の図の散布図をプロット
ax.scatter(x, y, s=s)
ax.set_xlabel("xtitle", fontsize=fontsize)
ax.set_ylabel("ytitle", fontsize=fontsize)
#親の図の枠線を太くする
ax.spines["top"].set_linewidth(spines)
ax.spines["left"].set_linewidth(spines)
ax.spines["bottom"].set_linewidth(spines)
ax.spines["right"].set_linewidth(spines)
ax.tick_params(direction="in", length=length, width=width, labelsize=labelsize)

# ヒストグラムのビンの太さと数を指定
binwidth = 0.01
bins = 10
#ヒストグラムのプロット
#orientation='horizontal'とすることでy方向のヒストの向きを調節
ax_histx.hist(x, bins=bins)
ax_histy.hist(y, bins=bins, orientation='horizontal')
#ヒスト図の枠の太さを調節
ax_histx.spines["top"].set_linewidth(spines)
ax_histx.spines["left"].set_linewidth(spines)
ax_histx.spines["bottom"].set_linewidth(spines)
ax_histx.spines["right"].set_linewidth(spines)
ax_histx.tick_params(direction="in", length=length, 
               width=width, labelsize=labelsize)

ax_histy.spines["top"].set_linewidth(spines)
ax_histy.spines["left"].set_linewidth(spines)
ax_histy.spines["bottom"].set_linewidth(spines)
ax_histy.spines["right"].set_linewidth(spines)
ax_histy.tick_params(direction="in", length=length, 
               width=width, labelsize=labelsize)

f:id:Dajiro:20200514192446p:plain
枠線を太くした図その4

おわりに

これで枠の太さの調整の感覚が掴めたのではないでしょうか。本稿の執筆でも大いに参考にしましたが、Matplotlibの公式ドキュメントには数多くの事例が紹介されているので作図に悩んだらここを当たるのがベストだと思います。それと、ヒストグラム付き散布図についてはseabornを使うという方も少なくないのではないでしょうか?恐らく上記コードよりも簡単にプロットできると思います。ただ私も調査したのですが、seabornでヒスト付き散布図の枠線の調整をする方法がついぞ見つかりませんでした。seabornは事例もMatplotlibほどには載っていないため私の場合困ったときに解決するのに時間がかかります。なので細かい調整などが必要な場合にはMatplotlib、素早く情報を手に入れたいときはseabornという風に使い分けるのが良いのではないでしょうか。とはいえ、seabornの使い方すぐ忘れちゃうのでほとんどMatplotlibなわけですが・・・。