library(TMB)
library(ggplot2)
set.seed(123)
Applied Statistical Modelling for Ecologistsで使われているということもありましたので、TMBをためしてみました。
準備
TMBライブラリと、データ可視化用にggplot2を読み込みます。擬似乱数も固定します。
データ
ゼロ過剰ポアソン分布にしたがう模擬データを生成します。
<- 100
N <- 0.3
p <- -1
alpha <- 0.8
beta <- runif(N, 0, 5)
x <- rbinom(100, 1, 1 - p) * rpois(N, exp(alpha + beta * x))
y <- data.frame(Y = y, X = x) example
このようなデータになりました。
ggplot(example, aes(x = X, y = Y)) +
geom_point()
ポアソン回帰
まずはポアソン回帰モデルをあてはめます。
モデル
例を参考にして、以下のようなモデルを書きました。対数尤度×-1を返り値にするみたいです。このあたりはStanでtarget
構文をつかう場合にちょっと似ている気がしました。あとで、Rで対数尤度×-1を最小化します。
TMBのC++のコードです。"models/poisson.cpp"
に保存しておきます。
コンパイルと最適化
モデルをコンパイルして、できたライブラリをロードします。
モデルをコンパイルしなおしても挙動がかわらなくて困ったことがあったのですが、そういうときはいったんライブラリをアンロードするとよいのかもしれません(Rを再起動してました)。
<- "poisson"
model_name file.path("models", paste(model_name, "cpp", sep = ".")) |>
compile()
file.path("models", dynlib(model_name)) |>
dyn.load()
MakeADFun
関数で、最適化関数に渡すオブジェクトを作成して、最適化します。最適化関数にはnlminb
を使用しました。Introductionではoptim
関数を使用していましたので、そちらもつかえるみたいです。
<- list(Y = example$Y, X = example$X)
data <- list(alpha = 1, beta = 1)
parameters <- MakeADFun(data, parameters, DLL = model_name)
obj_pois ## Constructing atomic D_lgamma
<- nlminb(obj_pois$par, obj_pois$fn, obj_pois$gr)
opt_pois ## outer mgc: 29879.53
## outer mgc: 771.6844
## outer mgc: 796.7531
## outer mgc: 183.2513
## outer mgc: 39.24369
## outer mgc: 22.22143
## outer mgc: 3.306709
## outer mgc: 0.8717667
## outer mgc: 0.09010732
## outer mgc: 0.01217738
## outer mgc: 9.899208e-08
結果
結果です。データにあわないモデルなので、推定値はまあそれなりです。
print(opt_pois)
## $par
## alpha beta
## -1.367507 0.815415
##
## $objective
## [1] 247.2812
##
## $convergence
## [1] 0
##
## $iterations
## [1] 10
##
## $evaluations
## function gradient
## 14 11
##
## $message
## [1] "relative convergence (4)"
glm
関数による結果と比較してみます。
<- glm(Y ~ X, family = poisson, data = example)
fit_glm summary(fit_glm)
##
## Call:
## glm(formula = Y ~ X, family = poisson, data = example)
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) -1.36751 0.20423 -6.696 2.14e-11 ***
## X 0.81542 0.05152 15.827 < 2e-16 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for poisson family taken to be 1)
##
## Null deviance: 646.12 on 99 degrees of freedom
## Residual deviance: 300.58 on 98 degrees of freedom
## AIC: 498.56
##
## Number of Fisher Scoring iterations: 5
logLik(fit_glm)
## 'log Lik.' -247.2812 (df=2)
パラメータ・対数尤度とも、同じ値になっていました。
ただし、残差逸脱度が自由度の3倍くらいあるので、やはり過分散になっているようです。
ゼロ過剰ポアソンモデル
つづいて、同じデータをゼロ過剰ポアソンモデルにあてはめます。
モデル
dzipois
という分布関数が用意されていますので、これを使用します。
zipois.cpp
// Zero-Inflated Poisson
#include <TMB.hpp>
template<class Type>
Type objective_function<Type>::operator() ()
{
DATA_VECTOR(Y);
DATA_VECTOR(X);
PARAMETER(p);
PARAMETER(alpha);
PARAMETER(beta);
Type lp = 0;
vector<Type> lambda = exp(alpha + beta * X);
lp += -sum(dzipois(Y, lambda, p, true));
return lp;
}
コンパイルと最適化
モデルをコンパイルして、できたライブラリをロードします。
<- "zipois"
model_name file.path("models", paste(model_name, "cpp", sep = ".")) |>
compile()
file.path("models", dynlib(model_name)) |>
dyn.load()
最適化します。
<- list(Y = example$Y, X = example$X)
data <- list(p = 0.5, alpha = 1, beta = 1)
parameters <- MakeADFun(data, parameters, DLL = model_name)
obj_zip ## Constructing atomic D_lgamma
<- nlminb(obj_zip$par, obj_zip$fn, obj_zip$gr)
opt_zip ## outer mgc: 21261.9
## outer mgc: 939.6188
## Warning in nlminb(obj_zip$par, obj_zip$fn, obj_zip$gr): NA/NaN function
## evaluation
## outer mgc: 873.141
## outer mgc: 364.9433
## outer mgc: 365.5757
## outer mgc: 70.04271
## outer mgc: 69.5802
## Warning in nlminb(obj_zip$par, obj_zip$fn, obj_zip$gr): NA/NaN function
## evaluation
## outer mgc: 80.84804
## outer mgc: 23.68681
## outer mgc: 25.78416
## outer mgc: 92.0502
## outer mgc: 17.66233
## outer mgc: 56.65431
## outer mgc: 10.246
## outer mgc: 2.013517
## outer mgc: 0.09686809
## outer mgc: 0.01954563
## outer mgc: 7.131502e-05
結果
結果です。最適化で警告が出たりしましたが、convergence=0
なので収束したようです。
推定値も、データを生成した元の値に近いものになっていました。
print(opt_zip)
## $par
## p alpha beta
## 0.2509950 -1.1137171 0.8300888
##
## $objective
## [1] 174.6459
##
## $convergence
## [1] 0
##
## $iterations
## [1] 18
##
## $evaluations
## function gradient
## 23 18
##
## $message
## [1] "relative convergence (4)"