.Giới thiệu

Trong thực tế, chúng ta hay gặp phải dữ liệu ở dạng số đếm (count data) như đếm số hoa ,quả, hạt trên cây trong 1 mùa vụ; số sản phẩm bán ra trong 1 ngày; số ngày nằm viện., etc. Đặc điểm cần lưu ý với count data là phân bố dữ liệu thường hay bị lệch phải, phương sai tăng khi giá trị trung bình tăng. Như vậy các phương pháp xử lý số liệu có giả định phân phối chuẩn (t-test, ANOVA, OLS regression, etc) khi áp dụng với dữ liệu count data sẽ không còn phù hợp nữa (trừ khi dữ liệu đã được chuyển đổi). Khi gặp dữ liệu dạng này chúng ta có thể có 2 lựa chọn: 1) chuyển đổi dự liệu về phân phối chuẩn; hoặc 2) thay đổi phương pháp xử lý (phi tham số, hồi quy tuyến tính hỗn hợp tổng quát- Generalized linear [mixed] model).

Ở bài hướng dẫn này chúng ta sẽ làm quen với một phương pháp khá phổ biến trong họ gia đình GLMM là Mixed negative binomial regression (tạm dịch; hồi quy nhị thức âm hỗn hợp) dùng để thay thế hồi quy OLS trong trường hợp biến phụ thuộc có dạng số đếm. Chúng tôi sẽ không đi sâu vào các công thức toán mà sẽ chú trọng giúp cho các bạn biết khi nào nên sử dụng mô hình negative binomial (NB), thực hành trên R, Stan theo trường phái Bayes và cách diễn giải kết quả.

Khi các bạn muốn xem xét ảnh hưởng của các biến độc lập \(\ x_{1}, x_{2},..x_{n}\) lên biến phụ thuộc dạng số đếm \(y\) thì chúng ta thường hay nghĩ ngay đến mô hình hồi quy Poisson. Mô hình Poisson có một giả định (assumption) rất quan trọng là: giá trị mean và variance bằng nhau. Tuy nhiên dữ liệu thực tế mà chúng ta thu thập ít khi thõa mãn giả định này. Khi variance > mean chúng ta gọi đó là over-dispersion (tạm dịch: quá phân tán), ngược lại, variance < mean thì gọi là under-dispersion .Khi dữ liệu gặp vấn đề over-dispersion thì đó chính là lúc chúng ta dùng mô hình negative binomial. Vậy làm cách nào để kiểm tra overdispersion ? Để trả lời câu hỏi đó, chúng ta hãy cùng thực hành với bộ dữ liệu “epil”.

Bộ dữ liệu này được thu thập từ 59 bệnh nhân bị động kinh. Các bệnh nhân được theo dõi số lần bị động kinh trong vòng 8 tuần (base). Sau đó bệnh nhân được phân chia ngẫu nhiên vào nhóm điều trị (treatment- dùng thuốc progabide ) và nhóm giả dược (placebo); Số lần bị động kinh được ghi nhận trong 2 tuần ở 8 tuần tiếp theo. Như vậy mỗi bệnh nhân sẽ được ghi nhận ở 4 thời điểm (period) và dạng dữ liệu này được gọi là dữ liệu tái đo lường (repeated measurement data). Ở bài thực hành này chúng ta sẽ sử dụng một mô hình hồi quy NB với random intercept để xem xét ảnh hưởng của biến lbase, điều trị (trt) và biến visit (được chuyển đổi từ biến period) với số lần bị động kinh trong thời gian theo dõi.

dat=read.csv("https://raw.github.com/vincentarelbundock/Rdatasets/master/csv/MASS/epil.csv")
names(dat)[2] <- "sei"
dat$visit <- (2*dat$period-5)/10
head(dat, 5) %>% knitr:: kable()
X sei trt base age V4 subject period lbase lage visit
1 5 placebo 11 31 0 1 1 -0.7563538 0.1142037 -0.3
2 3 placebo 11 31 0 1 2 -0.7563538 0.1142037 -0.1
3 3 placebo 11 31 0 1 3 -0.7563538 0.1142037 0.1
4 3 placebo 11 31 1 1 4 -0.7563538 0.1142037 0.3
5 3 placebo 11 30 0 2 1 -0.7563538 0.0814139 -0.3

