はじめに
技術文書に記載するグラフをきちんと描くのはそれなりに手間がかかります。フォントを整えたり、プロットサイズを調整したり、枠線を整えたりと、やることが多いのです。Pythonのグラフ描画ライブラリであるMatplotlibを使って、オフィシャルな感じのグラフを描く方法をまとめてみました。
素朴なグラフ
デフォルト設定のままグラフを描くと、下図のようになってしまいがちです。日本語は豆腐になり、プロットはなんとも言えない青っぽい丸。ちょっとこれでは初期設定が過ぎます。
ソースコード(クリックで展開)
import matplotlib.pyplot as plt
x_label = '温度 (℃)'
y_label = '引張強さ (N/mm2)'
# データの準備
x_data = [25, 100, 200, 300]
y_data = [550, 500, 300, 200]
# 散布図の作成
plt.scatter(x_data, y_data)
# 軸ラベルとタイトルを設定
plt.xlabel(x_label)
plt.ylabel(y_label)
# グラフを保存
plt.savefig('plot_00.png', bbox_inches='tight', dpi=300)
オフィシャルな感じのグラフ
技術文書にそのまま使えるレベルの、細部に気を使ったグラフにしてみます。
日本語対応
まず、豆腐(□)になっている日本語を表示させます。いや英語にしたらいいじゃん、と言われそうですが、日本語が必要な場合もあるのです。豆腐になっている理由は、デフォルトのフォントが日本語に対応していないからなので、日本語に対応したフォントを指定すれば解決します。
最も手軽なのは、pip install japanize-matplotlib する方法です。japanize-matplotlibはフォントファイル(IPAexゴシック)を内蔵しているため、確実に日本語が表示されます。
ソースコード(クリックで展開)
import matplotlib.pyplot as plt
import japanize_matplotlib
# 日本語の文字列
x_label = '温度 (℃)'
y_label = '引張強さ (N/mm2)'
# データの準備
x_data = [25, 100, 200, 300]
y_data = [550, 500, 300, 200]
# 散布図の作成
plt.scatter(x_data, y_data)
# 軸ラベルとタイトルを設定
plt.xlabel(x_label)
plt.ylabel(y_label)
# グラフを保存
plt.savefig('plot_01.png', bbox_inches='tight', dpi=300)
もろもろ調整
きちんとした感じのグラフにするため、ろもろもろ調整します。
- 目盛りの調整
- フォントサイズの調整
- 縦横比の調整
- グラフ範囲の調整
- 軸ラベルの調整(TeX表記)
- マーカーの調整
- 凡例の表示
下図の様なグラフであれば、技術文書に掲載しても問題ないレベルです。
ソースコード(クリックで展開)
import matplotlib.pyplot as plt
import japanize_matplotlib
# 目盛り
plt.rcParams['xtick.direction'] = 'in' # x軸の目盛りを内側表示
plt.rcParams['ytick.direction'] = 'in' # y軸の目盛りを内側表示
plt.rcParams['xtick.top'] = True # 上側の目盛りを表示
plt.rcParams['ytick.right'] = True # 右側の目盛りを表示
# 文字サイズ
plt.rcParams['font.size'] = 12
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
# 日本語の文字列
x_label = r'温度 $T\ (^{\circ}\mathrm{C})$'
y_label = r'引張強さ $\sigma_{0.2}\ (\mathrm{N/mm^2})$'
# データの準備
x_data = [25, 100, 200, 300]
y_data = [550, 500, 300, 200]
x_data2 = [25, 100, 200, 300]
y_data2 = [500, 450, 290, 190]
# 軸範囲
xmin, xmax = 0, 350
ymin, ymax = 0, 600
# オブジェクト指向スタイル(fig, ax)を使用
fig, ax = plt.subplots(figsize=(6, 4)) # インチ指定
# 散布図のプロット
ax.scatter(x_data, y_data,
c='black', # 色:黒
marker='o', # マーカー種類: 丸
s=50, # プロットサイズ
label='試験片A', # ラベル
zorder=3) # 重なり順
ax.scatter(x_data2, y_data2,
facecolors='white', # 中身を白
edgecolors='black', # 縁取を黒
marker='s', # マーカー種類: 四角(square)
s=50,
label='試験片B',
zorder=2)
# 軸範囲の指定
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# 軸ラベル
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
ax.legend(frameon=True) # 凡例
plt.tight_layout() # グラフの余白を自動調整
# グラフを保存
plt.savefig('plot_02.png', bbox_inches='tight', dpi=300)
各種グラフ事例
よく使いそうなグラフ事例です。
疲労試験結果
ソースコード
import matplotlib.pyplot as plt
import japanize_matplotlib
# 目盛り
plt.rcParams['xtick.direction'] = 'in' # x軸の目盛りを内側表示
plt.rcParams['ytick.direction'] = 'in' # y軸の目盛りを内側表示
# 文字サイズ
plt.rcParams['font.size'] = 12
plt.rcParams['axes.labelsize'] = 14
plt.rcParams['xtick.labelsize'] = 12
plt.rcParams['ytick.labelsize'] = 12
# 日本語の文字列
x_label = r'破断繰返し数 $N_f$'
y_label = r'応力振幅 $\sigma_{a}\ (\mathrm{MPa})$'
# データの準備
# 破断データ (Broken)
nf_broken = [1e4, 1.5e4, 2e4, 3.5e4, 5e4, 2e5]
stress_broken = [400, 400, 300, 300, 250, 200]
# 未破断データ (Run-out)
nf_runout = [1e6, 1e6] # 10^6回で打ち切り
stress_runout = [170, 150] # その時の応力
# 軸範囲
xmin, xmax = 1e3, 1e7
ymin, ymax = 0, 500
# オブジェクト指向スタイル(fig, ax)を使用
fig, ax = plt.subplots(figsize=(6, 4)) # インチ指定
# 散布図のプロット
ax.scatter(nf_broken, stress_broken,
facecolors='white', # 中身を白
edgecolors='black', # 縁取を黒
marker='o', # マーカー種類: 丸
s=50, # プロットサイズ
label='試験片A', # ラベル
zorder=3) # 重なり順
ax.scatter(nf_runout, stress_runout,
facecolors='white', # 中身を白
edgecolors='black', # 縁取を黒
marker='o', # マーカー種類: 丸
s=50,
label='試験片B',
zorder=2)
# 未破断データに矢印を追加する
for x, y in zip(nf_runout, stress_runout):
ax.annotate(
'', # テキストは空にする
xy=(x, y), # 矢印の始点(データ点)
xytext=(20, 0), # 矢印の終点(データ点から右に20ポイントずらす)
textcoords='offset points', # 「ポイント単位」でずらす指定
arrowprops=dict(
arrowstyle="<-", # 矢印の形
color='black', # 線の色
shrinkA=0, shrinkB=0 # 始点・終点の隙間をなくす
),
zorder=1 # プロットの下に矢印が来るように調整
)
# 軸範囲の指定
ax.set_xscale('log') # x軸を対数にする
ax.set_xlim(xmin, xmax)
ax.set_ylim(ymin, ymax)
# 軸ラベル
ax.set_xlabel(x_label)
ax.set_ylabel(y_label)
# ax.legend(frameon=True) # 凡例
plt.tight_layout() # グラフの余白を自動調整
# グラフを保存
plt.savefig('plot_03.png', bbox_inches='tight', dpi=300)
疲労試験やクリープ試験では、未破断のデータに矢印を付けることがよくあります。これは ax.annotate を使って、データ点を起点に矢印を描くことで実現できます。
for x, y in zip(nf_runout, stress_runout):
ax.annotate(
'', # テキストは空にする
xy=(x, y), # 矢印の始点(データ点)
xytext=(20, 0), # 矢印の終点(データ点から右に20ポイントずらす)
textcoords='offset points', # 「ポイント単位」でずらす指定
arrowprops=dict(
arrowstyle="<-", # 矢印の形
color='black', # 線の色
shrinkA=0, shrinkB=0 # 始点・終点の隙間をなくす
),
zorder=1 # プロットの下に矢印が来るように調整
)
ax.annotate は本来、「ある点(xy)に対して、注釈テキスト(xytext)から矢印を引っ張る」ための機能なので、未破断を示す場合には矢印の向きを逆( <- )にしている点に注意です。
