LoginSignup
3
4

More than 3 years have passed since last update.

Python 流出解析用タンクモデルのパラメータ検索に挑戦(日流量解析)

Last updated at Posted at 2019-08-18

(2019.08.21)1日ずらした相関図を追加。予測計算の部分は変更せず、1日ずらしの相関図作成部分を追加。まあまあの結果です。
(2019.08.24)月平均流量と年平均流量を表示するプログラムを追加。
(2019.09.03)月平均流量を表示するプログラムに月雨量表示を追加。

はじめに

「Python 流出解析用タンクモデルのプログラムを作ってみる」という記事で、タンクモデルのプログラムを作ってみたが、今回はモデルパラメータの自動検索を試みた。

「よく用いられている」とされている日流量解析用タンクモデルは、4段型で、1段(最上段)目に5個、2段めに3個、3段目に3個、4段目に1個もしくは2個、合計12あるいは13個のパラメータの入力が必要である。これは初期残留深さを除いた個数であり、初期残留深さを加えれば+4個のパラメータが存在する。このため、雨量データから流出量を計算する場合、最初に仮定したパラメータを入力し、実測流量を、より良く再現できるよう、パラメータの修正を行い、モデルを決定する。各タンクからの流出量は上部タンクからの流出の影響を受けるため、実測データと比較しながらパラメータを定めるのには労力がかかるが、その方法は、以下の文献などに紹介されている。

上記はタンクモデル開発者による論文であり、これを参考にしながらも、自己流でタンクモデルのモデルパラメータ検索に挑戦する。

方法論

できるだけ単純な方法を指向する観点から、ここで紹介する(思いついた)手法では以下の特色を持たせている。

  • 1) 各タンクに対しパラメータの補正係数は1つのみとする。すなわち4段タンクモデルには4個の補正係数を準備する
  • 2) タンクパラメータの補正に関して、タンクからの流量を増やしたい場合、側方流出係数は大きく、流出口位置は低く、下部への浸透流出係数は小さくする必要がある。これに対し各タンクに1つしか補正係数を準備していないので、乗算あるいは除算のみでパラメータの補正を行う。加算・減算はパラメータが負になる恐れがあるので用いない。
  • 3) 目標を流況曲線の再現におき、流況曲線を用いてパラメータ補正係数を算定する
  • 4) 流況曲線をタンクの数だけ分割しているが、分割比率は決められないので、乱数によりこれを仮定し、計算回数を増やすことにより、最適値を検索している。

準備するデータ

タンクモデルへの入力データとしての日雨量時系列と、モデルパラメータ同定のための実測日平均流量時系列を準備する。

モデル

モデルは4段モデルとし、パラメータの初期値は標準モデルより選定する。ここでは初期貯留量は既知の値とする。この事例では解析開始が1月1日であり、対象地域は雨季なので、流出口高さまで初期に貯留されているものと仮定する。

パラメータを収束させるための基本的な考え方

仮定したパラメータから流量予測を行い、ターゲットである実測流量との流量比からパラメータの補正量を算出し、これを前回パラメータに乗じることにより、補正されたパラメータを算出、これを用いて流量予測を行うというステップを繰り返す。

パラメータ補正の考え方

流況図の利用と区間分割

雨量から算定した日平均流量を実測流量に近似させたいことから、パラメータ補正には流況図を用いることとする。タンクは4段で構成されているが、第1段タンク(最上段)は流況図の高流量領域、第4段タンク(最下段)は流況図の低流量領域に、2段目・3段目は流況図の中間領域の流量に、より大きな影響を与えることが予想できる。このため流況図を、流量の非超過確率により4つの領域に分割し、それぞれの領域に、高流量から低流量域に向かい、第1段タンクから第4段タンクを割り当て、各領域の試算流量とターゲット流量(実測流量)との流量比により、各タンクのパラメータを修正する。なお、日流量解析では日最大流量再現には主眼を置かないため、ごく高流量域(非超過確率 $P_0$ 以下)は、補正係数の計算領域から除外している。なお、実際には各タンクが影響を及ぼす流量領域の非超過確率値を決めることはできない。このため、非超過確率の境界値($P_1$, $P_2$, $P_3$)は乱数により仮定してパラメータ推定・流量予測を繰り返し、ターゲットとの誤差が最小となるケースをパラメータの最適値とする。プログラム中では、指定した数値 [$P_0$+1 から 97 まで] から、random.choice でランダムに3個の整数を選び出し、これをソートして $P_1$, $P_2$, $P_3$ に当てはめており、毎回少しづつ異なる値が求まります。

