【Python】matplotlibでグラフの形を整えてみた(サブプロット編その1)

 

こんにちは、うさじんです。
本記事では前回、正弦波をpandas.DataFrame.plotでグラフを作りましたが、
それと同じことをmatplotlibのsubplotsで行ってみました。

前回の記事は以下です。

関連記事

  こんにちは、うさじんです。 前回の記事では【Excel】の散布図グラフを使ったデータの視覚化を目的としました。 今回は【Python】を使ってデータのグラフ化をやってみました。 前回記事 データを【Ex[…]

 

 

前回のおさらい pandas.DataFrame.plot の実行結果

前回、pandas.DataFrame.plotを使って4種の正弦波データについて
サブプロット4✕1、2✕2でデータ資料を作成しました。

図A グラフデータ資料1
図B グラフデータ資料2

しかし、余分な余白が生じたり、サブプロット時は縦軸・横軸の軸ラベルが消失してしまう等のライブラリ機能の限界がありました。
(もし解決方法がありましたらご教示いただけると助かります。)

もともとDataFrame.plotはpandasの機能の一つで、グラフ作成能力はmatplotlibのほうが秀でているようです。

matplotlibでの結果

とりあえずはじめは力技でやってみました。コードは以下です。

import pandas as pd #pandasのインポート
import matplotlib as mpl
from matplotlib import pyplot as plt
import japanize_matplotlib #日本語ライブラリのインポート

#データの1行目を読み込み。
df = pd.read_csv("test_data.csv", skiprows=[1])
df2= df.rename(columns={'Unnamed: 0':'Time[sec]'}) #時刻列の行名を変更
print(df2)

#Figureの作成
fig = plt.figure(figsize=(8, 10), tight_layout=True) #figの設定、余分な余白削除
fig.suptitle('サンプルデータ 正弦波', weight=5, fontsize=16) #グラフタイトルの設定、太さ、サイズ

#ax、サブプロットの作成
ax1 = fig.add_subplot(4,1,1, xlabel="time[sec]", ylabel="振幅[-]") #グラフが一つの場合は(1, 1, 1)で可
ax1.set_title(df2.columns[1], loc='left') #軸のタイトル、位置
ax1.grid() #デフォルトはmajor
ax1.set_xlim(0, 10) #横軸の最小値、最大値
ax1.set_ylim(-1.5, 1.5) #縦軸の最小値、最大値
#ax1.xaxis.set_major_locator(mpl.ticker.LinearLocator(11)) #軸目盛りを特定値-1の当分に分割
#ax1.yaxis.set_major_locator(mpl.ticker.LinearLocator(7)) #軸目盛りを特定値-1の当分に分割
ax1.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1)) #軸目盛りを特定値の倍数当分に分割
ax1.yaxis.set_major_locator(mpl.ticker.MultipleLocator(0.5)) #軸目盛りを特定値の倍数当分に分割
ax1.plot(df2.iloc[:,0], df2.iloc[:,1]) #データの指定、凡例追加の場合は label=df2.columns[]で指定し、ax.legend()で表示

ax2 = fig.add_subplot(4,1,2, xlabel="time[sec]", ylabel="振幅[-]")
ax2.set_title(df2.columns[2], loc='left')
ax2.grid()
ax2.set_xlim(0, 10)
ax2.set_ylim(-1.5, 1.5)
ax2.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax2.yaxis.set_major_locator(mpl.ticker.MultipleLocator(0.5))
ax2.plot(df2.iloc[:,0], df2.iloc[:,2])

ax3 = fig.add_subplot(4,1,3, xlabel="time[sec]", ylabel="振幅[-]")
ax3.set_title(df2.columns[3], loc='left')
ax3.grid()
ax3.set_xlim(0, 10)
ax3.set_ylim(-1.5, 1.5)
ax3.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax3.yaxis.set_major_locator(mpl.ticker.MultipleLocator(0.5))
ax3.plot(df2.iloc[:,0], df2.iloc[:,3])

ax4 = fig.add_subplot(4,1,4, xlabel="time[sec]", ylabel="振幅[-]")
ax4.set_title(df2.columns[4], loc='left')
ax4.grid()
ax4.set_xlim(0, 10)
ax4.set_ylim(-1.5, 1.5)
ax4.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax4.yaxis.set_major_locator(mpl.ticker.MultipleLocator(0.5))
ax4.plot(df2.iloc[:,0], df2.iloc[:,4])
図C 実行結果 fig

 

うーん、この。って感じですね。
実行結果は問題ない感じなのですが、コードのスマートさが一つも感じられません。
各グラフを個別に設定したい場合はいいんですけどね。

ちょっと改修します。

 

コードの改修後

以下、改修したコードです。

import pandas as pd #pandasのインポート
import matplotlib as mpl
from matplotlib import pyplot as plt
import japanize_matplotlib #日本語ライブラリのインポート

#データの1行目を読み込み。
df = pd.read_csv("test_data.csv", skiprows=[1])
df2= df.rename(columns={'Unnamed: 0':'Time[sec]'}) #時刻列の行名を変更
print(df2)

fig, axes = plt.subplots(len(num_list)-1, 1, #サブプロットの行数、列数
              squeeze=False, #squeeze=Falseで二次元配列とする
              figsize=(8, 10), #figureのサイズ
              tight_layout=True) #Tight_layoutの設定
fig.suptitle('サンプルデータ 正弦波(fig, ax = plt.subplots版)', weight=5, fontsize=16) #グラフタイトルの設定、太さ、サイズ

for row in range(len(num_list)-1):
  axes[row, 0].plot(df2.iloc[:,0], df2.iloc[:,row + 1])
  axes[row, 0].set_title(df2.columns[row + 1], loc='left')
  axes[row, 0].grid() #デフォルトはmajor
  axes[row, 0].set_xlabel("time[sec]") #横軸の軸ラベル
  axes[row, 0].set_ylabel("振幅[-]") #横軸の軸ラベル
  axes[row, 0].set_xlim(0, 10) #横軸の最小値、最大値
  axes[row, 0].set_ylim(-1.5, 1.5) #縦軸の最小値、最大値
  axes[row, 0].xaxis.set_major_locator(mpl.ticker.MultipleLocator(1)) #軸目盛りを特定値の倍数当分に分割
  axes[row, 0].yaxis.set_major_locator(mpl.ticker.MultipleLocator(0.5)) #軸目盛りを特定値の倍数当分に分割

実行結果

図D 実行結果 ig, axes = plt.subplots版

For文でサブプロットグラフを繰り返すことで、コードを大幅に短縮しました。
注意点としては、サブプロットの行数、列数を数える際と
For文での繰り返し回数の数字を混同しないようにすることです。

また、plt.subplots の tight_layout=True は必ず入れておきます。
自動で位置を調整するのは神だと思いました。

サブプロットグラフは描き方が沢山あるので工夫次第ではいろいろな設定が可能です。
コードの見やすさ、設定の容易さを考えながら、目的の様式を作っていくことが良いと思います。

次回はこのサブプロットグラフのもう一つの描き方を記事にしたいと思います。

よろしくお願いします。