.Mô tả dữ liệu

box <-  dat %>% ggplot(., aes(x = trt, y = sei)) +
        geom_boxplot(aes(fill = as.factor(period)), alpha = 0.7,outlier.colour = "blue") +
        theme_bw() + labs(x = "treatment", y = "Count") +
        scale_fill_discrete(name = "Period")+
        theme(axis.text = element_text(size = 12, color = "black"),
              axis.title = element_text(size = 15, color = "black"))
        
box

#

Từ biểu đồ boxplot chúng ta thấy rằng một số bệnh nhân có số lần bị động kinh rất cao và hiện tượng này xuất hiện ở cả 2 nhóm dùng thuốc progabide và placebo.

Kiểm tra phân bố của lbase.

tes.base <- dat %>% split(.$trt) %>% map(~ fBasics::shapiroTest(.$lbase))
tes.base
## $placebo
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9452
##   P VALUE:
##     0.0001702 
## 
## Description:
##  Mon Nov 06 00:45:25 2017 by user: Tien Tai
## 
## 
## $progabide
## 
## Title:
##  Shapiro - Wilk Normality Test
## 
## Test Results:
##   STATISTIC:
##     W: 0.9705
##   P VALUE:
##     0.00813 
## 
## Description:
##  Mon Nov 06 00:45:25 2017 by user: Tien Tai
wilcox.test(dat$lbase ~ dat$trt)
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  dat$lbase by dat$trt
## W = 6432, p-value = 0.3284
## alternative hypothesis: true location shift is not equal to 0
dens.base <- dat %>% ggplot(., aes(x =lbase, color = trt)) + geom_line(stat = "density", size = 0.9) +
        labs(x = "lbase", y = "Density") + theme_bw() + scale_color_discrete(name = "Treatment") + 
        theme(axis.text = element_text(size = 12, color = "black"),
        axis.title = element_text(size = 15, color = "black")) +
        theme(panel.grid.minor = element_blank())
dens.base

.Hồi quy poisson và negative binomial

Hồi quy poisson là mô hình được sử dụng phổ biến cho count data. Số sự kiện (số hoa quả, số sản phẩm, etc) phải được đếm trong một đơn vị thời gian, không gian xác định (1 vụ mùa, 1 ngày, một ô đo đếm, etc) và các sự kiện đó phải độc lập với nhau.

Một biến ngẫu nhiên \(y\) với giá trị kì vọng \(\mu\) tuân theo phân bố poisson sẽ có hàm probability mass function (hàm khối xác suất) như sau:

\[P\left ( Y = y \right ) = \frac{e^{-\mu }\mu^y}{y!} \] Có thể nhận ra rằng phân phối poisson chỉ có 1 tham số là mean \(\mu\). Trong hồi quy poisson, mean \(\mu\) được mô tả bằng một hàm số mũ của các biến độc lập tuyến tính (linear predictor). Link function được sử dụng trong hồi quy poisson là log link. \[\mu = e^\eta\] \[\eta = \beta_0 + \beta_1 x_1 + \beta_2 x_2 + ...+ \beta_n x_n\]

Đầu tiên chúng ta sẽ dùng mô hình poisson không có random effect để kiểm tra độ phân tán của dữ liệu (dispersion).

glm.pois <-  glm(sei ~ lbase + trt + visit , family = "poisson", data = dat) # fitting mô hình
summary(glm.pois)
## 
## Call:
## glm(formula = sei ~ lbase + trt + visit, family = "poisson", 
##     data = dat)
## 
## Deviance Residuals: 
##     Min       1Q   Median       3Q      Max  
## -5.4185  -1.4334  -0.2444   0.7166  10.9926  
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)   1.76538    0.03823  46.174  < 2e-16 ***
## lbase         1.17646    0.03086  38.121  < 2e-16 ***
## trtprogabide -0.10325    0.04532  -2.278  0.02272 *  
## visit        -0.29598    0.10148  -2.917  0.00354 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## (Dispersion parameter for poisson family taken to be 1)
## 
##     Null deviance: 2517.83  on 235  degrees of freedom
## Residual deviance:  973.81  on 232  degrees of freedom
## AIC: 1747.7
## 
## Number of Fisher Scoring iterations: 5
library(COUNT)
## Warning: package 'COUNT' was built under R version 3.3.3
## Loading required package: msme
## Warning: package 'msme' was built under R version 3.3.3
## Loading required package: MASS
## 
## Attaching package: 'MASS'
## The following object is masked from 'package:dplyr':
## 
##     select
## Loading required package: lattice
## Loading required package: sandwich
P__disp(glm.pois)
## pearson.chi2   dispersion 
##  1120.845606     4.831231

