関数解読normpostsim

Bayesian Computation with R: Second Edition (Use R!)

データ分布
y~N(mu,sigma2)
事前分布
p(mu,sigma2)proportional to (sigma2)^(-1)
の場合の(mu,sigma2)の事後分布からのサンプリング。
この場合,解析的な結果を得ることができるので,
サンプリングしなくても良い。

使用法

normpostsim(data,m=1000)

引数
data:観測値ベクトル
m: 発生させるサンプル数(デフォールト1000)

mu: シミュレートされた正規分布の平均のベクトル(m次元)
sigma2: シミュレートされた正規分布の分散のベクトル(m次元)


参考
Bayesian Data Analysis, Second Edition (Chapman & Hall/CRC Texts in Statistical Science)

#normpostsimの中身
 normpostsim = function (data, m = 1000)
{
    S = sum((data - mean(data))^2)
     #dataからその平均を引いて,2乗和を計算。Sに代入。
    xbar = mean(data)
     # dataの平均をxbarに代入。
    n = length(data)
     # dataの長さ(要素数)をnに代入。
    sigma2 = S/rchisq(m, n - 1)
     # Bayesian Data Analysis(BDA) p75の数式(3.5)と本質的には同じ。
     # 参考 BDA p574-575の一覧表。

    mu = rnorm(m, mean = xbar, sd = sqrt(sigma2)/sqrt(n))
     # BDA p75の数式(3.3)

    return(list(mu = mu, sigma2 = sigma2))
     # muとsigma2をリスト形式で保存して,出力。
}


#データ
#データの読み込み
# 自家受精させた植物と交雑受精させた植物の高さの差のデータ
# 出所:Fisher, R. (1960), Statistical Methods for Research Workers
data(darwin)

#dataにdarwin$differenceを利用
#mは未指定(デフォールトm=1000をそのまま使う)
#結果をsに代入
s=normpostsim(darwin$difference)

#平均のヒストグラムを描画
hist(s$mu)
#分散のヒストグラムを描画
hist(s$sigma2)

関数解読regroup

Bayesian Computation with R: Second Edition (Use R!)

指定した整数値ごとに行列の行を集計して新しい行列を作成する。
使用法

regroup(data,g)

引数
data:行列
g:1からdataの行数nの間の正整数


gごとに行列dataの行を集計した新しい行列
行列dataの行数がgで割り切れなかった場合は,
行列dataの最後尾の余った行(g-1)がその前のg行といっしょにまとめられる(はず)。




#regroupの中身
regroup = function (data, g)
{

    d = dim(data)
    n = d[1] #行数
    m = d[2] #列数
    N = floor(n/g)#行数をgで割り,小数点以下を切り捨てる。
    dataG = array(0, c(N, m))#N行m列の配列を作る。

    k = 0 #dataGの行添字kの初期値を設定。

    for (j in seq(1, (N - 1) * g + 1, g)) {
              #行列dataの行を(g+1)行おきに取り出す。
              # 例 g=3 なら1行目の次は4行目
              # seq(1, N * g, g) とすると問題が起こる?
        k = k + 1 # 1だけ増加。

        
        for (i in 0:(g - 1)){
         dataG[k, ] = dataG[k,] + data[j+i,]
          # dataGのk行目とdataのj+i行目を改めて
          # dataGのk行目に代入。
          # i=0,..,(g-1)まで繰り返す。
          # 例 j=1でg=3ならi=0,1,2
          #  dataの1〜3行目を合計することになる。
          # apply(data[j:j+(g-1),2,],2,sum)
          #  を使えば,内側のforは不要かな。
         }
    }

    if (n > N * g) {# nがgで割り切れなかった場合
        for (i in (N * g + 1):n){
         dataG[N, ] = dataG[N, ] + data[i,]
         #dataのN*g+1行目からn行目までを
         #dataGのN行目に合計して代入。
         # apply(data[N * g + 1:n],2,sum)が使えそう。
         }
    }
    return(dataG)#dataGを出力。
}


#1から20までの整数を4行5列の行列とする。
data=matrix(c(1:20),nrow=4,ncol=5)
data
#まとめる行の数を設定。
g=2
#dataを2行ごとまとめて出力。
regroup(data,2)