fig_auto0.jpg

パラメータ補正

仮定したパラメータによる流況図の各領域における予測流量を $Q_I$、実測流量を $Q_R$ とし、補正係数rを $r = \frac{\sum Q_R}{\sum Q_I}$ と定義する。もし仮定したパラメータによる予測流量が、実測流量より大きければ、予測流量が小さくなるようパラメータを補正してやれば良い。このため、各パラメータに対し以下の補正を行うことにする。ここに、$P$ の添字 $n$ は非超過確率の境界を示し、各パラメータの添字 $i$ は繰り返し回数を示す。

\begin{align}
& r_i=\cfrac{\sum_{P_n}^{P_{n+1}} Q_R}{\sum_{P_n}^{P_{n+1}} Q_{I(i)}} & & \text{補正係数}\\
& a_{1(i+1)}=a_{1(i)}*r_i & & \text{下側流出口流出係数}\\
& h_{1(i+1)}=\cfrac{h_{1(i)}}{r_i} & & \text{下側流出口高さ}& \\
& a_{2(i+1)}=a_{2(i)}*r_i & & \text{上側流出口流出係数}\\
& h_{2(i+1)}=\cfrac{h_{2(i)}}{r_i} & & \text{上側流出口高さ}\\
& b_{(i+1)}=\cfrac{b_{(i)}}{r_i} & & \text{浸透(底部)流出口流出係数}
\end{align}
流出係数 a1 および a2 の補正について

例えば、予測流量 $Q_I$ がターゲット流量 $Q_R$ より大きい場合、補正係数 $r$ は1より小さくなる。この場合、そのタンクでの予測流量を小さくするようパラメータを補正したいので、各流出係数に補正係数 $r$ を乗じることにより、次回繰り返しステップでの予測流量を小さくするよう補正する。

流出口高さ h1 および h2、浸透流出口流出係数 b の補正について

上記と同様、予測流量を小さくするようパラメータを補正する場合、$h_1$、$h_2$、$b$ を大きくすれば次回繰り返しステップでの予測流量は小さくなることが期待される。このため、各係数を補正係数 $r$ で除してやることにより、次回繰り返しステップの予測流量を小さくするよう補正する。

出力事例

パラメータ初期値

Case-1:火山灰地帯(1)

    para[0,:]=np.array([0.100, 15, 0.100,  60, 0.200, 15]) # タンク1
    para[1,:]=np.array([0.030, 30, 0.000, 999, 0.060, 30]) # タンク2
    para[2,:]=np.array([0.006, 60, 0.000, 999, 0.012, 50]) # タンク3
    para[3,:]=np.array([0.001,  0, 0.000, 999, 0.001,  0]) # タンク4

Case-2:火山灰地帯(2)

    para[0,:]=np.array([0.050, 15, 0.050,  60, 0.200, 15]) # タンク1
    para[1,:]=np.array([0.020, 30, 0.000, 999, 0.080, 30]) # タンク2
    para[2,:]=np.array([0.004, 60, 0.000, 999, 0.016, 50]) # タンク3
    para[3,:]=np.array([0.001,  0, 0.000, 999, 0.001,  0]) # タンク4
パラメータ初期値による流量予測(Case-1) パラメータ初期値による流量予測(Case-2)
fig fig

修正されたパラメータでの流量予測結果は以下の通り。上図に示す初期値をいれたものからみれば、ターゲットに近づいていることがわかります。

最終的な流量予測結果(Case-1) 最終的な流量予測結果(Case-2)
fig fig
同一日での流量比較(Case-1) 同一日での流量比較(Case-2)
fig fig

上図のように、ターゲットと予測値を同一日で比較するとかなり相関が悪い。流況図がそれなりに整合しているのだから、そんなことはないだろうということで、1日ずらした相関(当日の雨量に対して計算された予測値を翌日の流量とする)をとってみると下図のようになる。まあまあです。このことは、教科書*)にも、「日雨量から日流量を算出するとき、常に1日の遅れを与える」として示されています。ただモデルの特性のせいか、Case-2では全体的に流量が低く出る傾向が目立っています。このへんは初期値の工夫で改善できそうです。

