はじめに
表を画像出力するニーズが生じたのでやってみた。方法は2つ。
(1)matplotlib.table
を使う
(2)自前実装としてRectangle
の中にtext
を描画
作例
まずは作例を示そう。見た目は同じものが作れる。
(1)matplotlib.table使用
(2)Rectangleの中にtextを描画
プログラムの解説
fig_trim
余白削除
inp_data
データを定義
_data
: numpy2次元配列(dtype=object)にデータを格納。
関数からの戻り値では、すべて文字列にするが、数値データは小数点以下3桁に統一する。
tstr
: タイトル
ss
: 表の下に入れる注釈の文字列
mp_tbl
Matplotlib.tableを使った表の描画。
my_tbl
表出力の自前実装。patches.Rectangleで枠を作成し、その中にtextを描画していく
fig
表の全体像の定義。
表の描画可能領域を(0、1)、(0、1)とする。表の大きさは横1.0、縦0.25とする。この表の大きさは、
tw,th=1.0,0.25
で定義。
表の上端をy=1.0
とすることにより、グラフ領域上端直上にtitle
で表タイトルを描画。
注書は、表の下の空き領域に描画。
main
作図の2つのケースを制御
プログラム
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image, ImageChops
def fig_trim(fig):
def trim(im, border):
bg = Image.new(im.mode, im.size, border)
diff = ImageChops.difference(im, bg)
bbox = diff.getbbox()
return im.crop(bbox)
img_org=Image.open(fig,'r')
img_new=trim(img_org,'#ffffff')
print(img_new.size)
img_new.save(fig, 'JPEG', quality=100, optimize=True)
def inp_data():
_data=np.array([
['Class','C15','C20','C25','C30','C35','C40','C45','C50'],
['fc (MPa)',12,16,20,25,29,32,35,40],
['ft (MPa)',1.206,1.46,1.695,1.966,2.171,2.318,2.461,2.69],
['cc (MPa)',1.902,2.417,2.911,3.506,3.967,4.307,4.64,5.187],
['phi (deg)',54.827,56.379,57.541,58.666,59.396,59.871,60.298,60.923]
],dtype=object)
n,m=_data.shape
data=np.empty((n,m),dtype=object)
for i in range(n):
for j in range(m):
s=_data[i,j]
try:
s=float(s)
data[i,j]=f'{s:.3f}'
except ValueError:
data[i,j]=s
tstr='Strength parameter (concrete)'
s=[]
s.append(' * Class means concrete grade by BS (cube strehgth)')
s.append(' * fc means concrete grade by EN (cylinder strength)')
s.append(' * fc and ft mean compressive strength and tensile strength')
s.append(' * cc and phi mean cohesion and internal friction angle')
ss='\n'.join(s)
return data,tstr,ss
def mp_tbl(data,dpara,fsz):
xmin,xmax=dpara[0],dpara[1]
ymin,ymax=dpara[2],dpara[3]
tw,th=dpara[4],dpara[5]
colw=dpara[6]
x0,y0,dx,dy=xmin,ymax-th,tw,th
tbl=plt.table(
cellText=data,
colWidths=colw,
bbox=[x0,y0,dx,dy]
)
tbl.auto_set_font_size(False)
tbl.set_fontsize(fsz)
n,m=data.shape
for i in range(n):
for j in range(m):
col_fc='#ffffff'
col_tx='#000000'
hha,vva='right','center'
if i==0 or j==0:
col_fc='#dddddd'
hha='center'
tbl[i,j].set_linewidth(0.3)
tbl[i,j].set_text_props(ha=hha,va=vva)
tbl[i,j].set_facecolor(col_fc)
tbl[i,j].set_text_props(color=col_tx)
def my_tbl(data,dpara,fsz):
xmin,xmax=dpara[0],dpara[1]
ymin,ymax=dpara[2],dpara[3]
tw,th=dpara[4],dpara[5]
colw=dpara[6]
n,m=data.shape
dh=th/n
for i in range(n):
for j in range(m):
col='#ffffff'
x0=xmin+np.sum(colw[0:j])
y0=ymax-dh*(i+1)
if i==0 or j==0: col='#dddddd'
if j==0: x0=xmin
r=patches.Rectangle(
xy=(x0,y0),
width=colw[j],
height=dh,
edgecolor='#000000',
facecolor=col,
linewidth=0.3
)
plt.gca().add_patch(r)
xs=x0+colw[j]-0.1*colw[j]
ys=y0+0.5*dh
hha,vva='right','center'
if i==0 or j==0:
xs=x0+0.5*colw[j]
hha='center'
plt.text(xs,ys,data[i,j],ha=hha,va=vva,fontsize=fsz)
def fig(nnn,fnameF):
data,tstr,ss=inp_data()
n,m=data.shape
tw,th=1.0,0.25 # table width, table height
col=np.array([1.5]+[1]*(m-1))
colw=col/np.sum(col)*tw
fsz=7
xmin,xmax= 0, 1
ymin,ymax= 0, 1
iw,ih=6.4, 4.8 # default (inch)
plt.figure(figsize=(iw,ih),facecolor='w')
plt.rcParams['font.size']=fsz
plt.rcParams['font.family']='sans-serif'
plt.xlim([xmin,xmax])
plt.ylim([ymin,ymax])
plt.axis('off')
dpara=[xmin,xmax,ymin,ymax,tw,th,colw]
if nnn==1: mp_tbl(data,dpara,fsz)
if nnn==2: my_tbl(data,dpara,fsz)
# additional text
tstr=f'{tstr} ({fnameF})'
plt.title(tstr,x=0.5*tw/(xmax-xmin),fontsize=fsz)
plt.text(xmin,ymax-th-0.01,ss,ha='left',va='top',fontsize=fsz,linespacing=1.5)
plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
def main():
for nnn in [1,2]:
if nnn==1: fnameF='fig_mp_table.jpg'
if nnn==2: fnameF='fig_my_table.jpg'
fig(nnn,fnameF)
fig_trim(fnameF)
#---------------
# Execute
#---------------
if __name__ == '__main__': main()
感想
あまり表示を気にすることなく、ドカンと表を描画するならmatplotlib .table
もコード量が少なくていいが、自前実装してもそれほど’苦にならないコード量である。
次の投稿で示すが、罫線を自由に制御したい場合、matplotlib.table
よりも、Rectabgle + text
の方が楽に制御できる気がする。
以 上