damyarou

python, GMT などのプログラム

matplotlib 河川流況表示(2)

記事の最後に行く

概要

河川の流況を示す作図プログラムを紹介します。 対象年数が多くなった場合の事例です。

流況図

縦軸に日平均流量、横軸に超過確率(厳密には超過率)をとった年間の流況を示す図です。 ここでは、1年毎に17年分の線を引き、赤い太線で17年間の平均を示しています。 図の下に、各年の平均・最小・最大値を画面出力したものをのせています。

f:id:damyarou:20190503090418j:plain

-------------------------
Year    ave    min    max
-------------------------
2001   36.4    3.6  276.2
2002   32.2    3.6  315.3
2003   46.0    3.9  458.3
2004   32.8    3.6  578.1
2005   43.9    3.7  570.3
2006   36.7    3.6  306.7
2007   40.9    3.7  365.6
2008   46.4    3.7  331.0
2009   39.6    3.6  473.9
2010   56.4    5.1  407.0
2011   37.5    3.8  316.6
2012   48.3    3.9  278.0
2013   41.4    3.9  239.0
2014   39.7    3.6  259.6
2015   28.9    3.6  176.8
2016   49.4    4.8  449.9
2017   47.4    5.3  288.2
-------------------------
Ave.   41.4    3.6  578.1

月平均流量ヒートマップ

各年の月平均流量をマトリックス状に配置したヒートマップです。

f:id:damyarou:20190503090434j:plain

月平均流量時系列図

月平均流量の時系列図を年毎にマトリックス状に示したものです。 この事例では17年分のデータなので17個の小さなグラフを4行5列のマトリックスの中に配置しています。

f:id:damyarou:20190503090454j:plain

流況図作図プログラム

元データはエクセルファイルであり、はじめから日付と日平均流量の2カラムで構成されているので、データ読み込みの関数:rdata() では、そのまま読みってSeriesに格納し、出力しています。

データ入力時の注意事項

プログラム全文で示すプログラムは、データをエクセルから読み込んでいる。 ここで、エクセルデータでは、第一カラムは時系列データとして指定しているため、pandas で読み込んでもその属性が継続されている。 しかし、以下のように、csv あるいは text ファイルから読み込む場合、年月日はただの文字列となっているため、Series にする際に、第一カラム(この場合 df['date'] )を、明確に時系列データであることを明示しておく必要がある。そうしないとあとで年別の値を抽出する場合などに困ることになる。

def rdata():
    df = pd.read_csv('inp_flow.csv', sep=',')
    q=df['m3/s']
    d= pd.to_datetime(df['date'])   # <= この操作を行うことに注意
    qq=pd.Series(np.array(q),index=d)  
    return qq 

プログラム全文

import numpy as np
import pandas as pd
import datetime
import matplotlib.pyplot as plt
from scipy import interpolate


# global
tstr='Duration Curve at Kelaena Dam Site (2001-2017 by HEC-HMS)'
ylist=['2001','2002','2003','2004','2005','2006','2007','2008','2009','2010',\
     '2011','2012','2013','2014','2015','2016','2017'] # year


def rdata():
    fnameR='xls_qq_kuroki.xlsx' # input file name
    sname='Sheet1'
    df=pd.read_excel(fnameR, sheet_name=sname) # read excel data
    q=df['m3/s']
    d=df['date']
    qq=pd.Series(np.array(q),index=d)
    return qq


def makedata(qq):
    qqq=np.zeros((101,len(ylist)+1),dtype=np.float64) # discharge
    ppp=np.arange(101,dtype=np.float64)               # propability
    sta=np.zeros((len(ylist)+1,3),dtype=np.float64)   # ave, min, max of a year
    for i,sy in enumerate(ylist):
        qd=np.array(qq[sy])
        qd=np.sort(qd)[::-1]
        ii=np.arange(len(qd))
        pp=ii/(len(qd)-1)*100
        ff=interpolate.interp1d(pp,qd)
        qqq[:,i]=ff(ppp)
        sta[i,0]=np.average(qd)
        sta[i,1]=np.min(qd)
        sta[i,2]=np.max(qd)
    sta[-1,0]=np.average(qq)
    sta[-1,1]=np.min(qq)
    sta[-1,2]=np.max(qq)
    for j in range(len(ppp)):
        qqq[j,-1]=np.average(qqq[j,:])
    return ppp,qqq,sta


def drawfig(ppp,qqq):
    fsz=16
    xmin=0; xmax=100; dx=10
    ymin=0; ymax=200; dy=20
    fig=plt.figure(figsize=(10,6),facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    plt.xlabel('Non-exceedance probability (%)')
    plt.ylabel('River flow Q (m$^3$/s)')
    plt.xticks(np.arange(xmin,xmax+dx,dx))
    plt.yticks(np.arange(ymin,ymax+dy,dy))
    plt.grid(color='#999999',linestyle='solid')
    plt.title(tstr,loc='left',fontsize=fsz)
    for i,sy in enumerate(ylist):
        plt.plot(ppp,qqq[:,i],'-',color='#777777',lw=1)
    plt.plot(ppp,qqq[:,-1],'-',color='#ff0000',lw=3,label='Average')
    plt.legend()
    fnameF='fig_duration2.jpg'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0.1)
    plt.show()

    
