以下の内容はhttps://www.anarchive-beta.com/entry/2022/01/31/180000より取得しました。


【Python】1次元ガウス分布の作図

はじめに

 機械学習や統計学で登場する各種の確率分布について、「計算式の導出・計算のスクラッチ実装・計算過程や結果の可視化」などの「数式・プログラム・図」を用いた解説により、様々な角度から理解を目指すシリーズです。

 この記事では、ガウス分布のグラフ作成についてPythonを使って確認します。

【前の内容】

www.anarchive-beta.com

【他の内容】

www.anarchive-beta.com

【今回の内容】

1次元ガウス分布の作図

 1次元ガウス分布(Gaussian distribution・正規分布・Normal distribution)のグラフを作成します。この記事では、PythonのMatplotlibライブラリを利用して作図します。
 ガウス分布については「1次元ガウス分布の定義式 - からっぽのしょこ」、確率密度計算については「【Python】1次元ガウス分布の計算 - からっぽのしょこ」、Rを利用する場合は「【R】1次元ガウス分布の作図 - からっぽのしょこ」を参照してください。

 利用するライブラリを読み込みます。

# ライブラリを読込
import numpy as np
from scipy.stats import norm
import matplotlib.pyplot as plt


グラフの作成

 まずは、1次元ガウス分布のグラフを作成します。

パラメータの設定

 ガウス分布のパラメータ  \mu, \sigma を指定します。

# 平均パラメータを指定
mu = 0.0
print(mu)

# 標準偏差パラメータを指定
sigma = 1.0
print(sigma)
0.0
1.0

 平均(実数)  \mu、標準偏差(正の値)  \sigma \gt 0 を指定します。

 確率変数  x の作図範囲を設定します。

# x軸の範囲を設定
#x_min = -5.0
#x_max = 5.0
k = 4.0
u = 5.0
x_size = sigma # 基準値を指定
x_size *= k # 定数倍
x_min  = mu - x_size
x_max  = mu + x_size
x_min  = np.floor(x_min /u)*u # u単位で切り下げ
x_max  = np.ceil(x_max /u)*u  # u単位で切り上げ
print(x_min, x_max)

# x軸の値を作成
x_vec = np.linspace(start=x_min, stop=x_max, num=1001)
print(x_vec[:5])
-5.0 5.0
[-5.   -4.99 -4.98 -4.97 -4.96]

 範囲を指定して、確率変数がとり得る値(実数)  x の数値ベクトルを作成します。
 この例では、指定したパラメータ mu, sigma を使って、平均を中心に標準偏差の倍数  \mu \pm k \sigma の範囲を設定しています。

 続いて、設定した値に従う確率密度を計算して、グラフを作成していきます。

確率分布の計算

  x の値ごとにガウス分布に従う確率密度  \mathcal{N}(x \mid \mu, \sigma^2) を計算します。

# 確率密度を計算
dens_vec = norm.pdf(x=x_vec, loc=mu, scale=sigma)
print(dens_vec[:5])
[1.48671951e-06 1.56286711e-06 1.64275059e-06 1.72654452e-06
 1.81443119e-06]

 1次元ガウス分布の確率密度は、sp.stats.norm.pdf() で計算できます。確率変数の引数 x x (の配列)、平均の引数 loc \mu、標準偏差の引数 scale \sigma を指定します。

確率分布の作図

 パラメータラベルを作成します。装飾用の処理です。

