LoginSignup
15
13

More than 5 years have passed since last update.

Python:統計量の母数推定(非線形回帰の応用)

Last updated at Posted at 2018-10-23

2018.10.25 プログラムを母数推定部(計算のみ)と作図部の2部に書き換え

はじめに

2018年10月10日に海外での業務完了につき帰国した.現在は休暇中であり,のんびりしているが,全く何もやらないのもちょっと気が引けたので,仕事の練習として,Pythonを使った非線形回帰のプログラムを作ってみた.昔は同じことをFortranプログラムでやっていたが,scipyの統計関数や,非線形回帰機能を使うことにより,非常にシンプルにやりたいことを実現できることを確認した.

やっていることは,観測値を,仮定した確率分布関数に回帰し,母数(確率分布関数における係数)を推定するというものである.母数推定の方法は,これまでに積率法(モーメント法)などが研究されてきたが,ここでは Python:scipy の optimize.leastsq を用いる方法をとっている.

例として用いる観測値は,筆者の自宅に近い前橋地方気象台の各年雨量データ(前橋の気象データ)とした.

例題の中で扱った確率分布は,以下のものである.年最大日雨量データと下記の確率分布の関係を取扱った.

3変数対数正規分布     Log-Normal distribution with 3 parameters   LN3         (ln3)
2変数対数正規分布     Log-Normal distribution with 2 parameters   LN2         (ln2)
一般化極値分布        Generalized Extreme Value distribution      GEV         (gev)
グンベル分布          Gumbel distribution                         Gumbel      (gum) 
一般化パレート分布     Generalized Pareto distribution             GPD         (gpd)
指数分布             Exponential distribution                    Exponential (exp)
対数ピアソンIII型分布  Log-Pearson type III distribution           LP3         (lp3)
3変数ワイブル分布     Weibull distribution with 3 parameters      Weibull     (wei) 

なお,年最大日雨量データは,正規分布との適合性は良好ではないことが一般に知られている.このため,正規分布との適合性が比較的良い,各年の年間降雨量と正規分布との関係も扱ってみた.

当方の計算環境は以下の通り.

  • MacBook Pro (Retina, 13-inch, Mid 2014)
  • macOS Mojave
  • Python 3.7.0

参考文献

データの取得

データとして,国土交通省気象庁のホームページ(前橋の気象データ)から,前述の前橋地方気象台における1987年から2017年までの観測データを取得した.ここでは難しいことはせず,ホームページよりテキストをクリップボードにコピーし,テキストエディタと簡単なプログラムにより整形して用いている.
作成したデータファイルの書式は以下の通り.

inp_maebashi.txt
year    rfy    rfd   tave   tmax   tmin
1897 1384.6   67.5   12.5   17.7    8.3
1898 1099.1  107.6   13.3   18.5    8.9
1899 1097.0   66.3   13.0   18.5    8.7
....    ...    ...    ...    ...    ...

year : 観測年
rfy  : 年間降雨量
rfd  : 年最大日雨量
tave : 年平均気温
tmax : 年最高気温
tmin : 年最低気温

まずは,前段として,降雨量と気温の経時変化をみてみよう.降雨量については特に近年多雨化するような傾向は見られないが,気温については1950年頃から明らかに上昇傾向にある.地球温暖化の影響か? この件については時間があったらということで,この投稿では,日最大降雨量(変数名:rfd)および年降雨量(変数名:rfy)について,確率分布に回帰する問題を考える.

fig_base.jpg

プログラム中では,降雨量・気温を含めたファイルからデータを読み込んでいるが,興味ある読者が遊べるよう,分析に用いているデータを,1次元numpy配列として以下に示しておく.配列は1987年から2017年の値を順番に並べたものであり,ソートはかけていない.

# 前橋の各年の最大日降雨量(mm/day)1987〜2017年
import numpy as np

xx=np.array([67.5, 107.6,  66.3, 115.2, 123.7,  96.5,  93.2, 135.8,  68.6,  73.4,  71.5,  72.3,
             52.4,  95.5, 164.3,  65.5, 110.1, 140.1, 104.2,  71.2,  59.9, 113.9,  67.8, 113.3,
             94.2,  91.7,  92.3,  61.0,  98.0,  63.4,  99.7,  74.0, 119.8,  75.4,  76.5,  71.5,
             50.8,  74.3,  95.7,  90.6, 141.7,  69.3,  84.7, 130.7, 163.1,  52.1, 112.6, 148.0,
            128.5,  46.9, 357.4, 115.1,  75.6, 142.1,  67.6,  43.2,  69.6, 146.5, 262.4,  78.1,
             50.0, 180.0, 122.8,  48.2, 106.2, 147.2,  48.7,  92.4,  70.4, 145.7,  61.0,  75.0,
             89.0,  68.0, 100.5,  75.5,  73.0,  82.5,  94.5,  55.0,  98.0,  59.0,  72.0,  44.5,
            111.5, 175.0, 115.5,  57.0,  71.0, 102.5,  78.5,  63.5,  80.0,  95.0,  89.0,  90.5,
             85.0, 107.0,  56.5,  78.5, 157.5, 134.0, 201.0,  82.5, 103.5, 139.5,  53.5, 104.0,
            100.5,  76.5,  82.0,  66.5,  64.5,  64.0, 178.5,  83.0,  84.5, 104.0,  70.5, 105.5,
             91.0])
print(xx)
# 前橋の年降雨量(mm/year)1987〜2017年
import numpy as np

xx=np.array([1384.6, 1099.1, 1097.0,  927.9, 1235.6, 1574.0, 1213.6, 1308.1, 1304.4, 1272.4,
             1292.0, 1231.5, 1284.4, 1733.0, 1396.4, 1426.5, 1212.3, 1230.0, 1534.1, 1556.5,
             1327.5, 1078.8, 1321.8, 1538.0, 1526.4, 1161.2, 1459.7, 1008.8, 1339.6,  968.3,
             1233.5, 1389.2, 1298.2, 1184.8, 1138.8, 1352.5,  911.3,  968.9, 1242.7, 1376.0,
             1169.5, 1425.9, 1128.4, 1133.1, 1492.3,  868.2, 1168.5,  949.2, 1356.4, 1084.2,
             1363.3, 1545.8, 1172.4, 1497.0, 1139.1, 1164.1, 1274.6, 1275.3, 1776.6, 1310.5,
             1234.4, 1465.1, 1529.8,  879.5, 1310.2,  950.4,  799.0, 1291.5, 1148.2, 1245.5,
             1067.2, 1391.0, 1050.5,  984.5, 1030.0, 1051.5,  917.5, 1122.0, 1047.0,  986.0,
             1186.0,  847.5, 1100.0, 1060.0, 1283.0, 1275.0, 1163.0,  816.5, 1140.5, 1240.5,
             1191.5, 1356.5, 1657.5, 1201.0, 1438.5, 1174.0, 1346.5,  970.5, 1075.5,  815.5,
             1078.5, 1649.5, 1495.0, 1163.0, 1316.0, 1503.0, 1104.5, 1196.0, 1114.0, 1479.0,
             1310.5, 1425.0,  986.0, 1490.5, 1340.0, 1074.0,  998.5, 1395.5, 1232.0, 1249.0,
             1192.5])
