ガンマ分布に関連した数値計算のあれこれ
ガンマ分布とは?
ガンマ分布 (Gamma distribution) とは指数分布を一般化した確率分布で
- 人の体重
- ウイルスの潜伏期間
- 電子部品の寿命
- トラフィックの待ち時間
- 所得
などがガンマ分布に従うと言われています。形状パラメータ
と表されます。ガンマ関数
ガンマ分布はパラメータ
と定義されることもあります。
ガンマ分布の予備知識
指数分布との関係性
ある期間に平均して
この確率変数が
このため、ガンマ分布は「ある期間に平均して
※
平均と分散
E(X) = k \theta = k / \lambda V(X) = k \theta^2 = k / \lambda^2
再生性
となります。
パラメータ推定
モーメント法
モーメントを元にパラメータ推定を行う方法をモーメント法 (method of moment) といいます。モーメント法それ自体の説明は「モーメント法と最尤推定法の関連性についてメモ」がわかりやすいので、詳しく知りたい方はご覧ください。ここではガンマ分布のパラメータ推定の導出過程だけ書きます。
ガンマ分布の
なので、標本平均を
E[X] = k \theta = \mu E[X^2] = k (k+1) \theta^2 = \sigma^2 + \mu^2
となるはずです。これを
k = \mu^2 / \sigma^2 \theta = \sigma^2 / \mu
となり、パラメータを求められます。最尤推定の方が精度は良いですが、モーメント法でもそこそこの精度でパラメータを推定できます。
最尤推定
最尤推定 (maximum likelihood estimation) とは、尤度関数を最大化するようなパラメータを求める手法です。
となり、
局所近似最大化法
最適化の方法はいくつかありますが、ここでは Minka (2002) で提案された局所近似最大化法を紹介します。途中計算は「高速なガンマ分布の最尤推定法について - RPubs」を参照してもらうとして、以下の式を繰り返し計算すると、ガンマ分布のパラメータを求めることができます。
ただし、
Python で書くとこんな感じになります。
import numpy as np
from scipy.special import gamma, loggamma, digamma, polygamma
def estimate_gamma_parameters(
x: np.ndarray,
tol: float = 1e-6,
max_iter: int = 1000
) -> tuple[float, float, float]:
"""
Estimate parameters of Gamma distribution.
Returns
-------
k: float
The shape parameter.
theta: float
The scale parameter.
log_likelihood: float
The log likelihood.
"""
n = len(x)
mean_x = np.mean(x)
mean_log_x = np.mean(np.log(x))
a = np.log(mean_x) - mean_log_x
k = 0.5 / a
theta = mean_x / k
# ll = log likelihood
ll0 = n * (k - 1) * mean_log_x - n * loggamma(k) - n * k * np.log(theta) - n * mean_x / theta
for _ in range(max_iter):
k = 1 / (1/k + (-a - digamma(k) + np.log(k)) / (k * k * (1/k - polygamma(1, k))))
theta = mean_x / k
ll1 = n * (k - 1) * mean_log_x - n * loggamma(k) - n * k * np.log(theta) - n * mean_x / theta
if np.abs(ll1 - ll0) < tol:
break
ll0 = ll1
return (k, theta, ll1)
累積分布関数(不完全ガンマ関数)
ガンマ関数の累積分布関数は正則化された第1種不完全ガンマ関数 (lower incomplete gamma) となります。
第1種不完全ガンマ関数はガンマ関数の積分区間
正則化第1種不完全ガンマ関数
不完全ガンマ関数をガンマ関数で割った関数を正則化不完全ガンマ関数 (regularized incomplete gamma) といいます。
正則化不完全ガンマ関数の数値計算
正則化不完全ガンマ関数を数値的に求める方法はいくつかありますが、ここではテイラー展開を使う方法を紹介します。
import numpy as np
from scipy.special import gamma
def rligamma(a: np.ndarray, x: np.ndarray, num_iter: int = 1000) -> np.ndarray:
"""
Regularized lower incomplete gamma function by Taylor expansion.
γ(a, x) = x^a * exp(-x) * sum(k=0..infty, x^k / Γ(a+k))
"""
u = np.power(x, a) * np.exp(-x) / gamma(a + 1)
s = u
for k in range(1, num_iter):
u = u * (x / (a + k))
s += u
return s
他の計算方法は 吉田 (2012) が詳しいです。
正則化不完全ガンマ関数の逆関数の数値計算
正則化不完全ガンマ関数が累積密度関数であるという前提に立つと、単調増加であることがわかります。単調増加ということは、二分法で解を求めることができます。
- 最初に適当な区間
を決める。x_\mathrm{min}, x_\mathrm{max} - 経験的に
が良さそう。x_\mathrm{min} = 0, x_\mathrm{max} = 1.3 a + 40
- 経験的に
- 以下を繰り返す。
- 区間の中間点
と中間点におけるx_c = (x_\mathrm{min} + x_\mathrm{max}) / 2 を計算する。y_c = \gamma(a, x_c) / \Gamma(a) -
とy_c が十分近い値なら\hat{y} を出力して終了する。\hat{x} -
ならばy_c < \hat{y} なので、\hat{x} \in [x_c, x_\mathrm{max}] として次のループへx_\mathrm{min} \leftarrow x_c -
ならばy_c > \hat{y} なので、\hat{x} \in [x_\mathrm{max}, x_c] として次のループへx_\mathrm{max} \leftarrow x_c
- 区間の中間点
初期値の
x=1.3a+40 は y=0.99999999 となる x の上限になっている
def inverse_rligamma(
a: float,
y: float,
tol: float = 1e-6,
max_iter: int = 1000,
rligamma_kwargs = {}
) -> float:
"""
The inverse of regularized lower incomplete gamma function.
"""
xmin = 0.0
xmax = 1.3 * a + 40
for _ in range(max_iter):
xc = (xmax + xmin) / 2
yc = rligamma(a, xc, **rligamma_kwargs)
if np.abs(y - yc) < tol:
break
elif yc < y:
xmin = xc
else:
xmax = xc
return xc
Discussion