#1から49までの整数を5行5列の行列とする。
data=matrix(c(1:49),nrow=7,ncol=7)
data
#まとめる行の数を設定。
g=2
#dataを2行ごとまとめて出力。
#dataの行数がg=2で割り切れないので
#最後の3行(5行目から7行目)がまとめられている。
regroup(data,2)

######################
#ジーター選手の2004年の154ゲームの打撃成績
data(jeter2004)
attach(jeter2004)

#ヒット数と打席数?をまとめてdataに代入。
data=cbind(H,AB)
#1試合ごとの(H,AB)を5試合ごとに集計してdata1に代入。
#data1の最終行(30行)は9試合(146〜154試合)の集計データ
data1=regroup(data,5)

関数解読simcontour

Bayesian Computation with R: Second Edition (Use R!)

グリッド上で定義された一般的2変量密度関数より乱数を発生させる

使用法

simcontour(logf,limits,data,m)

引数
logf:適用する対数密度を定義した関数
limits:グラフを描く領域c(xlo,xhi,ylo,yhi)を指定
 xlo:x軸の最小値
xhi:x軸の最大値
ylo:x軸の最小値
yhi:x軸の最大値
data: 適用する対数密度のパラメータ
m:発生させる乱数の数



#simcontourの中身
simcontour = function (logf, limits, data, m)
{
    #simcontourの中でのみ使う関数LOGFを定義
    # mycontourの場合と同じ
    LOGF = function(theta, data) {
        if (is.matrix(theta) == TRUE) {
        #thetaが行列ならば,
            val = matrix(0, c(dim(theta)[1], 1))
            # ゼロ行列valを作成
            #  行数:thetaの行数
            #  列数:1
            for (j in 1:dim(theta)[1]){
             val[j] = logf(theta[j,], data)
             }#thetaのj行とlogfのパラメータdata
             # を使って,対数密度logfを計算し,valのj行に代入
            #for文を使わずに書けないか。
        }
        else val = logf(theta, data)
          #thetaが行列でないならば,
          # thetaとlogfのパラメータdata
          # を使って,対数密度logfを計算し,valに代入
        return(val)
          #valの値を出力する。
    }

    # (x,y)平面の与えた最小から最大の範囲を49等分
    ng = 50
    x0 = seq(limits[1], limits[2], len = ng)
     #最小値がxlo=limits[1],最大値がxhi=limits[2]
     #長さがngの等差数列をx0に代入。
     #この数列はxloとxhiの間をng-1等分する。
    y0 = seq(limits[3], limits[4], len = ng)
     #最小値がylo=limits[3],最大値がyhi=limits[4]
     #長さがngの等差数列をy0に代入。
     #この数列はyloとyhiの間をng-1等分する。
    X = outer(x0, rep(1, ng))
      #x0とrep(1, ng)(全要素が1の長さngのベクトル)
      # の外積をXに代入。x軸用のグリッドの準備。
    Y = outer(rep(1, ng), y0)
      #rep(1, ng)とx0
      # の外積をYに代入。y軸用のグリッドの準備。

    n2 = ng^2
    Z = LOGF(cbind(X[1:n2], Y[1:n2]), data)
    #thetaが行列でないなら,点thetaにおけるlogfの値をZに出力。
    #thetaが行列なら,thetaの各行で表される点におけるlogfの値を
    # n2行1列の行列として
    #  Zに出力。

    Z = Z - max(Z)
     #ZからZの要素の最大値を引いて,新たにZに代入。
     # 対数密度の頂点(モード)の高さを0に基準化。

    Z = matrix(Z, c(ng, ng))
     #再び、Zをng×ngの行列に変換。
###ここまで関数mycontourと全く同じ。

    d = cbind(X[1:n2], Y[1:n2], Z[1:n2])
    #  列ベクトルX[1:n2],Y[1:n2],Z[1:n2]束ねて行列を作成。
    #  各行は領域内のグリッドの座標に対応。

    dx = diff(x0[1:2])
     #x軸のグリッドの間隔(x0の第1要素と第2要素の差)をdxに代入。
    dy = diff(y0[1:2])
     #x軸のグリッドの間隔(y0の第1要素と第2要素の差)をdyに代入。

    prob = d[, 3]     #dの3列目(対数密度の高さ)をprobに代入。
    prob = exp(prob) #対数を元の数値に戻してprobに代入。
    prob = prob/sum(prob)# 高さの相対比率をprobに代入。
                        # 結果として,sum(prob)=1となる。

    i = sample(2500, m, replace = TRUE, prob = prob)
    #グリッド上の各点(50×50=2500個)に確率が定義されている。
    # それぞれの点を識別する添字(1〜2500)を定義された確率に従って
    # m回復元抽出する。

    return(list(x = d[i,1] + runif(m) * dx - dx/2,
                y = d[i,2] + runif(m) * dy - dy/2
                ))
        #確率(頻度)に応じた添字iをdの行を表す添字として使う。
        #グリッドを中心に半径dx(dy)の範囲で乱数をさらに散らすために
        # 第2項 runif(m) * dx - dx/2やrunif(m) * dy - dy/2 を追加。

}