Hàm *P__disp* cung cấp cho chúng ta Pearson \(\chi ^{2}\) statistic và Pearson dispersion statistic. Có thể tính thủ công Pearson dispersion statistic bằng cách lấy tổng bình phương của phần dư Pearson chia cho bậc tự do của mô hình (N quan sát - số biến trong mô hình bao gồm intercept).

poi.df <- df.residual(glm.pois) # 232
Pear.disp.sta <- sum(resid(glm.pois, type = "pearson")^2)/poi.df
Pear.disp.sta
## [1] 4.831231

Giá trị Pearson dispersion statistic > 1, có nghĩa là dữ liệu gặp vấn đề overdispersion. Xin có một lưu ý với các bạn là khi dữ liệu bị overdispersion, một giải pháp thay thế cho mô hình poisson là mô hình quasi-poisson.

glm.quasi <-  glm(sei ~ lbase + trt + visit , family = "quasipoisson", data = dat)
summary(glm.quasi) %>% coef()
##                Estimate Std. Error   t value     Pr(>|t|)
## (Intercept)   1.7653831 0.08403771 21.007035 1.362195e-55
## lbase         1.1764626 0.06783359 17.343363 9.064533e-44
## trtprogabide -0.1032456 0.09961567 -1.036439 3.010764e-01
## visit        -0.2959814 0.22304668 -1.326993 1.858156e-01

Hệ số hồi quy của mô hình glm.poi và glm.quasi là giống nhau, standard error (SE) của các hệ số hồi quy ở mô hình glm.pois lớn hơn so với SE ở mô hình glm.pois.

Chúng ta có thể tính toán sự sai khác SE giữa 2 mô hình dựa vào Pearson dispersion statistic. Khi nhân SE của các hệ số hồi quy ở mô hình poisson với căn bậc hai của Pearson dispersion statistic chúng ta sẽ có được SE tương ứng trong mô hình quasi-poisson.

se.quasi <- summary(glm.quasi) %>% coef(. )%>% apply(., 1, "[", 2)  
se.poi <- summary(glm.pois) %>% coef(.) %>% apply(., 1, "[", 2)  
se.trans <- se.poi * sqrt(Pear.disp.sta) 
se.both = data.frame(se.poi, se.trans,  se.quasi) %>% round(., 5)
se.both 
##               se.poi se.trans se.quasi
## (Intercept)  0.03823  0.08404  0.08404
## lbase        0.03086  0.06783  0.06783
## trtprogabide 0.04532  0.09962  0.09962
## visit        0.10148  0.22305  0.22305

Mixed negative binomial.

Mô hình NB có một số dạng riêng biệt, và ở phần này chúng ta sẽ tập trung dạng phổ biến của nó: quadratic negative binomial. Mô hình NB này có mean và variance như sau: \[\ mean = \mu\] \[variance = \mu + \alpha \mu^2\] Từ quadratic nhằm chỉ số mũ của mean \(\mu\) trong công thức tính variance. Mô hình negative binomial còn được gọi mô hình poisson-gamma distribution mixture model. Với biến \(y\) tuân theo phân phối NB thì chúng ta cần lưu ý những điểm sau:

Ở đây \(v\) được gọi là shape parameter trong phân phối gamma và nó cũng là indirect dispersion parameter trong phân phối NB; \(\alpha\)direct dispersion parameter. Như vậy đối với mô hình NB, có 2 tham số cần được ước tính là mean \(\mu\) và dispersion parameter (\(v\) hoặc \(\alpha\)) . Lưu ý răng khi sử dụng phần mềm R, thì \(v\) được gọi bằng tên thay thế là \(\theta\) ở trong phần kết quả của mô hình. Một số phần mềm khác như Stata, SAS không tính toán \(\theta\) (hay \(v\)) mà sẽ tính \(\alpha\) và gọi đó là dispersion parameter trong phần kết quả. Khi \(\theta \mapsto \ 0\), thì \(\alpha\rightarrow \infty\), và điều này cho thấy dữ liệu bị phân tán nghiêm trọng.

