Python でガンマ分布の計算をする方法を調べてみました。
結果を記載します。


ガンマ分布とは

そもそも、ガンマ分布とは何かがよくわからないので、ガンマ分布について記載します。

書籍

統計学入門 - 東京大学出版会 には以下の内容が記載されています。引用します。

6.8 ガンマ分布
ガンマ分布は Gamma distribution は指数分布(5.10) を一般化したものであって、次の確率密度関数で表される。
$$f(x) = \frac{\lambda^a}{\Gamma(a)}x^{\alpha-1}e^{-\lambda x} (x\geqq0), 0 (x<0) $$ ここで、$\alpha > 0$ である。ガンマ分布の重要な部分は$x^{\alpha-1}e^{-\lambda x}$の部分であり、これから$\alpha = 1$ ならば指数分布になることがわかる。

実世界での

実世界で、ガンマ分布となる事象には以下があります。

  • 体重の分布
  • エイズの潜伏期間
  • システム・ダウンまでの待ち時間
  • 電子部品の寿命
  • 保険金の支払額のモデル

Web

Web上の情報だと、以下が見つかりました。
* ガンマ分布 - Wikipedia
* デジタルマーケティングにおける基礎的な統計解析 | レポート・コラム | NRIネットコム
* ガンマ分布の意味と期待値、分散 | 高校数学の美しい物語
* ガンマ分布


ガンマ分布の計算方法

Python では以下のライブラリでガンマ分布の計算が行えます。
* numpy.random.gamma — NumPy v1.15 Manual
* scipy.stats.gamma — SciPy v1.2.0 Reference Guide
* tf.random.gamma  |  TensorFlow


numpy.random.gamma を使う

numpy.random.gamma では、ガンマ分布に従う乱数を持つ配列が取得できます。
* shape=2scale=2指定して配列を取得する

import numpy as np
shape, scale = 2., 2.
s = np.random.gamma(shape, scale, 1000)
print(s)

[ 3.82769986  0.57987294  8.71851465  2.09624801  3.07992525  1.33682621
  1.52681352  1.1038436   0.32452196  2.61687864  2.31279027  4.1192877
 11.93373265  1.49622973  2.43055922  1.36888852  2.43573475  6.16885654
  3.43197913  4.41584039  2.63252622  5.84375382  2.4926901   4.09717026
  0.54727079  1.37837228  1.57989827  3.60335118  7.90316212  1.73799822
  3.02557085  1.22738672  0.64397655  6.65220901  7.68344347  9.00825348
  1.30000658  4.21622653  3.81001449  5.44076707  0.29190632  3.4004747
  3.31920514  2.61953315  4.02076256  1.57446158  4.48032433  3.14871272
  0.77085133  8.99744653  1.55685237  1.53318994  4.99302374  1.25386129
  4.28039121  5.74574233  1.36186417 10.10874118  3.65846922  2.02873105
  3.34896627  0.52464966  2.091951    2.28638439  2.75070701 15.00023712
  1.66613084]
  • $\alpha = 1$を指定して指数分布に従う配列を取得、結果をグラフ描画する
    shape=1scale=1指定すると、指数分布に従う配列が取得できます。
    以下は、取得した結果をグラフに描画します。

%matplotlib inline
import numpy as np
shape, scale = 1., 1
s = np.random.gamma(shape, scale, 10000)
import matplotlib.pyplot as plt
import scipy.special as sps
count, bins, ignored = plt.hist(s, 50, density=True)
y = bins**(shape-1)*(np.exp(-bins/scale) /
                      (sps.gamma(shape)*scale**shape))
plt.plot(bins, y, linewidth=2, color='r')

[<matplotlib.lines.Line2D at 0x130fff2b0>]

20190207_output_6_1.png - Google ドライブ

  • np.random.exponential指数分布のグラフを描画する
    np.random.exponential使って、指数分布のグラフを描画してみます。

%matplotlib inline
import numpy as np
import pylab as plt

target = 1
beta = 1.0/target
Y = np.random.exponential(beta, 10000)
plt.hist(Y, density=True, bins=200,lw=0,alpha=.8)
plt.show()

20190207_output_8_0.png - Google ドライブ

np.random.gamma np.random.exponential同じような結果が取得できました。

  • shape値を変化させる
    shape値を変化させて、取得できる配列のグラフを描画します。

%matplotlib inline
import matplotlib.pyplot as plt

def plot_gamma(shape, scale):
    s = np.random.gamma(shape, scale, 1000)
    import scipy.special as sps
    count, bins, ignored = plt.hist(s, 50, density=True)
    y = bins**(shape-1)*(np.exp(-bins/scale) /
                      (sps.gamma(shape)*scale**shape))
    plt.plot(bins, y, linewidth=2, color='r')

shape, scale = 1., 1.
plot_gamma(shape, scale)
shape, scale = 2., 1.
plot_gamma(shape, scale)
shape, scale = 3., 1.
plot_gamma(shape, scale)
import matplotlib.pyplot as plt
plt.show()

20190207_output_11_0.png - Google ドライブ

shape値が123変化するにつれ、最頻値の取り得る座標が移動していきます。

scipy.stats.gamma使う

