計算ノート
2変量正規分布の密度関数 は次式で表される:
ここで は2次元のベクトル.
分散共分散行列の逆行列を と置くと,
と書ける.
密度関数の等高線を描くと(関数を同じ高さのところで輪切りにすると)楕円になる.
この投稿では, 2変量正規分布の密度関数を同じ高さのところで輪切りにする方法を考える.
密度関数のとる値が同じになるのは,
の値が同じになることと同値である(他の部分は定数).
分散共分散行列は実対称行列なので, 直交行列 を用いて,
と対角化できる(直交行列というのは転置行列が逆行列になる行列のこと).
また,
とすると,
である.
と変換すると,
である.
ベクトルの成分を,
として書き下すと,
となり, これは円の方程式である.
このことは円の各点 を で変換してやると楕円になることを意味する.
さて, いま考えているのは確率密度関数の等高線なので, どうせなら適当な確率 にもとづく高さで輪切りにしたい気持ちがある.
変換後の の世界で考えると, は独立な2つの標準正規分布(平均0, 分散1の正規分布)に従う確率変数である.
独立に標準正規分布に従うd個の確率変数をそれぞれ2乗して足し合わせた確率変数の従う分布を, 自由度dのカイ二乗分布という.
カイ二乗分布は統計学でよく使うのでたいていの統計ソフトでは を与える が計算できるようになっている.
の高さで密度関数を輪切りにしたものを確率楕円と呼ぶ.
実装例(Julia)
module Ellipse using Distributions using LinearAlgebra function ellipse2d(μ, Λ, p, n) A = eigen(Λ) P = A.vectors D = diagm(sqrt.(inv.(A.values))) c = quantile(Chisq(2), p) Q = P'*D*sqrt(c) t = 0:(2pi/n):2pi x = zeros(length(t), 2) #z = zeros(length(t)) for i in eachindex(t) x[i,:] = Q*[cos(t[i]), sin(t[i])] + μ #媒介変数表示 #z[i] = (x[i,:]-μ)'*Λ*(x[i,:]-μ) end return x end #μなし(原点中心) function ellipse2d(Λ, p, n) A = eigen(Λ) P = A.vectors D = diagm(sqrt.(inv.(A.values))) c = quantile(Chisq(2), p) Q = P'*D*sqrt(c) t = 0:(2pi/n):2pi x = zeros(length(t), 2) for i in eachindex(t) x[i,:] = Q*[cos(t[i]), sin(t[i])] end return x end function countpoints(Y, x) p = zero(Int) for i in axes(Y,2) #どこかしら曲線より上でかつどこかしら曲線より下 p += any(x[:,1] .< Y[1,i] .&& x[:,2] .< Y[2,i]) && any(Y[1,i] .< x[:,1] .&& Y[2,i] .< x[:,2]) end return p end end
適当に(乱数で)サンプルした2変量正規分布に従う点をプロットしてやると大体楕円におさまることがわかる.
ここでは に0.8を与えているが, 点の数を数えるとおおよそ期待値通りになっている.
julia> (m/20000) 0.79855
using Distributions using Random using LinearAlgebra using Distributions using Plots rng = MersenneTwister(4126) μ = randn(rng, 2) Λ = Symmetric(rand(rng, Wishart(3, [1 0;0 1]))) X = rand(rng, MvNormal(μ, inv(Λ)), 20000) x = Ellipse.ellipse2d(μ, Λ, 0.8, 201) scatter(X[1,:], X[2,:], msw=0, ma=0.01, legend=false, tick_direction=:out) plot!(x[:,1], x[:,2]) # png("./Desktop/scatter.png") m = Ellipse.countpoints(X, x) (m/20000)