0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

matplotlib: 表を作る(1)tableと自前実装(2025.08.29)

Posted at

はじめに

表を画像出力するニーズが生じたのでやってみた。方法は2つ。

(1)matplotlib.tableを使う
(2)自前実装としてRectangleの中にtextを描画

作例

まずは作例を示そう。見た目は同じものが作れる。

(1)matplotlib.table使用

fig_mp_table.jpg

(2)Rectangleの中にtextを描画

fig_my_table.jpg

プログラムの解説

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 の方が楽に制御できる気がする。

以 上

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?