LoginSignup
13
14

More than 5 years have passed since last update.

matplotlib グラフ作成Tips (3) 白抜き記号と矢印

Last updated at Posted at 2017-10-23

1. はじめに

まずは作例を.

とある設計をするのに,上記のグラフを作りました.私は現在海外勤務中であり,日本語入りのグラフは意味を持たないので,アルファベットのみ使用していることはご勘弁を.

ちなみに,Qiita内におけるグラフは以下により大きさを調整しています.

<img src="https://qiita-image-store.s3.amazonaws.com/0/129300/1b0ae421-aa63-639d-745e-ad4f8b1cc0bc.png" width=70%>

このグラフの作図上のポイントは,白抜き記号の描画と矢印の扱いです.

白黒ベースで作図したい場合,白抜き記号はぜひ使いたいのですが,これを実現するのに以外に手間取りました.

また matplotlib の矢印は,quiver で描くのもいいのですが,どうも勝手がよくないので,私の場合は arrow を用いています.しかし,arrow は始点座標と増分を指定すると,そこまで線を引いて更に矢印の head_length 分先端がとびだしてしまいます.これを意図した位置で止めるのに自分なりの方法を使っています.

これら2点の扱いを含め,グラフの説明をしたいと思います.

2. プログラム解説

(1) データ読み込み

データは別途行った計算結果を np.loadtxt で読み込んでいます.
ファイル名,データファイルから読み込む列を usecols で指定,読み飛ばす行数を skiprows で指定.
読み込んだデータを hr と mre という numpy 配列に格納します.

data1 = np.loadtxt(fnameR1, usecols=(0,4),skiprows=1)
hr=data1[:,0]
mre=data1[:,1]

(2) 条件を満たすインデックスを取得

下の事例では,hs という numpy 1次元配列から,その値がy5(=72)となるインデックスを取り出します.
np.where はリストを返すようなので,その値が1つだけでこれをスカラーに変換する場合,i5=_i[0][0]としてやる必要があります.
これに気がつくのに結構時間がかかった.

y5=72
_i=np.where(hs==y5); i5=_i[0][0]

(3) グラフ全体像の指定

私の場合,グラフ全体像を指定する部分は,描画部分の前半にまとめています.
あとでコピペで使いまわすのに便利だから.
下の事例では,横軸数値は 0.2 ピッチ,縦軸数値は 5 ピッチで描画し,グリッドは横軸 0.1 ピッチ,縦軸 1 ピッチとしています.これを行うため,import matplotlib.ticker as tickして,tick.MultipleLocatorを使っています.

fsz=16
xmin=0; xmax=1.4; dx=0.2
ymin=60; ymax=85; dy=1
#fig=plt.figure(figsize=(8,10))
#plt.style.use('dark_background')
fig=plt.figure(figsize=(8,10),facecolor='w')
plt.rcParams["font.size"] = fsz
plt.rcParams['font.family'] ='sans-serif'
plt.xlim(xmin,xmax)
plt.ylim(ymin,ymax)
plt.xlabel('Bending moment $M\: / \:bh^2$ (MPa)')
plt.ylabel('Elevation (EL.m)')
plt.gca().xaxis.set_major_locator(tick.MultipleLocator(0.2))
plt.gca().yaxis.set_major_locator(tick.MultipleLocator(5.0))
plt.gca().xaxis.set_minor_locator(tick.MultipleLocator(0.1))
plt.gca().yaxis.set_minor_locator(tick.MultipleLocator(1.0))
plt.grid(which='both',color='#999999',linestyle='solid')

(4) 白抜き記号の描画

白抜き記号を描画するには,colorで色を指定した後,markerfacecolorで内部の色を指定します.下の事例では,黒い丸を黒い実線で結ぶというのが基本ですが,makerfacecolorを白に指定しているので,白抜き丸記号でプロットされるようになります.

plt.plot(ms1,hs,'o-',ms=8,color='#000000',markerfacecolor='#ffffff',label='90 nos Tens-rebar',lw=1.5)

(5) 矢印

グラフ左側に縦方向に範囲を指定する矢印を描いています.これを描くのに一苦労です.
matplotlib の arrow は head_length 分飛び出す特性があることは前述のとおりです.このため私の場合,以下の方法で対応しています.
操作の基本的な考え方を示します.

  • 矢印の線の部分は plot で,矢の部分は arrow で描画します.面倒ですがこの2ステップで一つの矢印を描きます.
  • 矢の幅 hw と長さ hl を指定します.
  • 下向き矢印の場合は,始点 y 座標を矢の長さ hl だけ上に取ります.
  • y 方向増分座標は図上で目立たないように小さくとります.ここでは -0.01 としました.
  • あとは facecolor と edgecolor による色の指定と,指定した矢の幅,長さとなるように head_width および head_length を指定します.
  • 上向き矢印の場合は,始点を矢の長さ分下により,小さな y 方向増分(ここでは +0.01 )を指定するようにします.
hw=0.015; hl=0.4
plt.arrow(_x,60+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y2-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)

(6) 画像の保存

デフォルトでは荒っぽいので dpi を指定しています.またデフォルトでは画像の余白が大きすぎるので,bbox_inches="tight", pad_inches=0.1で余白を小さくしています.

fnameF='fig_rc_cf2.png'
plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)

(7) その他

あとは色々な装飾をするのにごちゃごちゃやっています.

3. プログラム全文

import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick

fnameR1='_inp_req.txt'
fnameR2='_inp_uls.txt'
data1 = np.loadtxt(fnameR1, usecols=(0,4),skiprows=1)
hr=data1[:,0]
mre=data1[:,1]
data2 = np.loadtxt(fnameR2, usecols=(0,3),skiprows=0)
n=16
hs=data2[0:n,0]
ms1=data2[0*n:1*n,1]
ms2=data2[1*n:2*n,1]
ms3=data2[2*n:3*n,1]
ms4=data2[3*n:4*n,1]
ms5=data2[4*n:5*n,1]
hr=81-hr
hs=81-hs
y5=72
y4=68
y3=65
y2=62
_i=np.where(hs==y5); i5=_i[0][0]
_i=np.where(hs==y4); i4=_i[0][0]
_i=np.where(hs==y3); i3=_i[0][0]
_i=np.where(hs==y2); i2=_i[0][0]

# Drawing
fsz=16
xmin=0; xmax=1.4; dx=0.2
ymin=60; ymax=85; dy=1
#fig=plt.figure(figsize=(8,10))
#plt.style.use('dark_background')
fig=plt.figure(figsize=(8,10),facecolor='w')
plt.rcParams["font.size"] = fsz
plt.rcParams['font.family'] ='sans-serif'
plt.xlim(xmin,xmax)
plt.ylim(ymin,ymax)
plt.xlabel('Bending moment $M\: / \:bh^2$ (MPa)')
plt.ylabel('Elevation (EL.m)')
plt.gca().xaxis.set_major_locator(tick.MultipleLocator(0.2))
plt.gca().yaxis.set_major_locator(tick.MultipleLocator(5.0))
plt.gca().xaxis.set_minor_locator(tick.MultipleLocator(0.1))
plt.gca().yaxis.set_minor_locator(tick.MultipleLocator(1.0))
plt.grid(which='both',color='#999999',linestyle='solid')

plt.plot(mre,hr,'-',color='#999999',label='Required moment',lw=3)
plt.plot(ms1,hs,'o-',ms=8,color='#000000',markerfacecolor='#ffffff',label='90 nos Tens-rebar',lw=1.5)
plt.plot(ms2,hs,'o-',ms=8,color='#777777',markerfacecolor='#777777',label='70 nos Tens-rebar',lw=1.5)
plt.plot(ms3,hs,'s-',ms=8,color='#000000',markerfacecolor='#ffffff',label='50 nos Tens-rebar',lw=1.5)
plt.plot(ms4,hs,'s-',ms=8,color='#777777',markerfacecolor='#777777',label='30 nos Tens-rebar',lw=1.5)
plt.plot(ms5,hs,'D-',ms=8,color='#000000',markerfacecolor='#ffffff',label='10 nos Tens-rebar',lw=1.5)
plt.plot([xmin,xmax],[81,81],'--',color='#000000',lw=2)
_x=0.07; _dx=0.03
plt.plot([_x-_dx,ms2[i2]],[y2,y2],'-.',color='#000000',lw=1.5)
plt.plot([_x-_dx,ms3[i3]],[y3,y3],'-.',color='#000000',lw=1.5)
plt.plot([_x-_dx,ms4[i4]],[y4,y4],'-.',color='#000000',lw=1.5)
plt.plot([_x-_dx,ms5[i5]],[y5,y5],'-.',color='#000000',lw=1.5)
plt.plot([_x,_x],[60,81],'-',color='#000000',lw=1.5)
hw=0.015; hl=0.4
plt.arrow(_x,60+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y2-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y2+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y3-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y3+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y4-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y4+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y5-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,y5+hl,0,-0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.arrow(_x,81-hl,0, 0.01,fc='#000000',ec='#000000',head_width=hw,head_length=hl)
plt.text(xmin+0.02,81+0.2,'Top of retaining wall (EL.81.0)',fontsize=fsz-2,ha='left',va='bottom')
dd=0.01
_y=0.5*(60+y2); plt.text(_x-dd,_y,'T32'   ,rotation=90,va='center',ha='right',fontsize=fsz-2)
_y=0.5*(60+y2); plt.text(_x+dd,_y,'90 nos',rotation=90,va='center',ha='left' ,fontsize=fsz-2)
_y=0.5*(y2+y3); plt.text(_x-dd,_y,'T32'   ,rotation=90,va='center',ha='right',fontsize=fsz-2)
_y=0.5*(y2+y3); plt.text(_x+dd,_y,'70 nos',rotation=90,va='center',ha='left' ,fontsize=fsz-2)
_y=0.5*(y3+y4); plt.text(_x-dd,_y,'T32'   ,rotation=90,va='center',ha='right',fontsize=fsz-2)
_y=0.5*(y3+y4); plt.text(_x+dd,_y,'50 nos',rotation=90,va='center',ha='left' ,fontsize=fsz-2)
_y=0.5*(y4+y5); plt.text(_x-dd,_y,'T32'   ,rotation=90,va='center',ha='right',fontsize=fsz-2)
_y=0.5*(y4+y5); plt.text(_x+dd,_y,'30 nos',rotation=90,va='center',ha='left' ,fontsize=fsz-2)
_y=0.5*(y5+81); plt.text(_x+dd,_y,'T32 10 nos',rotation=90,va='center',ha='left' ,fontsize=fsz-2)
plt.legend(shadow=True,facecolor='#ffffff',fontsize=fsz-2)
plt.title('Wall height=21m, counterfort slope=1:0.4',loc='left',fontsize=fsz)

fnameF='fig_rc_cf2.png'
plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
plt.show()

以 上

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14