*) 菅原正巳,水文学講座7 流出解析法,1973年10月15日初版2刷 共立出版株式会社

1日遅れでの流量比較(Case-1) 1日遅れでの流量比較(Case-2)
fig fig

選定されたパラメータ

Case-1

* Comparison of Discharge
Case          Qave    Qmax    Qmin
Observed      41.4   578.1     3.6
Predicted     40.0   418.1     2.6
* Estimated parameter
Tank No           a1        h1        a2        h2         b        ri
1st tank      0.1145   13.1043    0.1145   52.4171    0.1747   15.0000
2nd tank      0.0148   60.7691    0.0000  999.0000    0.1215   30.0000
3rd tank      0.0027  135.6202    0.0000  999.0000    0.0271   50.0000
4th tank      0.0005    0.0000    0.0000  999.0000    0.0020    0.0000

Case-2

* Comparison of Discharge
Case          Qave    Qmax    Qmin
Observed      41.4   578.1     3.6
Predicted     38.9   325.2     1.5
* Estimated parameter
Tank No           a1        h1        a2        h2         b        ri
1st tank      0.0740   10.1329    0.0740   40.5316    0.1351   15.0000
2nd tank      0.0115   52.2659    0.0000  999.0000    0.1394   30.0000
3rd tank      0.0018  136.4676    0.0000  999.0000    0.0364   50.0000
4th tank      0.0005    0.0000    0.0000  999.0000    0.0021    0.0000

Case-1 での月平均流量と年平均流量の確認

降雨日の1日遅れ予測流量が、ターゲット流量とまずまずの相関があることが確認できたし、流況図もまずまず再現できていることがわかったが、念の為、月平均流量と年平均流量が、ターゲットを再現できているか確認してみた。

年平均流量が、前述の事例と違うが、このグラフを作成するのに何回かプログラムを走らせているので、乱数により選定している各タンクに分担させる流量の非超過確率の違いによるもの。

月平均流量の比較

fig_thm_tank.jpg

年平均流量の比較

fig_thy_tank.jpg

タンクモデルプログラム

# パラメータ自動推定
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as tick
from scipy import interpolate
import numpy.random as random