def main():
    qq=rdata()
    ppp,qqq,sta=makedata(qq)
    drawfig(ppp,qqq)
    print('-------------------------')
    print('{0:>4s} {1:>6s} {2:>6s} {3:>6s}'.format('Year','ave','min','max'))
    print('-------------------------')
    for i,sy in enumerate(ylist):
        print('{0:4s} {1:6.1f} {2:6.1f} {3:6.1f}'.format(sy,sta[i,0],sta[i,1],sta[i,2]))
    print('-------------------------')
    print('{0:4s} {1:6.1f} {2:6.1f} {3:6.1f}'.format('Ave.',sta[-1,0],sta[-1,1],sta[-1,2]))
    
    
#==============
# Execution
#==============
if __name__ == '__main__': main()

月平均流量ヒートマップおよび月平均流量時系列図作図プログラム

元データは流況図用のものと同じです。 エクセルより読み取ったデータを以下のような pandas の DataFrame に書き直し、この数値をそのままヒートマップおよび時系列図にプロットしています。

       Jan    Feb    Mar    Apr   May   Jun   Jul   Aug   Sep   Oct   Nov  \
Year                                                                        
2001  18.1   37.7   63.2   48.2  30.1  56.3  22.3  17.6  10.6  10.2  61.6   
2002  48.6   96.0   81.5   44.2  26.1  32.6  15.3  11.0   3.8   4.7  12.2   
2003  46.1  145.3   80.3   45.3  32.7  23.6  25.6  24.9  22.6  10.9  26.8   
2004  28.6   68.1   66.0   18.7  69.8  60.4  18.7   8.7   5.6   4.7  12.2   
2005  50.9   53.3   74.9   88.8  35.7  26.2  29.6  12.7  11.7  32.3  35.7   
2006  36.0   90.4   70.8   68.3  53.7  56.9  17.3   8.6   9.2   4.7  12.2   
2007  35.8   62.0   57.8   99.4  33.7  78.0  26.6  19.6  13.2   7.4  26.3   
2008  23.2   51.5   45.2   49.0  32.7  55.1  45.5  34.0  14.7  31.4  87.0   
2009  74.7   58.8  134.6   30.4  36.0  21.4  22.7  16.5   3.9   6.5  29.6   
2010  52.6   92.4   93.7   62.1  39.5  43.9  32.4  58.5  33.8  64.7  63.2   
2011  33.1   58.4   55.9   53.6  45.1  32.6  32.0   8.7  23.6  11.7  25.8   
2012  76.7   79.8   93.0   83.3  69.1  45.2  26.2  19.4  14.6  13.5  25.7   
2013  28.3   72.0   70.2   66.0  38.6  47.3  55.8  22.9  13.7   8.4  37.5   
2014  33.5   34.1   57.9   63.3  57.3  41.6  57.9  23.2   4.0   7.8  39.1   
2015  33.3   77.4   66.9   39.5  41.5  21.5  15.0   8.6   3.8   4.7  12.3   
2016  29.0   91.6   88.1  106.3  34.0  54.6  24.1  12.9  33.3  31.4  41.4   
2017  30.6   58.0   69.2   68.8  56.5  61.3  29.1  28.2  18.5  45.2  65.6   
Ave.  40.0   72.2   74.7   60.9  43.1  44.6  29.2  19.8  14.1  17.7  36.1   

       Dec  Ave.  
Year              
2001  62.3  36.4  
2002  15.6  32.2  
2003  75.4  46.0  
2004  32.9  32.8  
2005  75.0  43.9  
2006  17.3  36.7  
2007  35.0  40.9  
2008  88.8  46.4  
2009  39.9  39.6  
2010  42.8  56.4  
2011  70.5  37.5  
2012  34.1  48.3  
2013  38.6  41.4  
2014  55.5  39.7  
2015  26.1  28.9  
2016  49.7  49.4  
2017  39.4  47.4  
Ave.  47.0  41.4  

プログラム全文

import numpy as np
import pandas as pd
import datetime
import matplotlib.pyplot as plt
import seaborn as sns


# global
ylist=['2001','2002','2003','2004','2005','2006','2007','2008','2009','2010', \
       '2011','2012','2013','2014','2015','2016','2017'] # year


def rdata():
    fnameR='xls_qq_kuroki.xlsx' # input file name
    sname='Sheet1'
    df=pd.read_excel(fnameR, sheet_name=sname) # read excel data
    q=df['m3/s']
    d=df['date']
    qq=pd.Series(np.array(q),index=d)
    return qq