print(xx)

プロッティング・ポジション公式

統計量を確率分布に回帰する場合に必要となるプロッティング・ポジション(非超過確率に相当)を定める公式を示す.プロッティング・ポジション公式を用いるため,標本値 $x$ は,昇順に並べ替えておくことに注意する.

\begin{equation}
F[x_{(i)}]=\frac{i-\alpha}{N+1-2\alpha}
\end{equation}
\begin{align}
&N          &  & \text{標本数} \\
&i          &  & \text{標本値を昇順に並べたときの小さいほうからの順位} \\
&x_{(i)}    &  & \text{$i$番目の順位標本値} \\
&F[x_{(i)}] &  & \text{プロッティング・ポジション(非超過確率相当)} \\
&\alpha     &  & \text{$0 \sim 1$の定数}
\end{align}

定数$\alpha$の値としては以下のものが知られている.本稿ではCunnane式($=0.4$)を用いる.

\begin{align}
&\text{Weibull}    & & \alpha=0 \\
&\text{Blom}       & & \alpha=0.375 \\
&\text{Cunnane}    & & \alpha=0.40 \\
&\text{Gringorten} & & \alpha=0.44 \\
&\text{Hazen}      & & \alpha=0.50 \\
\end{align}

確率分布の数式表現

統計量$x$が各種確率分布に従う場合の,確率密度関数,確率分布函数,クオンタイルの数式表現を以下に示す.

3変数対数正規分布(Log-Normal distribution with 3 parameters: LN3)

確率密度関数

\begin{equation}
f(x)=\frac{1}{(x-a)\sigma_y\sqrt{2\pi}}\cdot\exp\left\{-\frac{1}{2}\left[\frac{\ln(x-a)-\mu_y}{\sigma_y}\right]^2\right\}
\end{equation}

確率分布関数

\begin{equation}
F(x)=\Phi\left(\frac{\ln(x-a)-\mu_y}{\sigma_y}\right)
\qquad \Phi(z)=\frac{1}{\sqrt{2\pi}}\int_{-\infty}^z\exp\left(-\frac{1}{2}t^2\right)dt
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
z=\frac{\ln(x-a)-\mu_y}{\sigma_y} \qquad\rightarrow\qquad x=a+\exp(\mu_y+\sigma_y z)
\end{equation}
\begin{equation}
x_p=a+\exp(\mu_y+\sigma_y z_p) \qquad \text{$z_p$は$p=\Phi(z)$となる$z$の値}
\end{equation}

2変数対数正規分布(Log-Normal distribution with 2 parameters: LN2)

3変数対数正規分布において,$a=0$ としたもの.

確率密度関数

\begin{equation}
f(x)=\frac{1}{x\sigma_y\sqrt{2\pi}}\cdot\exp\left\{-\frac{1}{2}\left[\frac{\ln(x)-\mu_y}{\sigma_y}\right]^2\right\}
\end{equation}

確率分布関数

\begin{equation}
F(x)=\Phi\left(\frac{\ln(x)-\mu_y}{\sigma_y}\right)
\qquad \Phi(z)=\frac{1}{\sqrt{2\pi}}\int_{-\infty}^z\exp\left(-\frac{1}{2}t^2\right)dt
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
z=\frac{\ln(x)-\mu_y}{\sigma_y} \qquad\rightarrow\qquad x=\exp(\mu_y+\sigma_y z)
\end{equation}
\begin{equation}
x_p=\exp(\mu_y+\sigma_y z_p) \qquad \text{$z_p$は$p=\Phi(z)$となる$z$の値}
\end{equation}

一般化極値分布(Generalized Extreme Value distribution: GEV)

確率密度関数

\begin{equation}
f(x)=\frac{1}{a}\left(1-k\frac{x-c}{a}\right)^{1/k-1}\cdot\exp\left[-\left(1-k\frac{x-c}{a}\right)^{1/k}\right]
\qquad (k\neq 0)
\end{equation}

$c$は位置母数,$a$は尺度母数,$k$は形状母数である.

確率分布関数

\begin{equation}
F(x)=\exp\left[-\left(1-k\frac{x-c}{a}\right)^{1/k}\right]
\qquad (k\neq 0)
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
p=\exp\left[-\left(1-k\frac{x-c}{a}\right)^{1/k}\right] \qquad\rightarrow\qquad x=c+\frac{a}{k}\cdot\left\{1-[-\ln(p)]^k\right\}
\end{equation}
\begin{equation}
x_p=c+\frac{a}{k}\cdot\left\{1-[-\ln(p)]^k\right\}
\end{equation}

グンベル分布(Gumbel distribution)

グンベル分布は,一般化極値分布において,$k=0$の場合と一致する.

確率密度関数

\begin{equation}
f(x)=\frac{1}{a}\exp\left[-\frac{x-c}{a}-\exp\left(-\frac{x-c}{a}\right)\right] \qquad -\infty<x<\infty
\end{equation}

$c$は位置母数,$a$は尺度母数である.

確率分布関数

\begin{equation}
F(x)=\exp\left[-\exp\left(-\frac{x-c}{a}\right)\right]
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
p=\exp\left[-\exp\left(-\frac{x-c}{a}\right)\right] \qquad\rightarrow\qquad x=c-a\ln[-\ln(p)]
\end{equation}
\begin{equation}
x_p=c-a\ln[-\ln(p)]
\end{equation}

一般化パレート分布(Generalized Pareto distribution: GPD)

確率密度関数

\begin{equation}
f(x)=\frac{1}{a}\left(1-k\frac{x-c}{a}\right)^{1/k-1} \qquad (k\neq 0)
\end{equation}

$c$は位置母数,$a$は尺度母数,$k$は形状母数である.

確率分布関数

\begin{equation}
F(x)=1-\left(1-k\frac{x-c}{a}\right)^{1/k}
\qquad (k\neq 0)
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
p=1-\left(1-k\frac{x-c}{a}\right)^{1/k}\qquad\rightarrow\qquad x=c+\frac{a}{k}\left\{1-(1-p)^k\right\}
\end{equation}
\begin{equation}
x_p=c+\frac{a}{k}\cdot\left\{1-(1-p)^k\right\}
\end{equation}

指数分布(Exponential distribution)

指数分布は,一般化パレート分布において,$k=0$の場合と一致する.

確率密度関数

\begin{equation}
f(x)=\frac{1}{a}\exp\left(-\frac{x-c}{a}\right)
\end{equation}

$c$は位置母数,$a$は尺度母数である.

確率分布関数

\begin{equation}
F(x)=1-\exp\left(-\frac{x-c}{a}\right)
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
p=1-\exp\left(-\frac{x-c}{a}\right)\qquad\rightarrow\qquad x=c-a\ln(1-p)
\end{equation}
\begin{equation}
x_p=c-a\ln(1-p)
\end{equation}

対数ピアソンIII型分布(Log-Pearson type III distribution: LP3)