def fig_duration(ppp, qqq, qq0, fnameF):
    fsz=14
    xmin=0; xmax=100; dx=10
    ymin=0; ymax=200; dy=50
    plt.figure(figsize=(8,5),facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    tstr='Duration Curve'
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    plt.xlabel('Non-exceedance Probability (%)')
    plt.ylabel('Average Daily Discharge (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)
    plt.plot(ppp,qqq,'-', label='Predicted (Qm={0:.1f})'.format(np.mean(qqq)))
    plt.plot(ppp,qq0,'-', label='Observed (Qm={0:.1f})'.format(np.mean(qq0)))
    plt.legend(loc='upper right')
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def fig_correlation(qo, qe, fnameF):
    fsz=10
    xmin=0; xmax=600; dx=100
    ymin=0; ymax=600; dy=100
    plt.figure(figsize=(6,6),facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    tstr='Q-Q plot'
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    plt.xlabel('Observed (m$^3$/s)')
    plt.ylabel('Predicted (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.gca().set_aspect('equal',adjustable='box')
    plt.title(tstr,loc='left',fontsize=fsz)
    plt.plot(qo,qe,'o')
    plt.plot([xmin,xmax],[ymin,ymax],'-',lw=1)
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def inp_rf():
    # input rainfall data
    fnameR='rf_karaena.csv'
    df = pd.read_csv(fnameR, header=0, index_col=0)
    df.index = pd.to_datetime(df.index, format='%Y-%m-%d')
    df=df[df.index >= '2001-01-01'] # data after 2001-01-01 is available
    return df


def inp_tank():
    # ca  : catchment area (km2)
    # eva : evaporation (mm/day)
    # nt  : number of tanks
    # para = [a1, h1, a2, h2, b, r_ini]
    # a1    : discharge coefficient of the first outlet
    # h1    : height of the first outlet
    # a2    : discharge coefficient of the second outlet
    # h2    : height of the second outlet
    # b     : discharge coefficient of the bottom outlet    
    # r_ini : initial value of the storage depth (residual depth)
    ca=822.6
    eva=-10
    nt=4
    para=np.zeros((nt, 6))
    para[0,:]=np.array([0.100, 15, 0.100,  60, 0.200, 15])
    para[1,:]=np.array([0.030, 30, 0.000, 999, 0.060, 30])
    para[2,:]=np.array([0.006, 60, 0.000, 999, 0.012, 50])
    para[3,:]=np.array([0.001,  0, 0.000, 999, 0.001,  0])

    #para[0,:]=np.array([0.050, 15, 0.050,  60, 0.200, 15])
    #para[1,:]=np.array([0.020, 30, 0.000, 999, 0.080, 30])
    #para[2,:]=np.array([0.004, 60, 0.000, 999, 0.016, 50])
    #para[3,:]=np.array([0.001,  0, 0.000, 999, 0.001,  0])
    return nt, para, ca, eva


def cal_tank(rfj, r_i, a1, h1, a2, h2, b):
    # rfj : rainfall
    # r_i : residual depth
    # x_i : storage depth
    # y_i : runoff depth
    # z_i : infiltratio
    x_j = r_i + rfj
    if 0 < x_j:
        if x_j <= h1             : y_j = 0
        if h1 < x_j and x_j <= h2: y_j = a1 * (x_j - h1)
        if h2 < x_j              : y_j = a1 * (x_j - h1) + a2 * (x_j - h2)
        z_j = b * x_j
        r_j = x_j - y_j - z_j
    else:
        x_j = 0
        y_j = 0
        z_j=x_j
        r_j=0
    return x_j, y_j, z_j, r_j


def calc(nt, para, ca, eva, rf):
    xx=np.zeros(nt, dtype=np.float64)
    yy=np.zeros((nt, len(rf)), dtype=np.float64)
    zz=np.zeros((nt, len(rf)), dtype=np.float64)
    for ii in range(0, nt): # iteration for tank number
        #print('* Tank-'+str(ii+1))
        a1 =para[ii, 0] # run-off coefficient of lower outlet
        h1 =para[ii, 1] # height of upper outlet
        a2 =para[ii, 2] # run-off coefficient of upper outlet
        h2 =para[ii, 3] # height of upper outlet
        b  =para[ii, 4] # run-off coefficient of bottom outlet
        r_i=para[ii, 5] # initial storage (residual storage)
        if ii==0:
            rfl=rf # input rainfall to 1st tank
            jj=np.where(rfl<=0) # if no-rainfall, 
            rfl[jj]=eva         # evaporation is inputted 
        else:
            rfl=zz[ii-1,:] # input discharge from upper tank botom outlet for 2nd tank or lower
        #print('{0:>8s}{1:>8s}{2:>8s}{3:>8s}{4:>8s}'.format('rfj', 'r_i', 'x_j', 'y_j', 'z_j'))
        for j, rfj in enumerate(rfl):
            x_j, y_j, z_j, r_j = cal_tank(rfj, r_i, a1, h1, a2, h2, b)
            #print('{0:8.2f}{1:8.2f}{2:8.2f}{3:8.2f}{4:8.2f}'.format(rfj, r_i, x_j, y_j, z_j))
            r_i=r_j    
            yy[ii, j]=y_j
            zz[ii, j]=z_j
            xx[ii]=x_j
    qq=np.zeros(len(rf), dtype=np.float64)
    for j in range(0,len(rf)):
        qq[j] = np.sum(yy[:, j])/1000*ca*1e6/24/3600 # (m3/s)
    return qq, xx


def probdata(ppp, qq):
    qp=np.sort(qq)[::-1]
    ii=np.arange(len(qp))
    pp=ii/(len(qp)-1)*100
    ff=interpolate.interp1d(pp,qp)
    qqq=ff(ppp)
    return qqq    


def main():
    fig1='fig_auto1a.jpg'
    fig2='fig_auto2a.jpg'
    fig3='fig_auto3a.jpg'
    fig4='fig_auto4a.jpg'

    df0 = inp_rf() # input data
    q0 = df0['kuroki'].values # target discharge
    rf = df0['Dam'].values # rainfall
    ppp=np.arange(0,len(df0)+1,dtype=np.float64)/len(df0)*100 # propability
    qq0 = probdata(ppp, q0) # duration curve of taret discharge

    nt, para, ca, eva = inp_tank() # input model parameter
    qq, xx = calc(nt, para, ca, eva, rf) # run-off analysis
    qqq = probdata(ppp, qq) # duration curve of predicted discharge
    fig_duration(ppp, qqq, qq0, fig1) # draw duration curve

    jmax=50 # maximum number of iteration
    p0=5 # minimum probability considered
    num=np.arange(p0+1, 98, 1) # range of boundary on duration curve
    err0=np.sqrt(np.sum((qqq-qq0)**2)) # calculation of initial error factor
    param=para # initial value of parameter memoried
    coef=0.98 # correction factor for ratio
    kkk=0
    for jjj in range(jmax):
        for i in range(100): # difinition of boundary on duration curve
            arr=random.choice(num, 3, replace=False) # choice the 3 values of boundary
            par=np.sort(arr)
            p1=par[0] # lower limit of influence area of 1st tank
            p2=par[1] # lower limit of influence area of 2nd tank
            p3=par[2] # lower limit of influence area of 3rd tank
            if 2<p2-p1 and 2<p3-p2: break

        k0=np.where((p0 <= ppp) & (ppp < 100)) # index of whole range considered
        k1=np.where((p0 <= ppp) & (ppp < p1))  # index of 1st tank influence area
        k2=np.where((p1 <= ppp) & (ppp < p2))  # index of 2nd tank influence area
        k3=np.where((p2 <= ppp) & (ppp < p3))  # index of 3rd tank influence area
        k4=np.where((p3 <= ppp) & (ppp < 100)) # index of 4th tank influence area

        # re-difinition of initial tank parameter and reference discharge
        nt, para, ca, eva = inp_tank()
        qq, xx = calc(nt, para, ca, eva, rf)
        qqq = probdata(ppp, qq)

        ratio = np.zeros(nt, dtype=np.float64) # array declaration of correction factors 
        imax=10 # maximum number of iteration
        er0=err0 # initial value of error factor
        eps=0.05 # allowable error
        for iii in range(imax):
            ratio[0]=np.sum(qq0[k1])/np.sum(qqq[k1]) # correction factor for 1st tank
            ratio[1]=np.sum(qq0[k2])/np.sum(qqq[k2]) # correction factor for 2nd tank
            ratio[2]=np.sum(qq0[k3])/np.sum(qqq[k3]) # correction factor for 3rd tank
            ratio[3]=np.sum(qq0[k4])/np.sum(qqq[k4]) # correction factor for 4th tank
            ratio=ratio*coef # correction factor for total shape
            # parameter correction
            # 1st tank
            para[0,0]=para[0,0]*ratio[0]
            para[0,1]=para[0,1]/ratio[0]
            para[0,2]=para[0,2]*ratio[0]
            para[0,3]=para[0,3]/ratio[0]
            para[0,4]=para[0,4]/ratio[0]
            # 2nd tank
            para[1,0]=para[1,0]*ratio[1]
            para[1,1]=para[1,1]/ratio[1]
            para[1,4]=para[1,4]/ratio[1]
            # 3rd tank
            para[2,0]=para[2,0]*ratio[2]
            para[2,1]=para[2,1]/ratio[2]
            para[2,4]=para[2,4]/ratio[2]
            # 4th tank
            para[3,0]=para[3,0]*ratio[3]
            para[3,4]=para[3,4]/ratio[3]
            # calculation of reference discharge using corrected parameters
            qq, xx = calc(nt, para, ca, eva, rf)
            qqq = probdata(ppp, qq)
            er1=np.sqrt(np.sum((qqq[k0]-qq0[k0])**2)) # calculation of error factor
            if np.abs((er0-er1)/er0) < eps:
                break
            else:
                er0=er1
        err1=er1
        print('{0:4d}{1:10.1f}{2:10.1f}{3:10.1f}{4:4d}{5:4d}'
              .format(jjj, err1, err0, np.mean(qqq), iii, kkk)) # print of reference information
        if err1 < err0: 
            param=para
            err0=err1
            kkk=jjj

    print('{0:4d}{1:10.3f}'.format(kkk, err0))
    qq, xx = calc(nt, param, ca, eva, rf)
    print('Last xx = ', xx)
    qqq = probdata(ppp, qq)
    fig_duration(ppp, qqq, qq0, fig2)

    i0=np.where(rf<0)
    rf[i0]=0
    df1 = pd.DataFrame({'date' : df0.index.values,
                         'Rf'  : rf,
                         'Qo'  : q0,
                         'Qp'  : qq})
    df1 = df1.set_index('date')
    df1.to_csv('df1.csv')
    print(df1)

    print('* Comparison of Discharge')
    print('{0:10s}{1:>8s}{2:>8s}{3:>8s}'.format('Case' ,'Qave','Qmax','Qmin'))
    print('{0:10s}{1:8.1f}{2:8.1f}{3:8.1f}'.format('Observed' ,np.mean(qq0),np.max(qq0),np.min(qq0)))
    print('{0:10s}{1:8.1f}{2:8.1f}{3:8.1f}'.format('Predicted',np.mean(qqq),np.max(qqq),np.min(qqq)))
    print('* Estimated parameter')
    print('{0:10s}{1:>10s}{2:>10s}{3:>10s}{4:>10s}{5:>10s}{6:>10s}'
            .format('Tank No','a1','h1','a2','h2','b','ri'))
    ss=['1st tank','2nd tank','3rd tank','4th tank']
    for i in range(nt):
        print('{0:10s}{1:10.4f}{2:10.4f}{3:10.4f}{4:10.4f}{5:10.4f}{6:10.4f}'
             .format(ss[i],param[i,0],param[i,1],param[i,2],param[i,3],param[i,4],param[i,5]))

    # Correlation between same days
    qo=q0
    qe=qq
    fig_correlation(qo, qe, fig3)
    # Correlation between a day and previous day
    n=len(q0)
    qo=q0[1:n]
    qe=qq[0:n-1]
    fig_correlation(qo, qe, fig4)


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

月平均流量と年平均流量描画プログラム

雨量と流量の年変化を描画するプログラムで、凡例を装飾する以下の2つが効いてないみたいです。なんででしょう?

    plt.rcParams['legend.facecolor']='#ffffff' 
    plt.rcParams['legend.framealpha']=1

プログラム

# Outline of flow data
import numpy as np
import pandas as pd
import datetime
import matplotlib.pyplot as plt
import seaborn as sns


# global
_ylist=np.arange(2001,2018,1)
ylist=[]
for y in _ylist:
    ylist=ylist+[str(y)]


def rdata():
    df = pd.read_csv('df1.csv', header=0, index_col=0)
    df.index = pd.to_datetime(df.index, format='%Y-%m-%d')
    #df=df[df.index >= '2001-01-01']
    rf=df['Rf']
    q0=df['Qo']
    q1=df['Qp']
    d= df.index
    rfl=pd.Series(np.array(rf),index=d)
    qq0=pd.Series(np.array(q0),index=d)  
    qq1=pd.Series(np.array(q1),index=d)  
    return rfl, qq0, qq1 


def makedf(qq):
    # discharge data
    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])
            ii=np.where(0<qd)
            qt[i,j]=np.sum(qd[ii])
            ct[i,j]=len(qd[ii])
    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(qt[0:n,0:m])
    ct[n,m]=np.sum(ct[0:n,0:m])    
    dfq = 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]})
    dfq=dfq.set_index('Year')
    return dfq