Mô hình mixed NB với random effect theo trường phái frequentist có thể được fit với package gamlss hoặc glmmADMB. Để đơn giản về mặt cú pháp, chúng ta sẽ dùng package glmmADMB. Với bộ dữ liệu epil, chúng ta sẽ xem xét ảnh hưởng của trt, lbase và visit lên số lần bị động kinh với random intercept cho từng bệnh nhân. Mô hình mixed NB với random intercept chúng ta cần ước tính có dạng: \[\mu_{ij} = e^\left ( \beta_{0} + \beta_{trt} x_{trt} + \beta_{lbase} x_{lbase} + \beta_{visit} x_{visit} + sub_i + \varepsilon_{ij} \right )\] \(sub_i\) thể hiện giá trị random intercept cho bệnh nhân thứ \(i^{th}\); \(e^{\varepsilon_{ij}}\) tuân theo phân bố gamma, có mean = 1 và variance là giá trị direct dispersion parameter \(\alpha\).

library(glmmADMB)
## 
## Attaching package: 'glmmADMB'
## The following object is masked from 'package:MASS':
## 
##     stepAIC
## The following object is masked from 'package:stats':
## 
##     step
dat$subject <-  as.factor(dat$subject)
nb.admb <-  glmmadmb(sei ~ trt+lbase+visit + (1|subject), zeroInflation = FALSE, family = "nbinom" , data = dat )
summary(nb.admb)
## 
## Call:
## glmmadmb(formula = sei ~ trt + lbase + visit + (1 | subject), 
##     data = dat, family = "nbinom", zeroInflation = FALSE)
## 
## AIC: 1264.7 
## 
## Coefficients:
##              Estimate Std. Error z value Pr(>|z|)    
## (Intercept)     1.818      0.108   16.83   <2e-16 ***
## trtprogabide   -0.334      0.151   -2.21    0.027 *  
## lbase           1.011      0.101   10.03   <2e-16 ***
## visit          -0.273      0.167   -1.64    0.101    
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Number of observations: total=236, subject=59 
## Random effect variance(s):
## Warning in .local(x, sigma, ...): 'sigma' and 'rdig' arguments are present
## for compatibility only: ignored
## Group=subject
##             Variance StdDev
## (Intercept)   0.2394 0.4893
## 
## Negative binomial dispersion parameter: 7.4574 (std. err.: 1.7549)
## 
## Log-likelihood: -626.353

Tính incidence rate ratio (IRR) và khoảng tin cậy 95%.

IRR <-  cbind(est.IRR =  coef(nb.admb)  %>% exp(.),
              CI95 = confint(nb.admb) %>% exp(.)) %>% round(., 3)
IRR
##              est.IRR 2.5 % 97.5 %
## (Intercept)    6.159 4.984  7.612
## trtprogabide   0.716 0.533  0.963
## lbase          2.748 2.255  3.348
## visit          0.761 0.549  1.055

Trt và lbase ảnh hưởng có ý nghĩa thống kê lên số lần bị động kinh của các bệnh nhân trong thời gian theo dõi. Số lần bị động kinh ở bệnh nhân được điều trị bằng thuốc progabide giảm khoảng 28% so với các bệnh nhân dùng giả dược. Mô hình cho ta biết dispersion parameter, ở đây là \(\theta\) bằng 7.457. Variance của random intercept không quá lớn, điều này cho thấy có ít sự khác biệt giữa các bệnh nhân.