# ラベル用の文字列を作成
param_lbl = f'$\\mu = {mu}, \\sigma = {sigma}$'
print(param_lbl)
[tex: \mu = 0.0, \sigma = 1.0$

 ギリシャ文字などの記号を使った数式を表示する場合は、LaTeXの記法(LaTeXコマンド)を使います。数式に変換する範囲(コマンドの文字列)を $ で挟んで、'$コマンド$' の形式で文字列を指定します。ただし、メタ文字(改行文字 \n など)として認識されないために、バックスラッシュを追加(2つ \\ に)する必要がある場合があります。
 オブジェクト(プログラム上の変数)の値を使う場合は、f文字列(や format() )を使います。変数名を波括弧 {} で挟んで、f'{変数名}' の形式で文字列を指定します。(丸め込みなど)数値の文字列を調整する場合は、format() の記法で指定します。

  ガウス分布のグラフを作成します。

# ガウス分布を作図
plt.figure(figsize=(9, 6), dpi=100, facecolor='white')
plt.plot(
    x_vec, dens_vec, 
    color='#00A968', linewidth=1.0
) # 折れ線
plt.xlabel('$x$') # x軸ラベル
plt.ylabel('density') # y軸ラベル
plt.title(param_lbl, loc='left') # タイトル
plt.suptitle('Gaussian distribution', fontsize=20) # 図タイトル
plt.grid() # グリッド線
plt.show()

1次元ガウス分布の確率密度

 折れ線グラフは、plt.plot() で描画できます。第1引数に変数、第2引数に確率密度を指定します。

 以上で、基本的な作図処理を確認しました。

複数グラフの作成

 次は、複数のパラメータの1次元ガウス分布を並べて描画します。

パラメータの設定

 複数個のパラメータ  \mu, \sigma を設定します。

# 平均パラメータを指定
mu_vals = np.array([-5.0, 0.0, 7.4])
print(mu_vals)

# 標準偏差パラメータを指定
sigma_vals = np.array([2.4, 1.0, 0.5])
print(sigma_vals)
[-5.   0.   7.4]
[2.4 1.  0.5]

 平均  \mu_i、標準偏差  \sigma_i をそれぞれ数値ベクトルで指定します。

確率分布の作図

 1つの領域に複数の分布を重ねて表示する図と、複数の領域に分割してそれぞれの分布を表示する図の2パターンを作成します。

重ねて描画

 こちらのの図は、mu_valssigma_vals が同じパラメータ数である必要があります。

 確率変数  x の作図範囲を設定します。

# x軸の範囲を設定
#x_min = -15.0
#x_max = 10.0
k = 4.0
u = 5.0
x_size_vals = sigma_vals.copy() # 基準値を指定
x_size_vals *= k # 定数倍
x_min = np.min(mu_vals - x_size_vals)
x_max = np.max(mu_vals + x_size_vals)
x_min = np.floor(x_min /u)*u # u単位で切り下げ
x_max = np.ceil(x_max /u)*u  # u単位で切り上げ
print(x_min, x_max)

# x軸の値を作成
x_vec = np.linspace(start=x_min, stop=x_max, num=1001)
print(x_vec[:5])
-15.0 10.0
[-15.    -14.975 -14.95  -14.925 -14.9  ]

 「グラフの作成」のときと同様に処理します。
 この例では、指定したパラメータ mu_vals, sigma_vals を使って、平均を中心に標準偏差の倍数  \mu_i \pm k \sigma_i の最小値から最大値の範囲を設定しています。

  \mu, \sigma x の組み合わせごとに確率密度を計算します。

# パラメータ数を設定
param_num = len(mu_vals)
print(param_num)

# パラメータごとに確率密度を計算:(事前計算の場合)
dens_lt = [
    norm.pdf(x=x_vec, loc=mu_vals[i], scale=sigma_vals[i]) for i in range(param_num)
]
print(np.array(dens_lt).shape)
print(dens_lt[0][:5])
3
(3, 1001)
[2.82345875e-05 2.94854304e-05 3.07883470e-05 3.21453493e-05
 3.35585202e-05]

 計算や作図の処理用に、指定したパラメータ数 param_num を作成しておきます。
 パラメータ( mu_vals, sigma_vals の要素)ごとに、sp.stats.norm.pdf() で1次元ガウス分布の確率密度を計算して、リストに格納します。リスト内包表記により、パラメータ  \mu_i, \sigma_i のペアと変数  x の全ての組み合わせに対して計算できます。

 パラメータごとのガウス分布を重ねて描画します。

# パラメータごとにガウス分布を作図
plt.figure(figsize=(9, 6), dpi=100, facecolor='white')
for i in range(param_num):
    # 値を取得
    mu    = mu_vals[i]    # 平均
    sigma = sigma_vals[i] # 標準偏差
    dens_vec = dens_lt[i] # 確率密度:(事前計算の場合)
        
    # 確率密度を計算:(逐次計算の場合)
    #dens_vec = norm.pdf(x=x_vec, loc=mu, scale=sigma)

    # パラメータラベルを作成
    param_lbl = f'$\\mu = {mu}, \\sigma = {sigma}$'
    
    plt.plot(
        x_vec, dens_vec, 
        linewidth=1.0, 
        label=param_lbl
    ) # 折れ線
plt.xlabel('$x$')
plt.ylabel('density')
plt.suptitle('Gaussian distribution', fontsize=20) # 図タイトル
plt.legend(title='parameter') # 凡例
plt.grid() # グリッド線
plt.show()

1次元ガウス分布の確率密度:パラメータの比較

 for 文でパラメータごとに、「グラフの作成」のときと同様にグラフを描画します。

分割して描画

 こちらの図は、mu_valssigma_vals が異なるパラメータ数でも処理できます。

 確率変数  x の作図範囲を設定します。

# x軸の範囲を設定
k = 2.0
u = 5.0
x_size = np.max(sigma_vals)
x_size *= k # 定数倍
x_min  = np.min(mu_vals) - x_size
x_max  = np.max(mu_vals) + x_size
x_min  = np.floor(x_min /u)*u # u単位で切り下げ
x_max  = np.ceil(x_max /u)*u  # u単位で切り上げ
#x_min = -10.0
#x_max = 15.0
print(x_min, x_max)

# x軸の値を作成
x_vec = np.linspace(start=x_min, stop=x_max, num=1001)
print(x_vec[:5])
-10.0 15.0
[-10.     -9.975  -9.95   -9.925  -9.9  ]

 「グラフの作成」のときと同様に処理します。
 この例では、指定したパラメータ mu_vals, sigma_vals を使って、平均を中心に標準偏差の倍数  \mu_i \pm k \sigma_j の最小値から最大値の範囲を設定しています。

  \mu \sigma の組み合わせごとに確率密度を計算します。

# パラメータ数(サブプロット数)を設定
row_num = len(mu_vals)
col_num = len(sigma_vals)

# パラメータの組み合わせごとに確率密度を計算:(事前計算の場合)
#dens_lt = [
#    [norm.pdf(x=x_vec, loc=mu, scale=sigma) for sigma in sigma_vals] for mu in mu_vals
#]
dens_lt = [
    [norm.pdf(x=x_vec, loc=mu_vals[i], scale=sigma_vals[j]) for j in range(col_num)] for i in range(row_num)
]
print(np.array(dens_lt).shape)
print(dens_lt[0][0][:5])
(3, 3, 1001)
[0.01897665 0.01939192 0.01981412 0.02024332 0.02067958]

 この例では、mu_vals の要素数を行数 row_numsigma_vals の要素数を列数 col_num として、パラメータ数を作成しておきます。
 2つのリスト内包表記により、2つのパラメータ  \mu_i, \sigma_j の全ての組み合わせと、さらに変数  x との全ての組み合わせに対して、確率密度を計算できます。

 パラメータの組み合わせごとに描画領域を分割して分布を描画します。

# 確率密度軸の範囲を設定
#dens_max = 1.0
u = 0.5
dens_max = np.max(dens_lt)
dens_max = np.ceil(dens_max /u)*u # u単位で切り上げ

# パラメータの組み合わせごとにガウス分布を作図
fig, axes = plt.subplots(
    nrows=row_num, ncols=col_num, constrained_layout=True, 
    figsize=(12, 8), dpi=100, facecolor='white'
)
fig.suptitle('Gaussian distribution', fontsize=20)
for i in range(row_num):
    for j in range(col_num):
        # サブプロットを取得
        ax = axes[i, j]
        
        # 値を取得
        mu    = mu_vals[i]    # 平均
        sigma = sigma_vals[j] # 標準偏差
        dens_vec = dens_lt[i][j] # 確率密度:(逐次計算の場合)
        
        # 確率密度を計算:(逐次計算の場合)
        #dens_vec = norm.pdf(x=x_vec, loc=mu, scale=sigma)

        # パラメータラベルを作成
        param_lbl = f'$\\mu = {mu}, \\sigma = {sigma}$'
        
        ax.plot(
            x_vec, dens_vec, 
            linewidth=1.0
        ) # 確率密度
        ax.set_title(param_lbl, loc='left')
        ax.grid()
        ax.set_ylim(ymin=0.0, ymax=dens_max) # 確率密度軸の描画範囲
fig.supxlabel('$x$')
fig.supylabel('density')
plt.show()

1次元ガウス分布の確率密度:パラメータの比較

 plt.subplots() で描画領域を分割してグラフを描画できます。nrows 引数に縦方向の数、ncols 引数に横方向の数を指定します。

 この例では、 \mu の変化を縦方向、 \sigma の変化を横方向に並べています。

 以上で、複数の確率分布の作図処理を確認しました。

 この記事では、1次元のガウス分布のグラフを作成しました。次の記事では、パラメータと分布の関係を可視化します。

参考文献

おわりに

 グラフを描いて満足するつもりだったのだけど、もう少し踏み込みたくなってしまったので、確率論の本をいくつか借りてきて読み始めた。

  • 2025.01.06:加筆修正しました。その際に、この記事から「分布の計算」「パラメータの可視化」「乱数の生成」をそれぞれ独立の記事として分割しました。

 Python版の「パラメータの可視化」については、作図コードの解説を止めたこともありR版と内容は重複するので、Zennの方に投稿しています。

 修正版の更新日に公開された、私立恵比寿中学のライブ映像をどうぞ♪

 これまでは記事の投稿日と記念日などが被ったときに(実のところは記念日に投稿日を合わせることが多いのですが)関連するMVなどを貼ってたのですが、近頃は色んなグループがライブ映像を公開することが多くなったようなので、記事の末尾にとりあえず置いておくことにしました。

 このブログは技術ブログに偽装したオタクブログなので、記事を読んだならぜひ聴いてください♬
 それやこれやが私のブログ運営のモチベーションにも繋がりますので🌸

【次の内容】

zenn.dev




以上の内容はhttps://www.anarchive-beta.com/entry/2022/01/31/180000より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14