#平均ベクトルの指定
m=array(c(0,0),c(2,1))
#分散共分散行列の指定
v=array(c(1,.6,.6,1),c(2,2))
#上記の2つをリストにまとめる
normpar=list(m=m,v=v)

#結果を再現するために(疑似)乱数の種を設定。
set.seed(123)
#関数simcontourに従って,乱数の組を500個作成してsに代入。
s=simcontour(lbinorm,c(-4,4,-4,4),normpar,500)
#sを散布図で表示。
plot(s$x,s$y)
#関数mycontourを使って等高線を追加。
mycontour(lbinorm,c(-4,4,-4,4),normpar,add=T)

関数解読mycontour

Bayesian Computation with R: Second Edition (Use R!)
2変量密度関数の等高線の描画

一般的な2変量密度関数用の等高線グラフ
 実際には,対数密度関数を使ってグラフを描く。
 密度関数の定数部分は除いても描ける。
モード(最頻値)での密度関数の高さを基準にして
その高さの10%,1%,0.1%の点を結んで等高線を描く。
別のより一般的な関数contourを活用する。


使用法

mycontour(logf,limits,data,...)

引数
logf:適用する対数密度を定義した関数
limits:グラフを描く領域c(xlo,xhi,ylo,yhi)を指定
 xlo:x軸の最小値
 xhi:x軸の最大値
 ylo:x軸の最小値
 yhi:x軸の最大値

data: 適用する対数密度のパラメータ
... 関数contourに関するさらなる引数




密度関数の等高線グラフの描画

#mycontourの中身

mycontour = function (logf, limits, data, ...)
{
    #mycontourの中でのみ使う関数LOGFを定義
    LOGF = function(theta, data) {
        if (is.matrix(theta) == TRUE) {
          #thetaが行列ならば,
            val = matrix(0, c(dim(theta)[1], 1))
            # ゼロ行列valを作成
            #  行数:thetaの行数
            #  列数:1

            for (j in 1:dim(theta)[1]){
             val[j] = logf(theta[j,], data)
            }#thetaのj行とlogfのパラメータdata
             # を使って,対数密度logfを計算し,valのj行に代入
            #for文を使わずに書けないか。
        }
        else val = logf(theta, data)
          #thetaが行列でないならば,
          # thetaとlogfのパラメータdata
          # を使って,対数密度logfを計算し,valに代入
        return(val)
          #valの値を出力する。

    }
    # (x,y)平面の与えた最小から最大の範囲を49等分
    ng = 50
    x0 = seq(limits[1], limits[2], len = ng)
     #最小値がxlo=limits[1],最大値がxhi=limits[2]
     #長さがngの等差数列をx0に代入。
     #この数列はxloとxhiの間をng-1等分する。
    y0 = seq(limits[3], limits[4], len = ng)
     #最小値がylo=limits[3],最大値がyhi=limits[4]
     #長さがngの等差数列をy0に代入。
     #この数列はyloとyhiの間をng-1等分する。
    X = outer(x0, rep(1, ng))
      #x0とrep(1, ng)(全要素が1の長さngのベクトル)
      # の外積をXに代入。x軸用のグリッドの準備。
    Y = outer(rep(1, ng), y0)
      #rep(1, ng)とx0
      # の外積をYに代入。y軸用のグリッドの準備。

    n2 = ng^2
    Z = LOGF(cbind(X[1:n2], Y[1:n2]), data)
    #thetaが行列でないなら,点thetaにおけるlogfの値をZに出力。
    #thetaが行列なら,thetaの各行で表される点におけるlogfの値を
    # n2行1列の行列として
    #  Zに出力。

    #Xはベクトルではないが,X[1:n2]はベクトル。
    # 不思議。
    # X[1:n2]は行列Xの各列をつなぎ合わせたn2次元のベクトル。
    # cbind(X[1:n2], Y[1:n2])
    #  列ベクトルX[1:n2]とY[1:n2]束ねて行列を作成。
    #   各行は領域内のグリッドの座標に対応。
    #上で定義した関数LOGFを利用。

    Z = Z - max(Z)
     #ZからZの要素の最大値を引いて,新たにZに代入。
     # 対数密度の頂点(モード)の高さを0に基準化。


    Z = matrix(Z, c(ng, ng))
     #再び、Zをng×ngの行列に変換。

    contour(x0, y0, Z,
       levels = seq(-6.9, 0, by = 2.3),lwd = 2, ...)
     #関数contour:等高線を描く汎用関数。
     # levels Zの値が-6.9,-4.6,-2.3,0の点を等高線で結ぶ。
     # exp(0)=1
     # exp(-2.3)=約0.1
     # exp(-4.6)=約0.01
     # exp(-6.9)=約0.001

     # lwd:線の幅の指定。デフォールトは1。
     ## (ついでに)lty:線のタイプ
     ## 0=blank, 1=solid (default), 2=dashed,
     ## 3=dotted, 4=dotdash, 5=longdash, 6=twodash
     
}


