LoginSignup
15
15

More than 5 years have passed since last update.

Python matplotlibで棒グラフ(ハッチングと2軸グラフの凡例)

Last updated at Posted at 2018-08-26

はじめに

この記事を紹介しようと思ったきっかけは以下の通りです.

  • データ比較用の棒グラフを白黒で作成したかったので,ハッチングにより対応することにしました.色とりどりのグラフはよく見かけますが,今の時代に白黒グラフは以外に少ない?
  • 2軸グラフを作成する時,凡例部をうまくプログラムできないかと考えていたのですが,気に入った方法が見つかったので,実践してみることにしました.

棒グラフのプログラムは,こちらのサイト を参考に,自前のものを書き換えています.

なお,当方の環境は以下のとおりです.

  • MacBook Pro (Retina, 13-inch, Mid 2014)
  • macOS High Sierra
  • Python 3.7.0
  • matplotlib 2.2.2

ハッチングパターン

下の図は,棒グラフ本体は描画せずに,凡例だけを描画し,余白削除をしたものです.

上の図を描くコードは以下の通り.

  • plt.axis('off') で軸を消しています.
  • 棒のデータはいれず,プロット位置と棒の幅は,plt.plot([0][0],width=0, ...) とし,ラベルとハッチングパターンのみを指定します.この時,それぞれを変数,g1, ... g9 に格納します.
  • plt.legend で handlers に g1 から g9 を入れて凡例を描画します.
  • ハッチングパターンは,x とか xx とか,描きたいパターンの記号の数を増やすと細かくなります.
  • この方法は,matplotlib Legend guide を参考にしました.

プログラム全文

import matplotlib.pyplot as plt


def main():
    fsz=20   # fontsize
    fig=plt.figure(facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='monospace'
    plt.axis('off')

    g1=plt.bar([0],[0],width=0,align='center',label='hatch="x"',fill=None,hatch='x')
    g2=plt.bar([0],[0],width=0,align='center',label='hatch="xx"',fill=None,hatch='xx')
    g3=plt.bar([0],[0],width=0,align='center',label='hatch="xxx"',fill=None,hatch='xxx')
    g4=plt.bar([0],[0],width=0,align='center',label='hatch="//"',fill=None,hatch='//')
    g5=plt.bar([0],[0],width=0,align='center',label='hatch="||"',fill=None,hatch='||')
    g6=plt.bar([0],[0],width=0,align='center',label='hatch=".."',fill=None,hatch='..')
    g7=plt.bar([0],[0],width=0,align='center',label='hatch="++"',fill=None,hatch='++')
    g8=plt.bar([0],[0],width=0,align='center',label='hatch="--"',fill=None,hatch='--')
    g9=plt.bar([0],[0],width=0,align='center',label='hatch="*"',fill=None,hatch='*')
    # 凡例をまとめて描画
    plt.legend(handles=[g1,g2,g3,g4,g5,g6,g7,g8,g9],loc='best',shadow=True)

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


#==============
# Execution
#==============
if __name__ == '__main__': main()

2軸の棒グラフ

2軸棒グラフの描画事例を以下に示します.これは筆者が Python matplotlibで棒グラフ(2軸グラフと積み上げグラフ) に投稿したプログラムを書き換えたものです.

fig_bar2.jpg

上記グラフのコードを以下に示します.

左軸グラフのデータ描画部を g1=plt.bar(x,hp,width=w, ...) とし,右軸グラフのデータ描画部を,g2=plt.bar(x+w,lp,width=w, ...) として,凡例を,plt.legend(handles=[g1,g2], ...) で描画しています.

なお,元データをプロット時に並び替えたいので,文字列も含めて numpy 配列にしています.並び替えは numpy.argsort で昇順のインデックスを取得し,あとは一気に書き換えます.

プログラム全文

import matplotlib.pyplot as plt
import numpy as np


def main():
    # プロットデータ
    site=np.array(['L1','K ','M ','Q ','K1 ','J ','J1','G ','H ','I '])
    hdam=np.array([ 189, 108,  94, 186, 145, 138, 169, 108, 95,  90])
    lway=np.array([2304,4312,4300,2195,3999,3808,2987,3193,5034,5838])

    # データの並び替え
    ii=np.argsort(hdam)
    sp=site[ii]
    hp=hdam[ii]
    lp=lway[ii]

    x=np.arange(len(sp)) # x座標
    w=0.3 # 棒の幅

    fsz=16
    fig=plt.figure(figsize=(12,6),facecolor='w')
    plt.rcParams["font.size"] = fsz
    # 左軸グラフの描画
    plt.ylim(0,300)
    plt.ylabel('Dam height (m)')
    plt.xlabel('Site name')
    plt.xticks(x+w/2,sp,rotation=0)
    plt.grid(color='#999999',linestyle='--')
    g1=plt.bar(x,hp,width=w,align='center',label='Dam height (m)',fill=None,hatch='xx')
    # 右軸グラフの描画
    plt.twinx()
    plt.ylim(0,6000)
    plt.ylabel('Waterway length (m)')
    g2=plt.bar(x+w,lp,width=w,align='center',label='Waterway length (m)',fill=None,hatch='/')
    # 凡例をまとめて描画
    plt.legend(handles=[g1,g2],loc='best',shadow=True)

    plt.tight_layout()
    fnameF='fig_bar2.jpg'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0.1)
    plt.show()


