Python 時系列データのグラフ描画において、横軸の目盛り表記を固定で指定する

 本記事では、下図のような行方向に年月の時系列と数値があるデータセットにおいて、行数が揃っていない場合(欠損)がある場合でも、横軸の目盛りを指定してグラフを描画するサンプルコードを載せました。
 その方法は、欠損している年月データを作成する時にNaNで穴埋めします。その後、グラフを作図する際に、xticksにて横軸の目盛りに表示する「描画範囲」と「描画する文字列」を強制的に指定します。

下図はデータの前方が欠損している場合の例です。

 

使用するライブラリは次の通りです。

# ライブラリのインポート
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib
plt.rcParams["font.size"] = 15

ここで、作図するためのダミーデータを作ります。3人の個別の労働時間データをpandasデータフレームを作成します。個別にグラフ化したい場合に、ループ処理できるようにするため辞書形式にまとめます。

# Aさんの月別労働時間のpandasデータフレーム
df0 = pd.DataFrame({'年月': ['2023年11月', '2023年12月', '2024年1月', '2024年2月', '2024年3月','2024年4月', '2024年5月'],
                  '労働時間': [185, 180, 185, 190, 189, 187, 185]})
df0

# Bさんの月別労働時間のpandasデータフレーム
df1 = pd.DataFrame({'年月': ['2023年11月', '2023年12月', '2024年1月', '2024年2月', '2024年3月'],
                  '労働時間': [200, 195, 210, 205, 198]})
df1

# Cさんの月別労働時間のpandasデータフレーム
df2 = pd.DataFrame({'年月': ['2024年3月', '2024年4月', '2024年5月'],
                  '労働時間': [180, 160, 170]})
df2

# 上記3つのデータフレームを用いて、キーを氏名、バリューをデータフレームにした辞書を作成
data_dict = {
    'Aさん': df0,
    'Bさん':df1, 
    'Cさん':df2,
}
data_dict

下図は上記の処理により作成した辞書です。

 

 次に、横軸の範囲を指定するため、グラフの横軸に指定したい全ての年月を記載したpandasシリーズを作成します。これは、後ほど上記の各pandasデータフレームにマージします。そして、グラフのxticksに指定する時にも使用します。

# グラフで描画したい横軸の範囲をシリーズで作成する。後ほど、xticksにて指定するため。
all_dates = pd.Series([
    '2023年11月',
    '2023年12月',
    '2024年1月',
    '2024年2月',
    '2024年3月',
    '2024年4月',
    '2024年5月'])
all_dates

最後に、辞書をループして個人別にグラフを描画します。グラフは、縦軸が労働時間、横軸が指定した年月の範囲で固定します。

# 辞書をループで回して、個別にグラフを生成する
for name, df in data_dict.items():
    print(name)

    df_merge = all_dates.to_frame(name='年月').merge(df, on='年月', how='left')
    print(df_merge)

    plt.figure(figsize=(6, 4))
    plt.plot(df_merge['年月'], df_merge['労働時間'],
             color = 'blue',
             marker='o', 
             markerfacecolor='none',  # 中抜きにする
             markeredgecolor='blue',  # エッジの色を指定
             markeredgewidth=1,  # エッジの太さを指定            
             label=name)
    plt.title(f'{name}の労働時間', fontsize=14)
    plt.ylabel('労働時間 (時間)')
    # 横軸を all_dates で固定
    plt.xticks(ticks=range(len(all_dates)), labels=all_dates, rotation=45)  
    plt.xlabel('年月')
    plt.ylim(160, 220)

    # 各データ点の近くに数値を表示
    for x, y in zip(df_merge['年月'], df_merge['労働時間']):
        # xとyが有限値かつNaNでないことを確認
        if pd.notnull(y) and y != float('inf') and y != float('-inf'):
            plt.text(x, y, f'{y:.0f}', fontsize=12, ha='center', va='bottom') 
        
    plt.grid()
    plt.tight_layout()
    plt.show()

以上

<広告>