library(viridis)
new.df <-  data.frame(lbase = rep(seq(min(dat$lbase),max(dat$lbase), length.out =  50), times = 2), trt = rep(c(0,1), each = 50),visit = rep(mean(dat$visit), 100))
pred.count <-  predict(nb.admb, newdata = new.df, type = "link", se.fit = TRUE ) %>% map_df(~exp(.x))
new.df2 <- cbind(new.df, pred.count)
#
g.count <- ggplot(new.df2, aes(x = lbase, y = fit, color = as.factor(trt))) + geom_line(size = 1) +
  geom_ribbon( aes(ymin  = fit - 1.96*se.fit, ymax = fit + 1.96*se.fit, fill = as.factor(trt)), alpha = 0.6, color = "white") +
  labs(x = "lbase", y ="Predicted count")  +
  scale_fill_viridis(name = "Treatment", labels =c("Placebo","Progabide"),discrete=T,option="D", direction = 1) +
  scale_color_viridis(discrete= T,option="D", direction = 1) +
  guides(color =FALSE) + theme_bw() +
  theme(axis.title = element_text(size = 15, color = "black"), axis.text = element_text(size = 14, color = "black")) +
  theme(panel.grid.major = element_blank())
         
g.count

.Mixed negative binomial với trường phái Bayes

Đầu tiên chúng ta sẽ sử dụng package brms cho mô hình mixed NB, với prior được xác định tự động.

library(brms)
## Loading required package: Rcpp
## Warning: package 'Rcpp' was built under R version 3.3.3
## Loading 'brms' package (version 1.7.0.9000). Useful instructions 
## can be found by typing help('brms'). A more detailed introduction 
## to the package is available through vignette('brms_overview').
## 
## Attaching package: 'brms'
## The following object is masked from 'package:glmmADMB':
## 
##     VarCorr
priorNB.brms=get_prior(sei~ (1|subject) + 
                      trt+lbase+visit, 
                    data=dat, 
                    family="negbinomial")

NB.brms <- brm(sei~ (1|subject) + 
              trt+lbase+visit, 
            data=dat, 
            family='negbinomial',
            prior=priorNB.brms,
            chains=3, 
            iter=2000, 
            warmup=500, 
            thin=2)