#---------------
# Execute
#---------------
if __name__ == '__main__': main()

1軸の棒グラフ

上記と似た事例として,1軸のデータ比較用棒グラフを示しておきます.

fig_bar1.jpg

下に示すコードの中ではコメントアウトしていますが,これは1軸グラフなので,もちろん

plt.bar(x,  y1,width=w,label='with crane load',  align='center',lw=1,fill=None,hatch='xx')
plt.bar(x+w,y2,width=w,label='without crne load',align='center',lw=1,fill=None,hatch='/')
plt.legend(shadow=True,loc='best')

でも問題ありません.

プログラム全文

import matplotlib.pyplot as plt
import numpy as np


def main():
    # プロットデータ
    y1=[8127.220, 3109.375, 3683.840, 6504.671, 11687.447, 13068.109, 3993.506, 5995.114, 7050.279, 4058.274]
    y2=[1989.080, 2133.271, 4057.118, 5238.903, 10335.058, 11830.916, 3870.442, 5212.632, 5389.747, 2943.048]
    ss= ['B-C','B-1','B-2','B-3','B-A','B-P','C-E','C-I','B-W','C-W']

    x = np.arange(len(y1)) # x座標 
    w = 0.3  #棒の幅

    fsz=16
    fig=plt.figure(figsize=(12,6),facecolor='w')
    plt.rcParams["font.size"]=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.ylim([0,15000])
    plt.ylabel('Max. bending moment (kN-m)')
    plt.xticks(x+w/2,ss,rotation=60)
#    plt.bar(x,  y1,width=w,label='with crane load',  align='center',lw=1,fill=None,hatch='xx')
#    plt.bar(x+w,y2,width=w,label='without crne load',align='center',lw=1,fill=None,hatch='/')
#    plt.legend(shadow=True,loc='best')
    g1=plt.bar(x,  y1,width=w,label='with crane load',  align='center',lw=1,fill=None,hatch='xx')
    g2=plt.bar(x+w,y2,width=w,label='without crne load',align='center',lw=1,fill=None,hatch='/')
    plt.legend(handles=[g1,g2],loc='best',shadow=True)
    fnameF='fig_bar1.jpg'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0.1)
    plt.show()


#---------------
# Execute
#---------------
if __name__ == '__main__': main()

おまけで余白削除Pythonプログラム

プログラムが格納されているディレクトリ内の jpg ファイルを検索し,余白削除を行うプログラムを紹介しておきます.参考記事を以下に示します・

from PIL import Image, ImageChops
import glob, os

def trim(im, border):
  bg = Image.new(im.mode, im.size, border)
  diff = ImageChops.difference(im, bg)
  bbox = diff.getbbox()
  if bbox:
    return im.crop(bbox)


def main():
    lfig=[os.path.basename(r) for r in glob.glob('*.jpg')]
    for fig in lfig:
        img_org=Image.open(fig,'r')
        img_new=trim(img_org,'#ffffff')
        img_new.save(fig, 'JPEG', quality=100, optimize=True)
        img_new.show()


#==============
# Execution
#==============
if __name__ == '__main__': main()

以 上

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