【コピペで使える】matplotlibを使ったX軸に日付が指定できるグルーピング棒グラフ

当ページのリンクには広告が含まれています。

以前の記事で、グループ化した棒グラフが簡単に作成できる関数とクラスを紹介しました。

その時は、X軸に文字列ラベルを指定するようになっていましたが、今回は datetime型の日付が指定できるよう変更したものを紹介します。

コピー&ペーストして張り付けていただければ、すぐに使えるようになっていますので、X軸に日付を指定したい場合は、是非お役立て下さい。

目次

グループ化した棒グラフ(X軸日付対応)の描画サンプル

X軸に設定する値が文字列から 日付(datetime 型)に変わった以外は、前回のグループ化棒グラフと同じです。

グループ化した棒グラフ(x軸日付対応)関数

「グループ化した棒グラフ関数」の仕様は次の通りです。

grouped_bar(x軸の値,y軸の値,フォーマット,x軸の角度,数値の表示/非表示)

引数内容具体例
x軸の値X軸の値をdatetime型のリスト形式で指定[datetime(2021,7,1)datetime(2021,7,2),datetime(2021,7,3)]
y軸の値各棒グラフごとに「ラベル」と「値のリスト」を
タプルでまとめ、リストに登録したもの
( 'chart1' , [4,1,7,6,3] )
フォーマットX軸に表示する日付けのフォーマット'%Y/%m/%d'
X軸の角度X軸に表示する日付の角度45
数値の表示/非表示Trueで棒の上部に値を表示、Falseで非表示True 、False

下記サンプルは、X軸に2020年10月1日から5日間のdatetime データを生成してセットする内容になっています。


y = []
y.append(('chart1',[4,1,7,6,3]))
y.append(('chart2',[5,5,2,8,1]))
y.append(('chart3',[3,4,8,4,3]))
y.append(('chart4',[2,5,6,2,8]))
y.append(('chart5',[7,3,3,3,2]))
x = [datetime.datetime(2020,10,1) + datetime.timedelta(days=i) for i in range(5)]

grouped_bar(x,y,'%m/%d',45,True)

下記が実際の関数のソースコードです。

そのままコピー&ペーストして、適当にカスタマイズしてお使いください。

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime

def grouped_bar(x_datetime,y,format,rotation=0,isvalue=False,groupwidth=0.8,barwidth=0.9,location='best'):
    '''
    グループ化した棒グラフの描画

    Parameters
    ----------------------------
    isvalue:bool
        グラフ上部への値の表示/非表示
    groupwidth:float
        1つのグループの幅 を 0~1で指定。
    barwidth:float
        グループ内の棒グラフ1つ当たりの幅を0~1で指定。
    location:int,str
        凡例の表示場所
    '''
    fig = plt.figure()
    ax = fig.add_subplot()
    
    x = mdates.date2num(x_datetime)
    barcnt = len(y)
    size = groupwidth / barcnt
    xx = groupwidth/ 2
    
    # 棒グラフの描画
    for i in range(barcnt):
        pos =  [size / 2 + x[n] - xx + size * i for n in range(len(x))]
        rect = plt.bar(mdates.num2date(pos), y[i][1], width=size * barwidth, label=y[i][0], align="center")
        
        # グラフに値を表示
        if isvalue == True:
            for tx, ty in zip(pos, y[i][1]):
                plt.text(tx, ty, ty, ha='center', va='bottom')
    
    # 凡例
    ax.legend (loc=location)
    
    #グラフ表示エリアの下左右マージン設定
    plt.subplots_adjust(bottom=0.15)
    
    # X軸のラベルを表示
    ax.xaxis.set_major_formatter(mdates.DateFormatter(format))
    ax.xaxis.set_major_locator(mdates.DayLocator())
    ax.set_xlim(x_datetime[0] - datetime.timedelta(days=0.5),x_datetime[-1] + datetime.timedelta(days=0.5))
    ax.tick_params(axis='x', rotation=rotation)
    plt.show()

グループ化した棒グラフ(X軸日付対応)クラス

こちらもX軸に日付(datetime型)を指定する以外は全く同じです。

専用のメソッドを使ってX軸のラベルとY軸の値を登録し、最後に showメソッドを呼ぶことで描画されます。

機能メソッド名引数
コンストラクタGroupedBar
(
isvalue=False,
groupwith=0.8,
barwidth=0.9,
location='best'
)
値を棒の上に表示,
棒グラフグループの幅,
棒グラフの幅,
凡例の表示位置
X軸ラベルの登録set_xlabel(xlabel,format,rotation=0)Xラベルのリスト,
フォーマット,
X軸の日付けの角度
Y軸ラベルと
データの登録
add_bar(ylabel,values)Y軸のラベルのリスト,      
Y軸の値のリスト
グラフの表示show()

具体的な使用方法は次の通りです。

gb=GroupedBar()
gb.add_bar('chart1',[4,1,7,6,3])
gb.add_bar('chart2',[5,5,2,8,1])
gb.add_bar('chart3',[3,4,8,4,3])
#gb.add_bar('chart4',[2,5,6,2,8])
#gb.add_bar('chart5',[7,3,3,3,2])
gb.set_xlabel( [datetime.datetime(2020,10,1) + datetime.timedelta(days=i) for i in range(5)],'%m/%d',45)