## Compiling the C++ model
## Start sampling
## 
## SAMPLING FOR MODEL 'negbinomial(log) brms-model' NOW (CHAIN 1).
## 
## Gradient evaluation took 0 seconds
## 1000 transitions using 10 leapfrog steps per transition would take 0 seconds.
## Adjust your expectations accordingly!
## 
## 
## Iteration:    1 / 2000 [  0%]  (Warmup)
## Iteration:  200 / 2000 [ 10%]  (Warmup)
## Iteration:  400 / 2000 [ 20%]  (Warmup)
## Iteration:  501 / 2000 [ 25%]  (Sampling)
## Iteration:  700 / 2000 [ 35%]  (Sampling)
## Iteration:  900 / 2000 [ 45%]  (Sampling)
## Iteration: 1100 / 2000 [ 55%]  (Sampling)
## Iteration: 1300 / 2000 [ 65%]  (Sampling)
## Iteration: 1500 / 2000 [ 75%]  (Sampling)
## Iteration: 1700 / 2000 [ 85%]  (Sampling)
## Iteration: 1900 / 2000 [ 95%]  (Sampling)
## Iteration: 2000 / 2000 [100%]  (Sampling)
## 
##  Elapsed Time: 3.188 seconds (Warm-up)
##                5.403 seconds (Sampling)
##                8.591 seconds (Total)
## 
## 
## SAMPLING FOR MODEL 'negbinomial(log) brms-model' NOW (CHAIN 2).
## 
## Gradient evaluation took 0 seconds
## 1000 transitions using 10 leapfrog steps per transition would take 0 seconds.
## Adjust your expectations accordingly!
## 
## 
## Iteration:    1 / 2000 [  0%]  (Warmup)
## Iteration:  200 / 2000 [ 10%]  (Warmup)
## Iteration:  400 / 2000 [ 20%]  (Warmup)
## Iteration:  501 / 2000 [ 25%]  (Sampling)
## Iteration:  700 / 2000 [ 35%]  (Sampling)
## Iteration:  900 / 2000 [ 45%]  (Sampling)
## Iteration: 1100 / 2000 [ 55%]  (Sampling)
## Iteration: 1300 / 2000 [ 65%]  (Sampling)
## Iteration: 1500 / 2000 [ 75%]  (Sampling)
## Iteration: 1700 / 2000 [ 85%]  (Sampling)
## Iteration: 1900 / 2000 [ 95%]  (Sampling)
## Iteration: 2000 / 2000 [100%]  (Sampling)
## 
##  Elapsed Time: 3.378 seconds (Warm-up)
##                5.929 seconds (Sampling)
##                9.307 seconds (Total)
## 
## 
## SAMPLING FOR MODEL 'negbinomial(log) brms-model' NOW (CHAIN 3).
## 
## Gradient evaluation took 0 seconds
## 1000 transitions using 10 leapfrog steps per transition would take 0 seconds.
## Adjust your expectations accordingly!
## 
## 
## Iteration:    1 / 2000 [  0%]  (Warmup)
## Iteration:  200 / 2000 [ 10%]  (Warmup)
## Iteration:  400 / 2000 [ 20%]  (Warmup)
## Iteration:  501 / 2000 [ 25%]  (Sampling)
## Iteration:  700 / 2000 [ 35%]  (Sampling)
## Iteration:  900 / 2000 [ 45%]  (Sampling)
## Iteration: 1100 / 2000 [ 55%]  (Sampling)
## Iteration: 1300 / 2000 [ 65%]  (Sampling)
## Iteration: 1500 / 2000 [ 75%]  (Sampling)
## Iteration: 1700 / 2000 [ 85%]  (Sampling)
## Iteration: 1900 / 2000 [ 95%]  (Sampling)
## Iteration: 2000 / 2000 [100%]  (Sampling)
## 
##  Elapsed Time: 3.215 seconds (Warm-up)
##                5.202 seconds (Sampling)
##                8.417 seconds (Total)
summary(NB.brms)
##  Family: negbinomial(log) 
## Formula: sei ~ (1 | subject) + trt + lbase + visit 
##    Data: dat (Number of observations: 236) 
## Samples: 3 chains, each with iter = 2000; warmup = 500; thin = 2; 
##          total post-warmup samples = 2250
##     ICs: LOO = Not computed; WAIC = Not computed
##  
## Group-Level Effects: 
## ~subject (Number of levels: 59) 
##               Estimate Est.Error l-95% CI u-95% CI Eff.Sample Rhat
## sd(Intercept)     0.52      0.07      0.4     0.69        994    1
## 
## Population-Level Effects: 
##              Estimate Est.Error l-95% CI u-95% CI Eff.Sample Rhat
## Intercept        1.81      0.11     1.60     2.04       1814    1
## trtprogabide    -0.34      0.17    -0.67    -0.01       1640    1
## lbase            1.01      0.11     0.79     1.23       1716    1
## visit           -0.27      0.17    -0.60     0.06       1679    1
## 
## Family Specific Parameters: 
##       Estimate Est.Error l-95% CI u-95% CI Eff.Sample Rhat
## shape     7.66      1.92     4.64    11.97       1622    1
## 
## Samples were drawn using sampling(NUTS). For each parameter, Eff.Sample 
## is a crude measure of effective sample size, and Rhat is the potential 
## scale reduction factor on split chains (at convergence, Rhat = 1).

Các hệ số hồi quy và tham số \(\theta\) trong mô hình NB.brms theo trướng phái Bayes gần như không khác biệt đáng kể so với mô hình nb.admab. Bây giờ chúng ta sẽ sử dụng Stan để để fit mô hình thông qua package rstan.

Xmat <-  model.matrix(~dat$trt+dat$lbase+dat$visit)
Nsub <-  nlevels(dat$subject)  # no of subjects
K = ncol(Xmat)
data.NBI = list(y = dat$sei,           #  response variable
             N = nrow(dat),          # no of obs
             X = Xmat,               # model matrix of predictors and intercept
             K = K,          # no of predictors + intercept
             Nsub = Nsub,            # no of subs ( 59 = no of random effect VALUEs)
             sub = dat$subject %>% as.numeric(.), # Subject ID
             bpri = rep(0, K),       # To define prior for beta
             Bpri = diag(1, K),      # Prior matrix of beta 
             apri = rep(0, Nsub),    # To define prior for intercept
             Apri = diag(1, Nsub ))  # Prior matrix of intercept 

Ở những bài thực hành trước các bạn đã làm quen với cách viết stan code theo từng block, nên chúng tôi xin phép không nhắc lại lần nữa. Ở đây chúng tôi sẽ ghi chú trực tiếp trong từng block để giải thích ý nghĩa của từng dòng lệnh.