確率密度関数

\begin{equation}
f(x)=\frac{1}{|a|\cdot\Gamma(b)\cdot x}\left(\frac{\ln x-c}{a}\right)^{b-1}\cdot\exp\left(-\frac{\ln x-c}{a}\right)
\qquad a>0 : \exp(c)<x<\infty
\end{equation}

$c$は位置母数,$a$は尺度母数,$b$は形状母数である.

確率分布関数

\begin{equation}
F(x)=G\left(\frac{\ln x-c}{a}\right)
\qquad G(w)=\frac{1}{\Gamma(b)}\int_0^w t^{b-1}\exp(-t)dt \qquad (a>0)
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
w=\frac{\ln x-c}{a} \qquad\rightarrow\qquad x=\exp(c+a w)
\end{equation}
\begin{equation}
x_p=\exp(c+a w_p) \qquad \text{$w_p$は$p=G(w)$となる$w$の値}
\end{equation}

3変数ワイブル分布(Weibull distribution with 3 parameters)

確率密度関数

\begin{equation}
f(x)=\frac{k}{a}\left(\frac{x-c}{a}\right)^{k-1}\exp{\left[-\left(\frac{x-c}{a}\right)^k\right]}
\qquad (k\neq 0)
\end{equation}

$c$は位置母数,$a$は尺度母数,$k$は形状母数である.

確率分布関数

\begin{equation}
F(x)=1-\exp\left[-\left(\frac{x-c}{a}\right)^k\right]
\qquad (k\neq 0)
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
1-p=\exp\left[-\left(\frac{x-c}{a}\right)^k\right] \qquad\rightarrow\qquad x=c+a[-\ln{(1-p)}]^{1/k}
\end{equation}
\begin{equation}
x_p=c+a[-\ln{(1-p)}]^{1/k}
\end{equation}

各種確率分布と最大日雨量の関係

以下にプログラム事例を示す.プログラムは以下の2部となっている.

  • 母数推定(計算のみ)
  • 作図(Q-Qプロット,確率分布曲線,確率密度曲線)

結果グラフ

Q-Qプロット

fig_maebashi_qq.jpg

確率分布曲線

fig_maebashi_cdf.jpg

確率密度曲線

fig_maebashi_hist.jpg

母数推定プログラム

母数推定は,前述「非超過確率$p$に対する水文統計量$x_p$」に示した式を用い,左辺$x_p$に標本値(観測値)$x$を,右辺$p$に標本値$x$に対応する非超過確率を入れ,左辺と右辺の差が最小となるよう,右辺の未定係数(母数)を optimize.leastsq で求めるよう行なっている.

母数推定計算部の簡単な説明(対数ピアソンIII型:LP3 の例)

主要な変数・関数の簡単な説明を,対数ピアソンIIIを例に,以下に記す.

  • xx : 標本値(各年の日最大雨量観測値)
  • pp : プロッティング・ポジション公式で求めた非超過確率
  • parameter0 : 未定係数の初期値(3変数回帰なので3個)
  • func_lp3 : 最小化する関数.この場合ガンマ分布のパラメータ bb も未知数であることに注意.
  • parameter : 求めるべき未定係数
  • r : Q-Qプロットにおける相関係数
  • 計算結果は,リスト res にて戻す.

対数ピアソンIII型分布の母数推定に関するコードは以下の通り.optimize.leastsq の利用により,自作で回帰するのと比べ,コードは非常にシンプルである.

def lp3(xx,pp):
    def func_lp3(parameter,xx,pp):
        bb=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        ww=gamma.ppf(pp, bb, loc=0, scale=1)
        est=np.exp(cc+aa*ww)
        residual=xx-est
        return residual
    # parameter calculation
    npara=3; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_lp3,parameter0,args=(xx,pp))
    bb=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    ww=gamma.ppf(pp, bb, loc=0, scale=1)
    yy=np.exp(cc+aa*ww)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['lp3(b,c,a,r)',bb,cc,aa,rr]
    return res

ワイブル分布(関数 wei)での初期値

ワイブル分布におけるleastsqに与える初期値は,後述する別解法(Step-1)に示す方法のうち,$c=0$ とおいたときの,$k$ と $a$ の値を与えている.

母数推定プログラム全文

py_calc.py
#======================
# Parameter Calculation
#======================
import sys
import numpy as np
from scipy.stats import norm  # normal distribution
from scipy.stats import gamma # Gamma distribution
from scipy import optimize    # for use of leastsq


def wdata(fnameW,res,f):
    print('{0:>15s} {1:15.7e} {2:15.7e} {3:15.7e} {4:15.7e}'.format(res[0],res[1],res[2],res[3],res[4]),file=f)
    print('{0:>15s} {1:10.3f} {2:10.3f} {3:10.3f} {4:10.3f}'.format(res[0],res[1],res[2],res[3],res[4]))


def ln3(xx,pp):
    def func_ln3(parameter,xx,zz):
        aa=parameter[0]
        mu=parameter[1]
        sd=parameter[2]
        est=aa+np.exp(mu+sd*zz)
        residual=xx-est
        return residual
    # parameter calculation
    zz=norm.ppf(pp, loc=0, scale=1)
    npara=3; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_ln3,parameter0,args=(xx,zz))
    aa=result[0][0]
    mu=result[0][1]
    sd=result[0][2]
    yy=aa+np.exp(mu+sd*zz)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['ln3(a,m,s,r)',aa,mu,sd,rr]
    return res


def gev(xx,pp):
    def func_gev(parameter,xx,pp):
        kk=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        est=cc+aa/kk*(1-(-np.log(pp))**kk)
        residual=xx-est
        return residual
    # parameter calculation
    npara=3; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_gev,parameter0,args=(xx,pp))
    kk=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    yy=cc+aa/kk*(1-(-np.log(pp))**kk)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['gev(k,c,a,r)',kk,cc,aa,rr]
    return res


def gpd(xx,pp):
    def func_gpd(parameter,xx,pp):
        kk=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        est=cc+aa/kk*(1-(1-pp)**kk)
        residual=xx-est
        return residual
    # parameter calculation
    npara=3; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_gpd,parameter0,args=(xx,pp))
    kk=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    yy=cc+aa/kk*(1-(1-pp)**kk)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['gpd(k,c,a,r)',kk,cc,aa,rr]
    return res


def lp3(xx,pp):
    def func_lp3(parameter,xx,pp):
        bb=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        ww=gamma.ppf(pp, bb, loc=0, scale=1)
        est=np.exp(cc+aa*ww)
        residual=xx-est
        return residual
    # parameter calculation
    npara=3; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_lp3,parameter0,args=(xx,pp))
    bb=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    ww=gamma.ppf(pp, bb, loc=0, scale=1)
    yy=np.exp(cc+aa*ww)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['lp3(b,c,a,r)',bb,cc,aa,rr]
    return res