gb.show()

下記が実際のクラスのソースコードになります。

そのままコピー&ペーストして、適当にカスタマイズしてお使いください。

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import datetime

class GroupedBar:
    '''
    グループ化した棒グラフを描画するためのヘルパークラス
    '''
    
    def __init__(self,isvalue=False,groupwidth=0.8,barwidth=0.9,location='best'):
        '''
        コンストラクタ
        
        Parameters
        ----------------------------
        isvalue:bool
            グラフ上部への値の表示/非表示
        groupwidth:float
            1つのグループの幅 を 0~1で指定。
        barwidth:float
            グループ内の棒グラフ1つ当たりの幅を0~1で指定。
        location:int,str
            凡例の表示場所
        '''
        self.groupwidth = groupwidth
        self.barwidth = barwidth
        self.location = location 
        self.isvalue = isvalue
        self.values = []
        self.xlabel = []
        self.groupwidth=0.8
        self.barwidth = 0.9
        self.location='best'
        self.isvalue=False
        self.rotation = 0
        self.format = ''
    
    def set_xlabel(self,xlabel,format,rotation=0):
        '''
        X軸ラベルの登録
    
        Parameters
        ----------------------------
        xlabel:[str]
            X軸のラベルをリストで指定
        '''
        self.xlabel = xlabel
        self.format = format
        self.rotation = rotation
 
    def add_bar(self,ylabel,values):
        '''
        棒グラフの登録
        
        Parameters
        ----------------------------
        ylabel:str
            棒グラフのラベル
        values:[int,float]]
            y軸の値をリストで指定
        '''
        self.values.append((ylabel,values)) 
    
    def show(self):
        '''
        棒グラフの描画
        '''
        x = mdates.date2num(self.xlabel)
        barcnt = len(self.values)
        diff = abs(x[0] - x[1])
        size = (self.groupwidth / barcnt) * diff
        xx = (self.groupwidth/ 2) * diff
              
        fig = plt.figure()
        ax = fig.add_subplot()

        # 棒グラフの描画
        for i in range(barcnt):
            pos = [size / 2 + x[n] - xx + size * i for n in range(len(x))]
            rect = plt.bar(mdates.num2date(pos), self.values[i][1], width=size * self.barwidth,
                           label=self.values[i][0], align="center")
            
            # グラフに値を表示
            if self.isvalue == True:
                for tx, ty in zip(pos, self.values[i][1]):
                    plt.text(tx, ty, ty, ha='center', va='bottom')
        # 凡例
        plt.legend (loc=self.location)
        
        #グラフ表示エリアの下マージン設定
        plt.subplots_adjust(bottom=0.15)
        
        # X軸のラベルを表示
        ax.xaxis.set_major_formatter(mdates.DateFormatter(self.format))
        ax.xaxis.set_major_locator(mdates.DayLocator())
        ax.set_xlim(self.xlabel[0] - datetime.timedelta(days=0.5),self.xlabel[-1] + datetime.timedelta(days=0.5))
        ax.tick_params(axis='x', rotation=self.rotation)
        plt.show() 

前回からの変更点

各X軸の値を中心に、複数の棒グラフ左右に振り分けるため、棒の位置や幅を計算していたのですが、X軸の値が datetime 型になったことにより、そのままでは計算できなくなりました。

そこで、X軸で渡された datetime型の値を date2num を使って一旦シリアル値に変換することで、棒の位置や幅を検索するロジックをそのまま利用できるようにしました。

x = mdates.date2num(self.xlabel)

そして、実際に棒を表示する際は、 num2date を使ってシリアル値からdatetime型に再び変換し、plt.barに渡すようにしました。

pos = [size / 2 + x[n] - xx + size * i for n in range(len(x))]
rect = plt.bar(mdates.num2date(pos), self.values[i][1], width=size * self.barwidth,
               label=self.values[i][0], align="center")

また、X軸のラベルを表示する箇所では、set_major_formatterや set_major_locator を使って、指定されたフォーマットで日付が表示できるようにしています。

ax.xaxis.set_major_formatter(mdates.DateFormatter(format))
ax.xaxis.set_major_locator(mdates.DayLocator())

X軸にdatetime型を表示する場合、set_xlim を使ってX軸の表示範囲を設定しないとレイアウトが崩れる場合があります。

しかし、set_xlim を使うと棒グラフの開始と終了の一部が見切れてしまうので、実際の開始~終了より、0.5日間だけ広く取るように設定しています。

ax.set_xlim(x_datetime[0] - datetime.timedelta(days=0.5),x_datetime[-1] + datetime.timedelta(days=0.5))

最後に、X軸の角度を tick_params で設定しています。

ax.tick_params(axis='x', rotation=rotation)

もし日付ではなく時刻で表示する場合は、set_major_locatorの引数を DayLocator() からHourLocater() に、timedeltaの引数を days= から hours= に変更することで対応できると思います。

まとめ

今回は、以前紹介した「グループ化した棒グラフが簡単に描画できる関数とクラス」に対して、X軸にdatetime型の日付が指定できるようにしたものを紹介しました。

今回はX軸の単位が日付になっていますが、プログラムを少し変更すれば年や時分秒に対応することも可能です。

もし良ければお使いください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次