stan_NBI.stri ="
data {
    int<lower=0> N;        //no of obs
    int<lower=0> K;        //no of parameters (intercept+predictors)
    int<lower=0> Nsub;     //no of subs
    int<lower=0> sub[N];   // index for subject
    int<lower=0> y[N];     //dependent variable
    matrix[N,K] X;         //model matrix
    vector[K] bpri;        
    matrix[K,K] Bpri;
    vector[Nsub] apri;
    matrix[Nsub, Nsub] Apri;
}
parameters {
   vector[K] beta;            //beta[1] is population intercept
   vector[Nsub] re_itcept;    // random intercept
   real<lower=0> sigma_re;             //SD random intercept
   real<lower=0> alpha;                //direct dispersion parameter
}
transformed parameters {
   vector[N] eta;             //linear predictor
   vector[N] mu;              //estimated mean of dependent variable
   eta = X*beta;
   for(i in 1:N) {
     mu[i] = exp(eta[i] + re_itcept[sub[i]]);
   }
}
model {
   //prior
   alpha ~ cauchy(0,25);
   sigma_re ~ cauchy(0,5);
   beta ~ multi_normal(bpri, Bpri);     //mean = 0; sd = 1
   re_itcept ~ multi_normal(apri, Apri*sigma_re);   //mean = 0
   //likelihood
   y ~ neg_binomial_2(mu, 1/alpha); //theta = 1/alpha
}
"

Chạy mô hình với Stan.

library(rstan)
## Warning: package 'rstan' was built under R version 3.3.3
## Loading required package: StanHeaders
## Warning: package 'StanHeaders' was built under R version 3.3.3
## rstan (Version 2.16.2, packaged: 2017-07-03 09:24:58 UTC, GitRev: 2e1f913d3ca3)
## For execution on a local, multicore CPU with excess RAM we recommend calling
## rstan_options(auto_write = TRUE)
## options(mc.cores = parallel::detectCores())
## 
## Attaching package: 'rstan'
## The following object is masked from 'package:tidyr':
## 
##     extract
rstan_options(auto_write = TRUE)
options(mc.cores = parallel::detectCores())
set.seed(12345)
fit.stan <-  stan(model_code = stan_NBI.stri, data = data.NBI, 
                 iter = 2000, chains = 3)