def ln2(xx,pp):
    def func_ln2(parameter,xx,zz):
        mu=parameter[0]
        sd=parameter[1]
        est=np.exp(mu+sd*zz)
        residual=xx-est
        return residual
    # parameter calculation
    zz=norm.ppf(pp, loc=0, scale=1)
    npara=2; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_ln2,parameter0,args=(xx,zz))
    mu=result[0][0]
    sd=result[0][1]
    yy=np.exp(mu+sd*zz)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['ln2(-,m,s,r)',0,mu,sd,rr]
    return res


def gum(xx,pp):
    def func_gum(parameter,xx,pp):
        cc=parameter[0]
        aa=parameter[1]
        est=cc-aa*np.log(-np.log(pp))
        residual=xx-est
        return residual
    # parameter calculation
    npara=2; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_gum,parameter0,args=(xx,pp))
    cc=result[0][0]
    aa=result[0][1]
    yy=cc-aa*np.log(-np.log(pp))
    rr=np.corrcoef(xx, yy)[0][1]
    res=['gum(-,c,a,r)',0,cc,aa,rr]
    return res


def exp(xx,pp):
    def func_exp(parameter,xx,pp):
        cc=parameter[0]
        aa=parameter[1]
        est=cc-aa*np.log(1-pp)
        residual=xx-est
        return residual
    # parameter calculation
    npara=2; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_exp,parameter0,args=(xx,pp))
    cc=result[0][0]
    aa=result[0][1]
    yy=cc-aa*np.log(1-pp)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['exp(-,c,a,r)',0,cc,aa,rr]
    return res


def wei(xx,pp):
    def func_wei(parameter,xx,pp):
        kk=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        est=cc+aa*(-np.log(1-pp))**(1/kk)
        residual=xx-est
        return residual
    # parameter calculation
    n=len(xx)
    cc=0.0
    y=np.log(-np.log(1-pp))
    x=np.log(xx-cc)
    c1=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/(n*np.sum(x**2)-(np.sum(x))**2)
    c2=(np.sum(x**2)*np.sum(y)-np.sum(x)*np.sum(x*y))/(n*np.sum(x**2)-(np.sum(x))**2)
    kk=c1
    aa=np.exp(-c2/c1)
    parameter0 = np.array([kk,cc,aa])
    result = optimize.leastsq(func_wei,parameter0,args=(xx,pp))
    kk=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    yy=cc+aa*(-np.log(1-pp))**(1/kk)
    rr=np.corrcoef(xx, yy)[0][1]
    res=['wei(k,c,a,r)',kk,cc,aa,rr]
    return res


def main():
    args = sys.argv
    fnameL=args[1] # Location name
    fnameR='inp_'+fnameL+'.txt' # input data file
    fnameW='res_'+fnameL+'.txt' # output data file
    # coefficient for plotting position formula
    #alpha=0.0 # Weibull
    alpha=0.4 # Cunnane
    #alpha=0.5 # Hazen
    # data input
    data=np.loadtxt(fnameR, skiprows=1)
    xx =data[:,2]
    xx=np.sort(xx)
    # setting protting position
    n=len(xx)
    ii=np.arange(n)+1
    pp=(ii-alpha)/(n+1-2*alpha)
    #parameter calculation
    print('{0:>15s} {1:>10s} {2:>10s} {3:>10s} {4:>10s}'.format('Dist.','k or b','c or m','a or s','r'))
    f=open(fnameW,'w')
    res=ln3(xx,pp); wdata(fnameW,res,f)
    res=gev(xx,pp); wdata(fnameW,res,f)
    res=gpd(xx,pp); wdata(fnameW,res,f)
    res=lp3(xx,pp); wdata(fnameW,res,f)
    res=ln2(xx,pp); wdata(fnameW,res,f)
    res=gum(xx,pp); wdata(fnameW,res,f)
    res=exp(xx,pp); wdata(fnameW,res,f)
    res=wei(xx,pp); wdata(fnameW,res,f)
    f.close()


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

出力ファイル

res_maebashi.txt
   ln3(a,m,s,r)   4.6206741e+01   3.6279534e+00   7.7413782e-01   9.8997190e-01
   gev(k,c,a,r)  -2.6407481e-01   7.6352934e+01   2.2461378e+01   9.9360710e-01
   gpd(k,c,a,r)  -1.7110038e-01   5.9531466e+01   3.1083527e+01   9.8645487e-01
   lp3(b,c,a,r)   3.7085592e+00   3.7885439e+00   1.9107401e-01   9.9187839e-01
   ln2(-,m,s,r)   0.0000000e+00   4.4621660e+00   4.4827030e-01   9.7247983e-01
   gum(-,c,a,r)   0.0000000e+00   7.7602703e+01   3.3029941e+01   9.6098207e-01
   exp(-,c,a,r)   0.0000000e+00   5.3290528e+01   4.3488789e+01   9.7840226e-01
   wei(k,c,a,r)   8.4712424e-01   5.9621867e+01   3.4147990e+01   9.8109111e-01

作図プログラム

  • 基データファイルから観測値を読み込む
  • 母数推定プログラムで計算した結果を書き込んだファイルから母数を推定結果を読み込む
  • 観測値・母数推定結果からプロット用データを作成しファイル出力
  • ファイルよりデータを読み込み作図.
  • Q-Qプロット描画:観測値(observed)と推定結果(predicted)の関係をプロット.
  • 確率分布関数描画:観測値ー非超過確率関係を描画し,その上にQ-Qプロット作図プログラムで出力した確率分布曲線を重ね書きする.
  • 確率分布描画:観測値のヒストグラムを描画し,その上にQ-Qプロット作図プログラムで出力した確率密度曲線を重ね書きする.ヒストグラム作成時は,正規化しておく.

母数推定プログラムの出力ファイル(_res.txt)の読み込みは,pands により以下のように行う.空白区切テキストなので sep='\s+' とし,Header はないので header=None とする.

    fnameR='res_maebashi.txt'
    df=pd.read_csv(fnameR, sep='\s+', header=None)
    print(df)

pandas で読み込んだデータ(print(df)の結果)は以下の通り.自動で行名と列名が追加される.

              0          1          2          3         4
0  ln3(a,m,s,r)  46.206741   3.627953   0.774138  0.989972
1  gev(k,c,a,r)  -0.264075  76.352934  22.461378  0.993607
2  gpd(k,c,a,r)  -0.171100  59.531466  31.083527  0.986455
3  lp3(b,c,a,r)   3.708559   3.788544   0.191074  0.991878
4  ln2(-,m,s,r)   0.000000   4.462166   0.448270  0.972480
5  gum(-,c,a,r)   0.000000  77.602703  33.029941  0.960982
6  exp(-,c,a,r)   0.000000  53.290528  43.488789  0.978402
7  wei(k,c,a,r)   0.847124  59.621867  34.147990  0.981091

作図プログラム全文

py_plot.py
#======================
# Plot
#======================
import sys
import numpy as np
from scipy.stats import norm  # normal distribution
from scipy.stats import gamma # Gamma distribution
import scipy.special as sp    # for use of Gamma function
import pandas as pd
import matplotlib.pyplot as plt


# Declaration of global variables
xmin=0; xmax=600; dx=100
fsz=12 # font size