def makedfr(qq):
    # rainfall data
    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
    for i,sy in enumerate(ylist): # year
        for j,sm in enumerate(mlist): # month 
            sym=sy+sm
            qd=np.array(qq[sym])
            ii=np.where(0<=qd)
            qt[i,j]=np.sum(qd[ii])
    for i in range(n):
        qt[i,m]=np.sum(qt[i,0:m])
    for j in range(m):
        qt[n,j]=np.sum(qt[0:n,j])/len(ylist)
    qt[n,m]=np.sum(qt[0:n,0:m])/len(ylist)
    dfr = pd.DataFrame({'Year': ylist+['Ave'],
                       'Jan' : qt[:,0],
                       'Feb' : qt[:,1],
                       'Mar' : qt[:,2],
                       'Apr' : qt[:,3],
                       'May' : qt[:,4],
                       'Jun' : qt[:,5],
                       'Jul' : qt[:,6],
                       'Aug' : qt[:,7],
                       'Sep' : qt[:,8],
                       'Oct' : qt[:,9],
                       'Nov' : qt[:,10],
                       'Dec' : qt[:,11],
                       'Sum' : qt[:,12]})
    dfr=dfr.set_index('Year')
    return dfr


def figts(dfq0, dfq1, dfr):
    # 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
    xx=np.array([1,2,3,4,5,6,7,8,9,10,11,12])
    xx=xx-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):
        # plot data
        y0 = dfq0.loc[sy][0:12] # observed discharge
        y1 = dfq1.loc[sy][0:12] # predicted discharge
        yy = dfr.loc[sy][0:12]  # rainfall

        num=i+1
        plt.subplot(irow,icol,num)
        xmin=0;xmax=12
        ymin=0;ymax=200
        plt.xlim([xmin,xmax])
        plt.ylim([ymin,ymax])
        if i==0 or i==5 or i==10 or i==15:
            plt.ylabel('Discharge Q (m$^3$/s)')
        else:
            plt.yticks([])           
        if i==13 or i==14 or i==15 or i==16:
            plt.xticks(xx,mlist,rotation=90)
        else:
            plt.xticks(xx,emp,rotation=90)
        plt.plot(xx,y0,'o-',color='#ffa500',lw=1,label='Qo')
        plt.plot(xx,y1,'o-',color='#000080',lw=1,label='Qp')
        props = dict(boxstyle='square', facecolor='#ffffff', alpha=1)
        xs=xmin+0.05*(xmax-xmin); ys=ymin+0.95*(ymax-ymin)
        plt.text(xs,ys, sy, fontsize=fsz,va='top',ha='left', bbox=props)
        plt.legend()

        plt.twinx()
        ymin=0; ymax=800; dy=200
        plt.ylim([ymin,ymax])
        if i==4 or i==9 or i==14 or i==16:
            plt.ylabel('Rainfall (mm/month)')
        else:
            plt.yticks([])           
        plt.bar(xx,yy,color="#00ffff", edgecolor="#000000", alpha=0.3, linewidth=1)

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