## In file included from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/config.hpp:39:0,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/math/tools/config.hpp:13,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core/var.hpp:7,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core/gevv_vvv_vari.hpp:5,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core.hpp:12,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/mat.hpp:4,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math.hpp:4,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/src/stan/model/model_header.hpp:4,
##                  from file1b2c33bf107c.cpp:8:
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/config/compiler/gcc.hpp:186:0: warning: "BOOST_NO_CXX11_RVALUE_REFERENCES" redefined
##  #  define BOOST_NO_CXX11_RVALUE_REFERENCES
##  ^
## <command-line>:0:0: note: this is the location of the previous definition
## In file included from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/base.hpp:28:0,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array.hpp:21,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/numeric/odeint/util/multi_array_adaption.hpp:29,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/numeric/odeint.hpp:61,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/prim/arr/functor/integrate_ode_rk45.hpp:13,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/prim/arr.hpp:38,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/prim/mat.hpp:298,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/mat.hpp:12,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math.hpp:4,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/src/stan/model/model_header.hpp:4,
##                  from file1b2c33bf107c.cpp:8:
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp: In static member function 'static void boost::multi_array_concepts::detail::idgen_helper<N>::call(Array&, const IdxGen&, Call_Type)':
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp:42:43: warning: typedef 'index_range' locally defined but not used [-Wunused-local-typedefs]
##        typedef typename Array::index_range index_range;
##                                            ^
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp:43:37: warning: typedef 'index' locally defined but not used [-Wunused-local-typedefs]
##        typedef typename Array::index index;
##                                      ^
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp: In static member function 'static void boost::multi_array_concepts::detail::idgen_helper<0u>::call(Array&, const IdxGen&, Call_Type)':
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp:53:43: warning: typedef 'index_range' locally defined but not used [-Wunused-local-typedefs]
##        typedef typename Array::index_range index_range;
##                                            ^
## C:/Users/Tien Tai/Documents/R/win-library/3.3/BH/include/boost/multi_array/concept_checks.hpp:54:37: warning: typedef 'index' locally defined but not used [-Wunused-local-typedefs]
##        typedef typename Array::index index;
##                                      ^
## In file included from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core.hpp:42:0,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/mat.hpp:4,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math.hpp:4,
##                  from C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/src/stan/model/model_header.hpp:4,
##                  from file1b2c33bf107c.cpp:8:
## C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core/set_zero_all_adjoints.hpp: At global scope:
## C:/Users/Tien Tai/Documents/R/win-library/3.3/StanHeaders/include/stan/math/rev/core/set_zero_all_adjoints.hpp:14:17: warning: 'void stan::math::set_zero_all_adjoints()' defined but not used [-Wunused-function]
##      static void set_zero_all_adjoints() {
##                  ^
print(fit.stan, pars = c("beta", "alpha", "sigma_re"))
## Inference for Stan model: e65d136b55f40d9f95e1a469e74f6cef.
## 3 chains, each with iter=2000; warmup=1000; thin=1; 
## post-warmup draws per chain=1000, total post-warmup draws=3000.
## 
##           mean se_mean   sd  2.5%   25%   50%   75% 97.5% n_eff Rhat
## beta[1]   1.79    0.00 0.12  1.56  1.72  1.80  1.87  2.01   690    1
## beta[2]  -0.31    0.01 0.16 -0.63 -0.41 -0.31 -0.21  0.00   716    1
## beta[3]   1.00    0.00 0.11  0.78  0.92  1.00  1.07  1.21   706    1
## beta[4]  -0.27    0.00 0.17 -0.60 -0.38 -0.27 -0.15  0.05  3000    1
## alpha     0.14    0.00 0.03  0.09  0.12  0.14  0.16  0.22  3000    1
## sigma_re  0.29    0.00 0.08  0.17  0.24  0.28  0.34  0.48  1519    1
## 
## Samples were drawn using NUTS(diag_e) at Mon Nov 06 00:49:36 2017.
## For each parameter, n_eff is a crude measure of effective sample size,
## and Rhat is the potential scale reduction factor on split chains (at 
## convergence, Rhat=1).
para.df <- as.data.frame(fit.stan) %>% dplyr::select(., c(starts_with("beta"), sigma_re, alpha)) %>%
mutate(.,Iteration=as.numeric(rep(c(1:1000),3)), Chain=as.factor(rep(c(1:3),each=1000)))
names(para.df)[1:4] <- c("Intercept","beta_trt","beta_lbase","beta_visit")
head(para.df, 5)
##   Intercept   beta_trt beta_lbase  beta_visit  sigma_re     alpha
## 1  1.750573 -0.2000101  1.0700179 -0.49801039 0.2145746 0.1289975
## 2  1.728312 -0.1089971  0.9621034 -0.50221667 0.2811650 0.1058311
## 3  1.908557 -0.3411169  1.0928259 -0.06548896 0.2391511 0.1592637
## 4  1.859661 -0.4237947  0.9210108  0.03969330 0.2042042 0.1498636
## 5  1.936265 -0.2616077  0.8604439 -0.54907105 0.2322041 0.1735661
##   Iteration Chain
## 1         1     1
## 2         2     1
## 3         3     1
## 4         4     1
## 5         5     1
g1 <- para.df %>% gather(Intercept:alpha, key="Parameters", value="Para_values") %>%
mutate(., Parameters_order = factor(Parameters, levels = names(para.df)[1:6])) %>%
ggplot(aes(x = Para_values,fill= Chain, col= Chain))+
geom_density(alpha=0.2, show.legend = F) +
facet_wrap(~ Parameters_order, ncol=1, scales = "free_y") + theme_bw(7)


g2 <- para.df %>% gather(Intercept:alpha, key="Parameters", value="Para_values") %>%
mutate(., Parameters_order = factor(Parameters, levels = names(para.df)[1:6])) %>%
ggplot(aes(x = Iteration, y = Para_values, col= Chain))+
geom_path(alpha= 0.6, show.legend = F) +
facet_wrap(~ Parameters_order, ncol=1, scales = "free_y") + theme_bw(7)
gridExtra::grid.arrange(g2,g1,ncol=2)