def drawqq(xx,yy,tstr,ss,j):
    # Q-Q plot
    ymin=xmin; ymax=xmax; dy=dx
    nplot=240+j
    plt.subplot(nplot)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Observed (mm/day)')
    plt.ylabel('Predicted (mm/day)')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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.scatter(xx,yy,s=20,c='#000080',marker='o')
    plt.plot([xmin,xmax],[ymin,ymax],'-',color='#999999',lw=1)
    xs=xmin+(xmax-xmin)*0.03
    ys=ymin+(ymax-ymin)*0.97
    plt.text(xs,ys,ss,ha='left',va='top',fontname='monospace')


def drawcum(xx,pp,yy,ff,tstr,j):
    # CDF plot
    ymin=0; ymax=1.0; dy=0.25
    nplot=240+j
    plt.subplot(nplot)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/day)')
    plt.ylabel('Non-exceedance Probability')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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.scatter(xx,pp,s=10,c='#000080',marker='o',linewidth=0.5,clip_on=False,label='Observed')
    plt.plot(yy,ff,'-',color='#ff0000',lw=1,label='Predicted')
    plt.legend(loc='lower right')


def drawhist(xx,xpdf,ypdf,tstr,j):
    # PDF plot + Histogram
    ymin=0; ymax=0.02; dy=0.005
    nplot=240+j
    plt.subplot(nplot)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/day)')
    plt.ylabel('Normalized frequency')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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)
    edges = range(xmin,xmax,10)
    plt.hist(xx,color='#00ccff',bins=edges,density=True,label='Observed')
    plt.plot(xpdf,ypdf,'-',color='#ff0000',lw=2,label='Predicted')
    plt.legend(loc='upper right')


def wdata(fnameW,vx,vy):
    f=open(fnameW,'w')
    for i in range(len(vx)):
        print('{0:15.7e} {1:15.7e}'.format(vx[i],vy[i]),file=f)
    f.close()


