46
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Python matplotlib 時系列グラフ(時間軸の設定)

Last updated at Posted at 2019-04-15

はじめに

年末よりずっと忙しかったのですが、久しぶりに余裕ができて記事を書いています。
何年か前にやった仕事のフォローのため二週間の出張でマレーシアに来ています。お客さんに提出する報告書で久しぶりに時系列グラフを作成する必要があったので、その時調べたものをアップしたいと思います。(実はお客さん提出用グラフの時間軸はデフォルトで出してしまったのですが、提出後今後のために調べたものをアップしているのが本当のところです)

当方の環境は以下の通り。

  • MacBook Pro (Retina, 13-inch, Mid 2014)
  • macOS MOjave
  • Python 3.7.2

時間軸設定には以下のサイトを参考にしました。

作例は以下の通り。ただの時系列グラフですが、よくサイトで目にするものよりはスパンが長く、月単位に目盛りを入れているところがミソです。

fig_qiita.jpg

モジュールのインポート

下記のモジュールをインポート。この他、エクセルからデータを読み込むので、xlrdpip でインストールしておく必要があります。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime
import matplotlib.dates as mdates

データ読み込み

データは2つのエクセルファイルから読み込みます。

ファイル calvert.xlsx

1つめのデータは下に示すもので、作例グラフの緑線を描くためのもの。
エクセルで以下のような形で収納されています。これは自分でデータを打ち込んだので、打ち込みやすいよう、日付(dd)、月(mm)、年(yy)、値(Q) という並びにしています。
欠測期間が長く、グラフの線を連続させたくない場合は、欠測期間中のある一日の日付とデータとして nan をいれることにより、グラフの線を結ばないで描画してくれます。

Screenshot 2019-04-15 09.15.57.png

ファイル v-notch.xlsx

2つめのデータは下に示すもので、作例グラフの濃い青線を描くためのもの。
これは既存のエクセルファイルがあったのでそれを用いていますが、ここで使用するのは、カラム A (Date)とカラム N (RWL) だけです。

Screenshot 2019-04-15 09.14.35.png

エクセルファイルからのデータ読み込み

エクセルからのデータ読み込みは pandas で行っています。
データ読み込み部のコードは以下の通り。

    # discharge data input
    fnameR='calvert.xlsx'
    df = pd.read_excel(fnameR,sheet_name='data')
    ss=[]
    for sd,sm,sy in zip(df['dd'],df['mm'],df['yy']):
        s1='{0:0>2d}'.format(sd)
        s2='{0:0>2d}'.format(sm)
        s3='{0:0>4d}'.format(sy)
        ss=ss+[s1+'/'+s2+'/'+s3]
    df.index=pd.to_datetime(ss, format='%d/%m/%Y')
    # reservoir water level input
    fnameR='v-notch.xlsx'
    dfr = pd.read_excel(fnameR,usecols=[0,13],index_col=0)
    dfr.index = pd.to_datetime(dfr.index, format='%d/%m/%Y')

calvert.xlsx の読み込みでは、日付・月・年の個別の文字列を連結した文字列リスト [ss] を作り、これを日付データとしてデータフレームのインデックスとします。

v-notch.xlsx の読み込みでは、usecols=[0,13] により必要なカラム(0と13)のみを読み込み、index_col=0 として、カラム0をデータフォレームのインデックスとします。またインデックスを日付データにします。

calvert.xlsx からのデータをデータフレーム df, v-notch.xlsx からのデータをデータフレーム dfr に格納しています。

横軸最小値と最大値の指定

横軸最小値を2015年3月1日、横軸最大値を2019年3月31日に指定します。このとき下のように日付を示す文字列を作成し、日付型に変換し、これを用いてx軸範囲を指定しています。
日付の指定は、%Y-%m-%d という形にしています。Python は日付はデフォルトでこの形で扱っているようですので。

    sxmin='2015-03-01'
    sxmax='2019-03-31'
    xmin = datetime.datetime.strptime(sxmin, '%Y-%m-%d')
    xmax = datetime.datetime.strptime(sxmax, '%Y-%m-%d')
    plt.xlim([xmin,xmax])

横軸の書式設定

横軸の書式設定は、以下のようにxaxis.set_major_formatterxaxis.set_minor_locatorを用います。
マレーシアでは通常は、日・月・年の順で表示するのでDateFormatter%d-%b-%Yを指定します。月の%bは短縮形英語表現です。月と日付の順番が紛らわしいので、これではっきりします。