def makedf(qq):
    mlist=['-01','-02','-03','-04','-05','-06','-07','-08','-09','-10','-11','-12']
    n=len(ylist)
    m=len(mlist)
    qt=np.zeros((n+1,m+1),dtype=np.float64) # sum of discharges of a month
    ct=np.zeros((n+1,m+1),dtype=np.int)     # sum of days of a manth
    for i,sy in enumerate(ylist):
        for j,sm in enumerate(mlist):
            sym=sy+sm
            qd=np.array(qq[sym])
            qt[i,j]=np.sum(qd)
            ct[i,j]=len(qd)
    for i in range(n):
        qt[i,m]=np.sum(qt[i,0:m])
        ct[i,m]=np.sum(ct[i,0:m])
    for j in range(m):
        qt[n,j]=np.sum(qt[0:n,j])
        ct[n,j]=np.sum(ct[0:n,j])
    qt[n,m]=np.sum(qq)
    ct[n,m]=len(qq)    
    df = pd.DataFrame({'Year': ylist+['Ave.'],
                       'Jan' : qt[:,0]/ct[:,0],
                       'Feb' : qt[:,1]/ct[:,1],
                       'Mar' : qt[:,2]/ct[:,2],
                       'Apr' : qt[:,3]/ct[:,3],
                       'May' : qt[:,4]/ct[:,4],
                       'Jun' : qt[:,5]/ct[:,5],
                       'Jul' : qt[:,6]/ct[:,6],
                       'Aug' : qt[:,7]/ct[:,7],
                       'Sep' : qt[:,8]/ct[:,8],
                       'Oct' : qt[:,9]/ct[:,9],
                       'Nov' : qt[:,10]/ct[:,10],
                       'Dec' : qt[:,11]/ct[:,11],
                       'Ave.': qt[:,12]/ct[:,12]})
    df=df.set_index('Year')
    return df


def fighmap(df):
    wid=0.5*(len(ylist)+1)
    fsz=14
    plt.figure(figsize=(12,wid),facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
#    cm='YlGnBu'
    cm='Blues'
    sns.heatmap(df,annot=True,fmt='.1f',vmin=0,vmax=160,cmap=cm,cbar=False,linewidths=0.5)
    plt.xlabel('Month')
    plt.ylabel('Year')
    plt.yticks(rotation=0)
    plt.title('Monthly Average Discharge (m$^3$/s)',loc='left')
    fnameF='fig_hmap2.jpg'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0.1)
    plt.show()

    
def figts(df):
    # arrange of subplot
    # ----------------------------
    # i= 0  i= 1  i= 2  i= 3  i= 4
    # i= 5  i= 6  i= 7  i= 8  i= 9
    # i=10  i=11  i=12  i=13  i=14
    # i=15  i=16
    # ----------------------------
    icol=5
    irow=4
    fsz=11
    xmin=0;xmax=12
    ymin=0;ymax=200
    x=np.array([1,2,3,4,5,6,7,8,9,10,11,12])
    x=x-0.5
    mlist=['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
    emp=['']*12
    plt.figure(figsize=(12,12/icol*irow),facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.suptitle('Monthly Average Discharge in Each Year', x=0.5,y=0.91,fontsize=fsz+4)
    plt.subplots_adjust(wspace=0.07,hspace=0.07)
    for i,sy in enumerate(ylist):
        num=i+1
        plt.subplot(4,5,num)
        plt.xlim([xmin,xmax])
        plt.ylim([ymin,ymax])
        if i==0 or i==5 or i==10 or i==15:
            plt.ylabel('Discharge (m$^3$/s)')
        else:
            plt.yticks([])           
        if 12<=i:
            plt.xticks(x,mlist,rotation=90)
        else:
            plt.xticks(x,emp,rotation=90)
        y=df.loc[sy][0:12]
        qa=df.loc[sy][12]
        plt.plot(x,y,'o-',color='#000080',lw=1)
        plt.plot([xmin,xmax],[qa,qa],'--',color='#ff0000',lw=2)
        plt.text(8.5,qa,'Qave',fontsize=fsz,va='bottom',ha='center', color='#ff0000')
        s1='Year: '+sy
        s2='Qave: {0:.1f}'.format(qa)
        textstr=s1+'\n'+s2
        props = dict(boxstyle='square', facecolor='#ffffff', alpha=1)
        xs=xmin+0.95*(xmax-xmin); ys=ymin+0.95*(ymax-ymin)
        plt.text(xs,ys, textstr, fontsize=fsz+2,va='top',ha='right', bbox=props)
    fnameF='fig_series2.jpg'
    plt.savefig(fnameF, dpi=100, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def main():
    qq=rdata()
    df=makedf(qq)
    pd.options.display.precision = 1
    print(df)
    fighmap(df)
    figts(df)
    
    
#==============
# Execution
#==============
if __name__ == '__main__': main()

Thank you.

記事の先頭に行く