scipy.stats.gamma使用して、ガンマ分布の計算ができます。
使い方はやはり、他のscipy.stats関数群と同様です。

%matplotlib inline
from scipy.stats import gamma
import matplotlib.pyplot as plt
import numpy as np


def plot_gamma(a):
    fig, ax = plt.subplots(1, 1)
    # -----------------------------
    # モーメントを求める
    # ---------------------
    # 以下をいつも忘れる
    # mean 平均, var 分散,skew 歪度,kurt 尖度

    mean, var, skew, kurt = gamma.stats(a, moments='mvsk')
    print("平均: {0}, 分散: {1}, 歪度: {2}, 尖度: {3}".format(mean, var, skew, kurt))

    # numpy.linespace で等差数列を生成する
    # gamma.ppf は、%点関数
    x = np.linspace(gamma.ppf(0.01, a),
                     gamma.ppf(0.99, a), 100)

    #  pdf は、確率密度関数 これもいつも忘れる。   
    # plot の引数 alpha は、透過度を示す値、lw は線の太さを指定     
    ax.plot(x, gamma.pdf(x, a),'r-', lw=5, alpha=0.6, label='gamma pdf')

    # 固定型統計分布: frozen RV
    rv = gamma(a)
    ax.plot(x, rv.pdf(x), 'k-', lw=2, label='frozen pdf')

    #  xが0.001,0.5, 0.999 での%点関数 を求める    
    vals = gamma.ppf([0.001, 0.5, 0.999], a)

    # numpy.allclose で配列の等価、不等価の判定ができる。    
    allclose_result = np.allclose([0.001, 0.5, 0.999], gamma.cdf(vals, a))
    print("allclose_result: {0}".format(allclose_result))

    # rvs でガンマ分布に従う乱数を生成、size は 1000 なので 要素数 1000 になる。 デフォルトは size = 1
    r = gamma.rvs(a, size=1000)
    # ヒストグラム描画、bins 指定なしでデフォルト 10 , stepfilled は 塗りつぶしありの線    
    # densityがTrueの場合、戻確率密度を形成するように正規化される。
    ax.hist(r, density=True, histtype='stepfilled', alpha=0.2)
    ax.legend(loc='best', frameon=False)
    plt.show()


# a=5 のガンマ分布
plot_gamma(5.0)

# a=1  指数分布
plot_gamma(1.0)

平均: 5.0, 分散: 5.0, 歪度: 0.8944271909999159, 尖度: 1.2
allclose_result: True

20190207_output_14_1.png - Google ドライブ

平均: 1.0, 分散: 1.0, 歪度: 2.0, 尖度: 6.0
allclose_result: True

20190207_output_14_3.png - Google ドライブ


tf.random.gamma使う

tf.random.gamma は、numpy.random.gamma似た使い方ができます。
ガンマ分布に従う乱数を生成します。

%matplotlib inline
import tensorflow as tf
import pylab as plt
#  10000要素 alpha=1 つまり指数分布に従う配列を作成する     
samples = tf.random_gamma(shape=[10000], alpha=1)

# Sessionオブジェクトを作る
# 初めてTensorFLow 触りましたが、考え方に慣れておらずあまり意味わかっておりません。    
with tf.Session() as sess:
    print(samples.__dict__)

    y = samples.eval()
    plt.hist(y, density=True, bins=200,lw=0,alpha=.8)
    plt.show()

# alpha を定数化
alpha = tf.constant([[1.],[3.]])
# beta を定数化
beta = tf.constant([[3., 4.]])
# 2x2 の多次元配列を、3要素生成する
samples = tf.random_gamma([3], alpha=alpha, beta=beta)
with tf.Session() as sess:    
    y = samples.eval()
    print(y)


# tf.reduce_mean 与えたリストに入っている数値の平均値を求める関数    
# tf.square 要素ごとに二乗をとる
loss = tf.reduce_mean(tf.square(samples))
# 勾配を求める    
dloss_dalpha, dloss_dbeta = tf.gradients(loss, [alpha, beta])
# 損失関数の不偏確率導関数?    
print("alpha.shape == dloss_dalpha.shape", alpha.shape == dloss_dalpha.shape)  # True
print("beta.shape == dloss_dbeta.shape", beta.shape == dloss_dbeta.shape)  # True

{'_op': <tf.Operation 'random_gamma_27/Maximum' type=Maximum>, '_value_index': 0, '_dtype': tf.float32, '_shape_val': None, '_consumers': [], '_id': 318}

20190207_output_16_1.png - Google ドライブ

[[[0.0756161  0.03140004]
  [1.9382031  0.4650259 ]]

 [[0.1251423  0.14494717]
  [1.268082   0.6238722 ]]

 [[0.27819878 0.08588854]
  [0.98764956 0.29059193]]]
alpha.shape == dloss_dalpha.shape True
beta.shape == dloss_dbeta.shape True

使い方がわからず、諦めた

TensorFlow Probability に tf.distributions.Gammaいう 関数がありますが、使い方がわからず、記載を諦めました。
tf.distributions.Gamma  |  TensorFlow

参考

TensorFlow が初見には難しすぎて、これは別途修行が必要に思いました。
以上です。

コメント