主目盛の位置はxaxis.set_major_locatorMonthLocator(interval=6)で、6ヶ月単位に指定します。また補助目盛の位置はxaxis.set_minor_locatorMonthLocator(interval=1)で1ヶ月単位に指定します。autofmt_xdate()は長い表記を適当に斜めに表示してくれます。

グリッドは主目盛・補助目盛双方に指定するとうっとおしいので主目盛のみ(major)としました。

    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%b-%Y'))
    plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=6))
    plt.gca().xaxis.set_minor_locator(mdates.MonthLocator(interval=1))
    plt.gcf().autofmt_xdate()

この事例では、twinx()を用いた2軸利用のグラフとしていますが、上記記載は最後にしておく必要があります。twinx()の前に書いてもこの記載は有効にならずデフォルトの軸が表示されてしまいます。

プログラム全文

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import datetime
import matplotlib.dates as mdates



def main():
    # discharge data input
    fnameR='calvert.xlsx'
    df = pd.read_excel(fnameR,sheet_name='data')
    ss=[]
    for sd,sm,sy in zip(df['dd'],df['mm'],df['yy']):
        s1='{0:0>2d}'.format(sd)
        s2='{0:0>2d}'.format(sm)
        s3='{0:0>4d}'.format(sy)
        ss=ss+[s1+'/'+s2+'/'+s3]
    df.index=pd.to_datetime(ss, format='%d/%m/%Y')
    # reservoir water level input
    fnameR='v-notch.xlsx'
    dfr = pd.read_excel(fnameR,usecols=[0,13],index_col=0)
    dfr.index = pd.to_datetime(dfr.index, format='%d/%m/%Y')
    

    fsz=12
    plt.figure(figsize=(10,6), dpi=200,facecolor='w')
    plt.rcParams['font.size']=fsz
    sxmin='2015-03-01'
    sxmax='2019-03-31'
    xmin = datetime.datetime.strptime(sxmin, '%Y-%m-%d')
    xmax = datetime.datetime.strptime(sxmax, '%Y-%m-%d')
    plt.xlim([xmin,xmax])
    plt.ylim([0,500])
    plt.xlabel('Date')
    plt.ylabel('Discharge (Liter/min)')
    plt.grid(which='major',axis='both',color='#999999',linestyle='--')

    plt.plot(df.index,df['Q'],'-',color='#ff0000',label='Discharge')
    qs=165.0; spot='2016-05-31'
    dsp = datetime.datetime.strptime(spot, '%Y-%m-%d')
    plt.plot([dsp],[qs],'o',color='#ff0000')
    ss='31/05/2016\n{0:.0f}L/min'.format(qs)
    plt.text(dsp,qs-10,ss,va='top',ha='center',fontsize=fsz-2)
    plt.plot([0],[0],'-',lw=2,color='#0000ff',label='RWL')
    plt.title('Discharge of Calvert at River Outlet Bay',loc='left',fontsize=fsz)
    plt.legend(loc='lower right',fontsize=fsz)
    plt.twinx()
    plt.ylim([65,90])
    plt.ylabel('Reservoir Water Level (EL.m)')
    plt.plot(dfr.index,dfr['RWL'],'-',lw=2,color='#0000ff')

    plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%d-%b-%Y'))
    plt.gca().xaxis.set_major_locator(mdates.MonthLocator(interval=6))
    plt.gca().xaxis.set_minor_locator(mdates.MonthLocator(interval=1))
    plt.gcf().autofmt_xdate()

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


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

雑感

普段からエクセルは好んでは使わないのですが、データ入力などには向いている。テキストエディタでブランクをいれながら入力するよりは、エクセルのような表計算形式の入力のほうが楽です。またエクセル入力したファイルもpandasで楽々読み込みできるので、とても便利。

軸の単位を示すのに、xaxis.set_major_locatorには今回用いたMonthLocatorの他、WeekdayLocator, DayLocator, HourLocator, MinuteLocator, SecondLocatorがあり、色々な単位に対応できるようです。(前出:https://matplotlib.org/api/dates_api.html 参照)

これで時系列グラフの時間軸書式をある程度好きなように表現できるようになった気がします。

この投稿が、何かしら皆様のお役にもたてば幸いです。

以 上

46
50
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
46
50

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?