def figyy(dfq0, dfq1, dfr):
    # plot data
    xx=np.zeros(len(ylist), dtype=np.float64)
    y0=np.zeros(len(ylist), dtype=np.float64)
    y1=np.zeros(len(ylist), dtype=np.float64)
    yy=np.zeros(len(ylist), dtype=np.float64)
    for i, sy in enumerate(ylist):
        xx[i] = float(sy)         # year
        y0[i] = dfq0.loc[sy][12]  # ovserved annual discharge
        y1[i] = dfq1.loc[sy][12]  # predicted annual discharge
        yy[i] = dfr.loc[sy][12]   # annual rainfall
    ave0 = dfq0.loc['Ave'][12]    # average ovserved discharge
    ave1 = dfq1.loc['Ave'][12]    # average predicted discharge
    aver = dfr.loc['Ave'][12]     # average rainfall

    fsz=14
    xmin=float(ylist[0])-1
    xmax=float(ylist[len(ylist)-1])+1
    plt.figure(figsize=(10,5), facecolor='w')
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.rcParams['legend.frameon']=False
    plt.rcParams['legend.facecolor']='#ffffff' 
    plt.rcParams['legend.framealpha']=1

    ymin=0; ymax=100
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    plt.ylabel('Annual averave discharge Q (m$^3$/s)')
    locs = np.arange(xmin,xmax+1,1)
    xtick=['']+ylist+['']
    plt.xticks(locs, xtick, color='#000000', fontsize=fsz, rotation=70)
    plt.plot(xx,y0,'o-',color='#ffa500',label='Q_observed (Qm={0:.1f}m$^3$/s)'.format(ave0))
    plt.plot(xx,y1,'o-',color='#000080',label='Q_predicted (Qm={0:.1f}m$^3$/s)'.format(ave1))
    plt.grid(axis='y')
    plt.legend(loc='lower right').get_frame().set_alpha(1.0)

    plt.twinx()
    ymin=0; ymax=5000; dy=1000
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    plt.ylabel('Annual Rainfall (mm/year)')
    plt.bar(xx,yy,color="#00ffff", edgecolor="#000000", alpha=0.3, linewidth=1)
    tstr='Rm = {0:.0f} mm/year'.format(aver)
    plt.legend(loc='upper right', title=tstr)

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


def main():
    rfl, qq0, qq1 = rdata()
    dfq0 = makedf(qq0)
    dfq1 = makedf(qq1)
    dfr = makedfr(rfl)
    pd.options.display.precision = 1
    pd.set_option("display.width", 200)
    print('* Observed')
    print(dfq0)
    print()
    print('* Predicted')
    print(dfq1)
    print()
    print('* Rainfall')
    print(dfr)
    figts(dfq0, dfq1, dfr)
    figyy(dfq0, dfq1, dfr)


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

以 上

3
4
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
3
4