関数lbinormを使って等高線を描画

#平均ベクトルの指定
m=array(c(0,0),c(2,1))
#分散共分散行列の指定
v=array(c(1,.6,.6,1),c(2,2))
#上記の2つをリストにまとめる
normpar=list(m=m,v=v)

#等高線の描画
mycontour(lbinorm,c(-4,4,-4,4),normpar)
#x軸y軸を追加
abline(v=0,h=0)

#分散共分散行列のみ変更(相関係数=0)
v1=array(c(1,0,0,1),c(2,2))
normpar=list(m=m,v=v1)
mycontour(lbinorm,c(-4,4,-4,4),normpar)
abline(v=0,h=0)


#分散共分散行列のみ変更(相関係数=0.9)
v2=array(c(1,.9,.9,1),c(2,2))
normpar=list(m=m,v=v2)
mycontour(lbinorm,c(-4,4,-4,4),normpar)
abline(v=0,h=0)

関数解読lbinorm

Bayesian Computation with R: Second Edition (Use R!)
2変量正規分布の密度関数の自然対数の計算
定数部分を除くカーネルの自然対数


使用法
lbinorm(xy,par)

引数
xy: 密度関数の対数を評価する点(x,y)
par:平均ベクトルmと分散共分散行列vをリスト形式にまとめたもの



対数密度のカーネルの値

#lbinormの中身
lbinorm=function (xy, par)
{
    m = par$m
     #リストparからmの部分を取り出し,mに代入。
    v = par$v
     #リストparからvの部分を取り出し,vに代入。
    x = xy[1]
     #xyの第1要素をxに代入。
    y = xy[2]
     #xyの第2要素をyに代入。
    zx = (x - m[1])/sqrt(v[1, 1])
    zy = (y - m[2])/sqrt(v[2, 2])
     #xとyを標準化してzxとzyにそれぞれ代入。

    r = v[1, 2]/sqrt(v[1, 1] * v[2, 2])
     #分散共分散行列vの要素を使って相関係数rを計算。
    return(-0.5/(1 - r^2) * (zx^2 - 2 * r * zx * zy + zy^2))
     #2変量正規密度の対数(定数項を除く)を出力。

}

#平均ベクトル
mean=c(0,0)
#分散共分散行列
#対角項が1の対角行列
varcov=diag(c(1,1))
#対数密度を評価する点
value=c(1,1)
#平均ベクトルと分散共分散行列をリスト形式で保存
param=list(m=mean,v=varcov)
lbinorm(value,param)

関数解読rtruncated

Bayesian Computation with R: Second Edition (Use R!)

切断された確率分布からのサンプリング

積分布関数と(分位数を出力する)逆累積分布関数(分位数)
が利用可能な場合に適用できる。
Rの場合,例えば正規分布なら
積分布関数はpnorm
分位数を出力する関数はqnorm