def data_ln3(xx,pp,aa,mu,sd,rr):
    # plot data
    zz=norm.ppf(pp, loc=0, scale=1)
    yy=aa+np.exp(mu+sd*zz)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (LN3)'
    ss='a={0:7.3f}\n'.format(aa)
    ss=ss+'m={0:7.3f}\n'.format(mu)
    ss=ss+'s={0:7.3f}\n'.format(sd)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    zz=norm.ppf(ff, loc=0, scale=1)
    yc=aa+np.exp(mu+sd*zz)
    xpdf=np.arange(aa+1,xmax,1)
    ypdf=1/(xpdf-aa)/sd/np.sqrt(2*np.pi)*np.exp(-1/2*((np.log(xpdf-aa)-mu)/sd)**2)
    fnameW='_q_ln3.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_ln3.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_ln3.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_gev(xx,pp,kk,cc,aa,rr):
    # plot data
    yy=cc+aa/kk*(1-(-np.log(pp))**kk)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (GEV)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa/kk*(1-(-np.log(ff))**kk)
    xpdf=np.arange(xmin+1,xmax,1)
    coef=1-kk*(xpdf-cc)/aa
    ypdf=1/aa*coef**(1/kk-1)*np.exp(-coef**(1/kk))
    fnameW='_q_gev.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_gev.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_gev.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_gpd(xx,pp,kk,cc,aa,rr):
    # plot data
    yy=cc+aa/kk*(1-(1-pp)**kk)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (GPD)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa/kk*(1-(1-ff)**kk)
    xpdf=np.arange(xmin+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=1/aa*(1-kk*coef)**(1/kk-1)
    fnameW='_q_gpd.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_gpd.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_gpd.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_lp3(xx,pp,bb,cc,aa,rr):
    # plot data
    ww=gamma.ppf(pp, bb, loc=0, scale=1)
    yy=np.exp(cc+aa*ww)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (LP3)'
    ss='b={0:7.3f}\n'.format(bb)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    ww=gamma.ppf(ff, bb, loc=0, scale=1)
    yc=np.exp(cc+aa*ww)
    xpdf=np.arange(np.exp(cc),xmax,1)
    coef=(np.log(xpdf)-cc)/aa
    ypdf=1/np.abs(aa)/sp.gamma(bb)/xpdf*coef**(bb-1)*np.exp(-coef)
    fnameW='_q_lp3.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_lp3.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_lp3.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_ln2(xx,pp,mu,sd,rr):
    # plot data
    zz=norm.ppf(pp, loc=0, scale=1)
    yy=np.exp(mu+sd*zz)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (LN2)'
    ss='m={0:7.3f}\n'.format(mu)
    ss=ss+'s={0:7.3f}\n'.format(sd)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    zz=norm.ppf(ff, loc=0, scale=1)
    yc=np.exp(mu+sd*zz)
    xpdf=np.arange(mu+1,xmax,1)
    ypdf=1/xpdf/sd/np.sqrt(2*np.pi)*np.exp(-1/2*((np.log(xpdf)-mu)/sd)**2)
    fnameW='_q_ln2.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_ln2.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_ln2.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_gum(xx,pp,cc,aa,rr):
    # plot data
    yy=cc-aa*np.log(-np.log(pp))
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (Gumbel)'
    ss='c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    yc=cc-aa*np.log(-np.log(ff))
    xpdf=np.arange(xmin+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=1/aa*np.exp(-coef-np.exp(-coef))
    fnameW='_q_gum.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_gum.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_gum.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_exp(xx,pp,cc,aa,rr):
    # plot data
    yy=cc-aa*np.log(1-pp)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (Exponential)'
    ss='c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    yc=cc-aa*np.log(1-ff)
    xpdf=np.arange(xmin+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=1/aa*np.exp(-coef)
    fnameW='_q_exp.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_exp.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_exp.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def data_wei(xx,pp,kk,cc,aa,rr):
    # plot data
    yy=cc+aa*(-np.log(1-pp))**(1/kk)
    rr=np.corrcoef(xx, yy)[0][1]
    tstr='Q-Q plot (Weibull)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr)
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa*(-np.log(1-ff))**(1/kk)
    xpdf=np.arange(cc+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=kk/aa*coef**(kk-1)*np.exp(-coef**kk)
    fnameW='_q_wei.txt'; wdata(fnameW,xx,yy)
    fnameW='_c_wei.txt'; wdata(fnameW,yc,ff)
    fnameW='_d_wei.txt'; wdata(fnameW,xpdf,ypdf)
    return ss


def main():
    args = sys.argv
    fnameL=args[1] # location
    # coefficient for plotting position formula
    #alpha=0.0 # Weibull
    alpha=0.4 # Cunnane
    #alpha=0.5 # Hazen
    # data input
    fnameR='inp_'+fnameL+'.txt'
    data=np.loadtxt(fnameR, skiprows=1)
    xx =data[:,2]
    xx=np.sort(xx)
    # setting protting position
    n=len(xx)
    ii=np.arange(n)+1
    pp=(ii-alpha)/(n+1-2*alpha)
    # parameter input
    fnameR='res_'+fnameL+'.txt'
    df=pd.read_csv(fnameR, sep='\s+', header=None)
    print(df)
    # maing plot data
    ss=[]

    aa=df.iloc[0,1]
    mu=df.iloc[0,2]
    sd=df.iloc[0,3]
    rr=df.iloc[0,4]
    s=data_ln3(xx,pp,aa,mu,sd,rr)
    ss=ss+[s]

    kk=df.iloc[1,1]
    cc=df.iloc[1,2]
    aa=df.iloc[1,3]
    rr=df.iloc[1,4]
    s=data_gev(xx,pp,kk,cc,aa,rr)
    ss=ss+[s]

    kk=df.iloc[2,1]
    cc=df.iloc[2,2]
    aa=df.iloc[2,3]
    rr=df.iloc[2,4]
    s=data_gpd(xx,pp,kk,cc,aa,rr)
    ss=ss+[s]

    bb=df.iloc[3,1]
    cc=df.iloc[3,2]
    aa=df.iloc[3,3]
    rr=df.iloc[3,4]
    s=data_lp3(xx,pp,bb,cc,aa,rr)
    ss=ss+[s]

    dm=df.iloc[4,1] # dummy
    mu=df.iloc[4,2]
    sd=df.iloc[4,3]
    rr=df.iloc[4,4]
    s=data_ln2(xx,pp,mu,sd,rr)
    ss=ss+[s]

    dm=df.iloc[5,1] # dummy
    cc=df.iloc[5,2]
    aa=df.iloc[5,3]
    rr=df.iloc[5,4]
    s=data_gum(xx,pp,cc,aa,rr)
    ss=ss+[s]

    dm=df.iloc[6,1] # dummy
    cc=df.iloc[6,2]
    aa=df.iloc[6,3]
    rr=df.iloc[6,4]
    s=data_exp(xx,pp,cc,aa,rr)
    ss=ss+[s]

    kk=df.iloc[7,1]
    cc=df.iloc[7,2]
    aa=df.iloc[7,3]
    rr=df.iloc[7,4]
    s=data_wei(xx,pp,kk,cc,aa,rr)
    ss=ss+[s]

    slist=['LN3','GEV','GPD','LN3','LN2','Gumbel','Exponential','Weibull']
    flist=['ln3','gev','gpd','lp3','ln2','gum','exp','wei']

    # Q-Q plot
    fig=plt.figure(figsize=(16,8),facecolor='w')
    for j,fn in enumerate(flist):
        fnameR='_q_'+fn+'.txt'
        data=np.loadtxt(fnameR, skiprows=0)
        xp=data[:,0]
        yp=data[:,1]
        tstr=slist[j]
        drawqq(xp,yp,tstr,ss[j],j+1)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_qq.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()

    # CDF plot
    fig=plt.figure(figsize=(16,8),facecolor='w')
    for j,fn in enumerate(flist):
        fnameR='_c_'+fn+'.txt'
        data=np.loadtxt(fnameR, skiprows=0)
        xp=data[:,0]
        yp=data[:,1]
        tstr=slist[j]
        drawcum(xx,pp,xp,yp,tstr,j+1)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_cdf.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()

    # PDFplot + Histogram
    fig=plt.figure(figsize=(16,8),facecolor='w')
    for j,fn in enumerate(flist):
        fnameR='_d_'+fn+'.txt'
        data=np.loadtxt(fnameR, skiprows=0)
        xp=data[:,0]
        yp=data[:,1]
        tstr=slist[j]
        drawhist(xx,xp,yp,tstr,j+1)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_hist.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


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

3変数ワイブル分布における別解法

3変数ワイブル分布で leastsq を用いる場合に,推定値が初期値に対して敏感であり,初期値の値によっては意図しない結果が得られてしまう.例えば,前橋のデータの場合,parameter0=np,array([1,1,1]) では非現実的な回帰推定値が計算されてしまうが,parameter0=np.array([10,10,10]) の場合は,現実的な回帰推定値が得られる.

このため,別解法による計算を試みた.
この問題では推定すべき母数が3個あるため,まず最初に位置母数 $c$ を推定し既知とした上で,形状母数 $k$ と尺度母数 $a$ を推定する方法をとる.

(Step-1)母数 c の推定

標本値 $x$ の非超過確率 $F(x)$ は,選定したプロッティング・ポジション公式により算定する.
確率分布関数を変形して,

\begin{equation}
1-F(x)=\exp\left[-\left(\frac{x-c}{a}\right)^k\right]
\end{equation}

上式の両辺の対数を2回とることにより

\begin{equation}
\ln\{-\ln[1-F(x)]\}=k \ln(x-c)-k \ln a \qquad\rightarrow\qquad Y=A\cdot X+B
\end{equation}

上記より,標本数を$N$として,

\begin{equation}
Y_i=\ln\{-\ln[1-F(x_i)]\} \qquad X_i=\ln(x_i-c)
\end{equation}
\begin{equation}
A=\cfrac{N \sum(X_i Y_i)-\sum X_i\cdot \sum Y_i}{N \sum X_i{}^2-(\sum X_i)^2} \qquad
B=\cfrac{\sum X_i{}^2\cdot \sum Y_i-\sum X_i\cdot \sum(X_i Y_i)}{N \sum X_i{}^2-(\sum X_i)^2}
\end{equation}
\begin{equation}
r=\cfrac{N \sum(X_i Y_i)-\sum X_i \cdot \sum Y_i}{\sqrt{[N \sum X_i{}^2-(\sum X_i)^2]\cdot[N \sum Y_i{}^2-(\sum Y_i)^2]}}
\end{equation}
\begin{equation}
k=-A \qquad a=\exp(-B/A)
\end{equation}

標本値を $Y_i=\ln{-\ln[1-F(x_i)]}$,$X_i=\ln(x_i-c)$ として直線回帰することにより,$k=A$,$a=\exp(-B/A)$ として求めることができる.
この際,$c$ の値を,1から徐々に増加させ,直線回帰による相関係数 $r$ が最大となる時の $c$ を求める $c$ とする.対数関数の引数は正の実数であるため,$c$を変化させる範囲は,標本値$x$の最小値未満である必要がある.

この段階で,一応3母数 $k$,$a$,$c$ は求まったことになるが,更に推定精度を上げるため,$c$ のみを固定し,次の段階で再度 $k$,$a$ を算定する.

(Step-2)k および a の推定

$c$ を既知として,次式を用いて,optimize.leastsq により $k$ および $a$ を求める.非線形回帰の初期値としては,Step-1で求めた $[k, a]$ を用いることができる.

\begin{equation}
x_p=c+a[-\ln{(1-p)}]^{1/k}
\end{equation}

結果グラフ

Step-1での結果

この方法では,確率密度関数と観測値ヒストグラムの関係は比較的良好であるが,大きな観測値の予測精度が他の方法に比べて良くない.

fig_maebashi_wei1.jpg

Step-1 + Step-2の結果

この方法では,Step-1までの結果に比べ,確率密度関数と観測値ヒストグラムの関係は悪くなるが,大きな観測値の予測精度がStep-1までの結果に比べ,よくなっている.

fig_maebashi_wei2.jpg

Step-1 + Step-2 で推定した母数を初期値として leastsq を利用

この方法では,確率密度関数と観測値ヒストグラムの関係は悪くなるが,大きな観測値の予測精度は,今回比較している3手法のうち最も良い.また,前述のプログラム py_calc.py での計算結果と同じ値に収束している.

fig_maebashi_wei3.jpg

プログラム

  • 関数 wei1:Step-1まで
  • 関数 wei2:Step-1 + Step-2
  • 関数 wei3:関数 wei2 で推定した母数を初期値として leastsq を利用
py_wei.py
#=========================
# Weibull distribution
#=========================
import sys
import numpy as np
from scipy.stats import norm  # normal distribution
from scipy import optimize
import matplotlib.pyplot as plt


# Declaration of global variables
fnameL='temp'
xmin=0; xmax=600; dx=100
# coefficient for plotting position formula
#alpha=0.0 # Weibull
alpha=0.4 # Cunnane
#alpha=0.5 # Hazen


def drawqq(xx,yy,tstr,ss):
    ymin=xmin; ymax=xmax; dy=dx
    fsz=12 # font size
    plt.subplot(131)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Observed (mm/day)')
    plt.ylabel('Predicted (mm/day)')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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(xx,yy,'o')
    plt.plot([xmin,xmax],[ymin,ymax],'-',color='#999999',lw=1)
    xs=xmin+(xmax-xmin)*0.03
    ys=ymin+(ymax-ymin)*0.97
    plt.text(xs,ys,ss,ha='left',va='top',fontname='monospace')


def drawcum(xx,pp,yy,ff,tstr):
    ymin=0; ymax=1.0; dy=0.25
    fsz=12 # font size
    plt.subplot(132)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/day)')
    plt.ylabel('Non-exceedance Probability')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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(xx,pp,'x',color='#0000ff',markersize=3,label='Observed')
    plt.plot(yy,ff,'-',color='#ff0000',lw=1,label='Predicted')
    plt.legend()


def drawhist(xx,xpdf,ypdf,tstr):
    ymin=0; ymax=0.02; dy=0.005
    fsz=12 # font size
    plt.subplot(133)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/day)')
    plt.ylabel('Normalized frequency')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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)
    edges = range(xmin,xmax,10)
    plt.hist(xx,color='#00ccff',bins=edges,density=True,label='Observed')
    plt.plot(xpdf,ypdf,'-',color='#ff0000',lw=2,label='Predicted')
    plt.legend()


def wei1(xx,pp):
    # parameter calculation by Step-1
    ac=np.arange(1,np.min(xx),0.1)
    n=len(xx)
    y=np.log(-np.log(1-pp))
    rr=np.zeros(len(ac),dtype=np.float64)
    for i,cc in enumerate(ac):
        x=np.log(xx-cc)
        rr[i]=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/np.sqrt(n*np.sum(x**2)-(np.sum(x))**2)/np.sqrt(n*np.sum(y**2)-(np.sum(y))**2)
    cc=ac[np.argmax(rr)]
    x=np.log(xx-cc)
    c1=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/(n*np.sum(x**2)-(np.sum(x))**2)
    c2=(np.sum(x**2)*np.sum(y)-np.sum(x)*np.sum(x*y))/(n*np.sum(x**2)-(np.sum(x))**2)
    kk=c1
    aa=np.exp(-c2/c1)
    # plot data
    yy=cc+aa*(-np.log(1-pp))**(1/kk)
    rr=np.corrcoef(xx, yy)
    tstr='Weibull (Step-1)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr[0][1])
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa*(-np.log(1-ff))**(1/kk)
    xpdf=np.arange(cc+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=kk/aa*coef**(kk-1)*np.exp(-coef**kk)

    fig=plt.figure(figsize=(12,4),facecolor='w')
    drawqq(xx,yy,tstr,ss)
    drawcum(xx,pp,yc,ff,tstr)
    drawhist(xx,xpdf,ypdf,tstr)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_wei1.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def wei2(xx,pp):
    def func_wei(parameter,xx,pp,cc):
        kk=parameter[0]
        aa=parameter[1]
        est=cc+aa*(-np.log(1-pp))**(1/kk)
        residual=xx-est
        return residual
    # parameter calculation by Step-1
    ac=np.arange(1,np.min(xx),0.1)
    n=len(xx)
    y=np.log(-np.log(1-pp))
    rr=np.zeros(len(ac),dtype=np.float64)
    for i,cc in enumerate(ac):
        x=np.log(xx-cc)
        rr[i]=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/np.sqrt(n*np.sum(x**2)-(np.sum(x))**2)/np.sqrt(n*np.sum(y**2)-(np.sum(y))**2)
    cc=ac[np.argmax(rr)]
    x=np.log(xx-cc)
    c1=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/(n*np.sum(x**2)-(np.sum(x))**2)
    c2=(np.sum(x**2)*np.sum(y)-np.sum(x)*np.sum(x*y))/(n*np.sum(x**2)-(np.sum(x))**2)
    kk=c1
    aa=np.exp(-c2/c1)
    # parameter calculation by Step-2
    parameter0 = [kk,aa]
    result = optimize.leastsq(func_wei,parameter0,args=(xx,pp,cc))
    kk=result[0][0]
    aa=result[0][1]
    # plot data
    yy=cc+aa*(-np.log(1-pp))**(1/kk)
    rr=np.corrcoef(xx, yy)
    tstr='Weibull (Step-2)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr[0][1])
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa*(-np.log(1-ff))**(1/kk)
    xpdf=np.arange(cc+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=kk/aa*coef**(kk-1)*np.exp(-coef**kk)

    fig=plt.figure(figsize=(12,4),facecolor='w')
    drawqq(xx,yy,tstr,ss)
    drawcum(xx,pp,yc,ff,tstr)
    drawhist(xx,xpdf,ypdf,tstr)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_wei2.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def wei3(xx,pp):
    def func_wei(parameter,xx,pp):
        kk=parameter[0]
        cc=parameter[1]
        aa=parameter[2]
        est=cc+aa*(-np.log(1-pp))**(1/kk)
        residual=xx-est
        return residual
    # parameter calculation
    ac=np.arange(1,np.min(xx),0.1)
    n=len(xx)
    y=np.log(-np.log(1-pp))
    rr=np.zeros(len(ac),dtype=np.float64)
    for i,cc in enumerate(ac):
        x=np.log(xx-cc)
        rr[i]=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/np.sqrt(n*np.sum(x**2)-(np.sum(x))**2)/np.sqrt(n*np.sum(y**2)-(np.sum(y))**2)
    cc=ac[np.argmax(rr)]
    x=np.log(xx-cc)
    c1=(n*np.sum(x*y)-np.sum(x)*np.sum(y))/(n*np.sum(x**2)-(np.sum(x))**2)
    c2=(np.sum(x**2)*np.sum(y)-np.sum(x)*np.sum(x*y))/(n*np.sum(x**2)-(np.sum(x))**2)
    kk=c1
    aa=np.exp(-c2/c1)
    parameter0 = [kk,cc,aa]
    result = optimize.leastsq(func_wei,parameter0,args=(xx,pp))
    kk=result[0][0]
    cc=result[0][1]
    aa=result[0][2]
    # plot data
    yy=cc+aa*(-np.log(1-pp))**(1/kk)
    rr=np.corrcoef(xx, yy)
    tstr='Weibull (amended)'
    ss='k={0:7.3f}\n'.format(kk)
    ss=ss+'c={0:7.3f}\n'.format(cc)
    ss=ss+'a={0:7.3f}\n'.format(aa)
    ss=ss+'(r={0:.3f})'.format(rr[0][1])
    ff=np.arange(0.01,1.00,0.01)
    yc=cc+aa*(-np.log(1-ff))**(1/kk)
    xpdf=np.arange(cc+1,xmax,1)
    coef=(xpdf-cc)/aa
    ypdf=kk/aa*coef**(kk-1)*np.exp(-coef**kk)

    fig=plt.figure(figsize=(12,4),facecolor='w')
    drawqq(xx,yy,tstr,ss)
    drawcum(xx,pp,yc,ff,tstr)
    drawhist(xx,xpdf,ypdf,tstr)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_wei3.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def main():
    global fnameL
    args = sys.argv
    fnameL=args[1] # location
    # data input
    fnameR='inp_'+fnameL+'.txt'
    data=np.loadtxt(fnameR, skiprows=1)
    xx =data[:,2]
    xx=np.sort(xx)
    # setting protting position
    n=len(xx)
    ii=np.arange(n)+1
    pp=(ii-alpha)/(n+1-2*alpha)
    # parameter calculation
    wei1(xx,pp)
    wei2(xx,pp)
    wei3(xx,pp)


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

正規分布と年降水量の関係

正規分布

統計量$x$が正規分布に従う場合の,確率密度関数,確率分布函数,クオンタイルの数式表現を以下に示す.

確率密度関数

\begin{equation}
f(x)=\frac{1}{\sigma_x\sqrt{2\pi}}\cdot\exp\left[-\frac{1}{2}\left(\frac{x-\mu_x}{\sigma_x}\right)^2\right]
\end{equation}

確率分布関数

\begin{equation}
F(x)=\Phi\left(\frac{x-\mu_x}{\sigma_x}\right)
\qquad \Phi(z)=\frac{1}{\sqrt{2\pi}}\int_{-\infty}^z\exp\left(-\frac{1}{2}t^2\right)dt
\end{equation}

非超過確率pに対する水文統計量xp

\begin{equation}
z=\frac{x-\mu_x}{\sigma_x} \qquad\rightarrow\qquad x=\mu_x+\sigma_x z
\end{equation}
\begin{equation}
x_p=\mu_x+\sigma_x z_p \qquad \text{$z_p$は$p=\Phi(z)$となる$z$の値}
\end{equation}

結果グラフ

fig_maebashi_nor.jpg

母数推定プログラム

py_nor.py
#=========================
# Normal distribution
#=========================
import sys
import numpy as np
from scipy.stats import norm  # normal distribution
from scipy import optimize
import matplotlib.pyplot as plt


# Declaration of global variables
fnameL='temp'
xmin=0; xmax=5000; dx=1000
# coefficient for plotting position formula
#alpha=0.0 # Weibull
alpha=0.4 # Cunnane
#alpha=0.5 # Hazen


def drawqq(xx,yy,tstr,ss):
    ymin=xmin; ymax=xmax; dy=dx
    fsz=12 # font size
    plt.subplot(131)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Observed (mm/year)')
    plt.ylabel('Predicted (mm/year)')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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(xx,yy,'o')
    plt.plot([xmin,xmax],[ymin,ymax],'-',color='#999999',lw=1)
    xs=xmin+(xmax-xmin)*0.03
    ys=ymin+(ymax-ymin)*0.97
    plt.text(xs,ys,ss,ha='left',va='top',fontname='monospace')


def drawcum(xx,pp,yy,ff,tstr):
    ymin=0; ymax=1.0; dy=0.25
    fsz=12 # font size
    plt.subplot(132)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/year)')
    plt.ylabel('Non-exceedance Probability')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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(xx,pp,'x',color='#0000ff',markersize=3,label='Observed')
    plt.plot(yy,ff,'-',color='#ff0000',lw=1,label='Predicted')
    plt.legend()


def drawhist(xx,xpdf,ypdf,tstr):
    ymin=0; ymax=0.003; dy=0.001
    fsz=12 # font size
    plt.subplot(133)
    plt.rcParams['font.size']=fsz
    plt.rcParams['font.family']='sans-serif'
    plt.xlabel('Precipitation (mm/year)')
    plt.ylabel('Normalized frequency')
    plt.xlim([xmin,xmax])
    plt.ylim([ymin,ymax])
    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)
    edges = range(xmin,xmax,100)
    plt.hist(xx,color='#00ccff',bins=edges,density=True,label='Observed')
    plt.plot(xpdf,ypdf,'-',color='#ff0000',lw=2,label='Predicted')
    plt.legend()


def nor(xx,pp):
    def func_nor(parameter,xx,zz):
        mu=parameter[0]
        sg=parameter[1]
        est=mu+sg*zz
        residual=xx-est
        return residual
    # parameter calculation
    zz=norm.ppf(pp, loc=0, scale=1)
    npara=2; parameter0 = np.ones(npara,dtype=np.float64)
    result = optimize.leastsq(func_nor,parameter0,args=(xx,zz))
    mu=result[0][0]
    sg=result[0][1]
    # plot data
    yy=mu+sg*zz
    rr=np.corrcoef(xx, yy)
    tstr='Normal'
    ss='m={0:7.3f}\n'.format(mu)
    ss=ss+'s={0:7.3f}\n'.format(sg)
    ss=ss+'(r={0:.3f})'.format(rr[0][1])
    ff=np.arange(0.01,1.00,0.01)
    zz=norm.ppf(ff, loc=0, scale=1)
    yc=mu+sg*zz
    xpdf=np.arange(xmin+1,xmax,1)
    coef=(xpdf-mu)/sg
    ypdf=1/sg/np.sqrt(2*np.pi)*np.exp(-1/2*(coef)**2)

    fig=plt.figure(figsize=(12,4),facecolor='w')
    drawqq(xx,yy,tstr,ss)
    drawcum(xx,pp,yc,ff,tstr)
    drawhist(xx,xpdf,ypdf,tstr)
    plt.tight_layout()
    fnameF='fig_'+fnameL+'_nor.jpg'
    plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
    plt.show()


def main():
    global fnameL
    args = sys.argv
    fnameL=args[1] # location
    # data input
    fnameR='inp_'+fnameL+'.txt'
    data=np.loadtxt(fnameR, skiprows=1)
    xx =data[:,1]
    xx=np.sort(xx)
    # setting protting position
    n=len(xx)
    ii=np.arange(n)+1
    pp=(ii-alpha)/(n+1-2*alpha)
    # parameter calculation
    nor(xx,pp)


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

以 上

15
13
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
15
13