(x <- pnorm(1,mean=1,sd=2))
qnorm(x,mean=1,sd=2)


使用法
rtruncated(n,lo,hi,pf,qf,...)
積分布関数がpfで分位数を出力する関数がqfの確率分布において
lo <=X <= hi
の範囲からn個のサンプルを出力する。

引数
n:発生させる乱数の数
lo:下限の切断点
hi:上限の切断点
pf:(切断前の)累積分布関数
qf:(切断前の)逆累積分布関数
...:pfやqfで使われる引数


この関数に適用する切断された確率分布からのn回分のサンプリング結果

#rtruncatedの中身
#関数の引数に関数を使える。
#runif(n)はn>=2の場合,ベクトルで
#pf(lo)やpf(hi)はスカラーだから,
#ベクトルのrunifにあわせて(各要素に対して),
#スカラーのpf(lo)やpf(hi)が繰り返し計算に使われる。

rtruncated=function (n, lo, hi, pf, qf, ...){
qf(pf(lo, ...) + runif(n) * (pf(hi, ...) - pf(lo, ...)), ...)
}

メモ

切断された新しい累積分布関数のX=xにおける値は
(pf(x)-pf(lo))/(pf(hi)-pf(lo))
である。これをF(x)とおいて
一様分布U[0,1]に従う確率変数をuとすると

Pr(u<=F(x))=F(x)

が成立する。

u=F(x)は

x = pf^(-1)(pf(lo)+u*((pf(hi)-pf(lo))))

と式変形できる。
ここで,pf^(-1)()はpf()の逆関数だからqf()に等しい。

結局,

x = qf(pf(lo)+u*((pf(hi)-pf(lo))))

を得る。

http://en.wikipedia.org/wiki/Inverse_transform_sampling

# 平均2,標準偏差1の正規分布で3以上の範囲から10個サンプリングする。

n=10
lo=3   # 下限には-Infを使える
hi=Inf # 上限にはInfを使える。
rtruncated(n,lo,hi,pnorm,qnorm,mean=2,sd=1)
# ベータ分布beta(2, 5)の0.3<=X<=0.8の範囲から20個サンプリングする。

n=20
lo=0.3
hi=0.8
rtruncated(n,lo,hi,pbeta,qbeta,2,5)

関数解読rigamma

Bayesian Computation with R: Second Edition (Use R!)

逆ガンマ分布IG(a,b)からのサンプリング
ガンマ分布G(a,b)に従う確率変数Xの逆数Y=1/Xは
(定義上)逆ガンマ分布に従うという事実を直接用いる。

使用法

rigamma(n, a, b)

引数

  • n: 発生させる乱数の数
  • a(>0): 逆ガンマ分布のshapeパラメータ(訳がわからない)。
  • b(>0): 逆ガンマ分布のrateパラメータ(訳がわからない)。

確認

X〜G(a,b)のとき,
E(X)=a*b
Var(X)=a*(b)^2
(a>0,b>0ならばE(X)とVar(X)は存在する)

Y〜IG(a,b)のとき,
E(Y)=1/((a-1)*b)
Var(Y)=1/((a-1)^2*(a-2)*b^2)
(a>0,b>0ならばYをサンプリングすることはできるが
E(X)が存在するのはa>1の場合
Var(X)は存在するのはa>2の場合)

http://en.wikipedia.org/wiki/Gamma_distribution
http://en.wikipedia.org/wiki/Inverse-gamma_distribution
(自分への)注意
Wikipediaの逆ガンマ分布の説明でのパラメータbetaはここでの1/bに対応。



逆ガンマ分布IG(a,b)からのn回分のサンプリング結果


#rigammaの中身
rigamma=function (n, a, b)
{
    return(1/rgamma(n, shape = a, rate = b))
    # G(a,b)からn回サンプリングして,
    #各値の逆数を計算して出力。
}


a=10
b=5
n=20
ig = rigamma(n,a,b)
mean(ig)
var(ig)
#nを増やすと期待値分散の理論値に近づく。
#期待値,分散が存在しないパラメータ設定で
# mean(ig)やvar(ig)を計算すると,nを増やすほど
# これらの値は大きくなる(発散する)。