lmer/glmerで信頼区間を算出して作図する方法

お久しぶりの投稿です。

# 本日のテーマ
過去に書いた自分の記事に突っ込みを入れます。

過去に、lmer/glmerで信頼区間を算出する方法、としてconfint.mermod関数を紹介しました。
https://qiita.com/Jwellyfish/items/4b667a4dd1e0da18ee9f

現時点では (おそらくその当時も) もっと簡単な方法が存在していますので、そのご紹介です。
併せて、効果量 (effect size/risk ratio) と信頼区間をプロットする方法も紹介します。

データはHSAURパッケージのBtheBを使うことにします。

# 概要
lmer()/glmer()で作成されたmerModオブジェクトにbroom::tidy()を噛ませることで、tibbleとして出力してくれます。summary(lmer/glmer) ではp値および信頼区間を算出してくれませんが、broom::tidy()の引数conf.int = TRUEを設定することで信頼区間を出力してくれます。broom::tidy()で出力した効果量と信頼区間をforestplot::forestplot()に引き渡すことで、簡単に効果量をプロットできます。

# 準備
まずはBtheBを準備しましょう。
この辺の前処理は、REQUIER02の土屋さんのご発表をtidyverseで再現したものです。

```
> library(HSAUR)
> data <- BtheB %>%
  tibble::rownames_to_column(var = "ID") # adding rownames as a variable "ID"

> head(data)
  ID drug length treatment bdi.pre bdi.2m bdi.4m bdi.6m bdi.8m
1  1   No    >6m       TAU      29      2      2     NA     NA
2  2  Yes    >6m     BtheB      32     16     24     17     20
3  3  Yes    <6m       TAU      25     20     NA     NA     NA
4  4   No    >6m     BtheB      21     17     16     10      9
5  5  Yes    >6m     BtheB      26     23     NA     NA     NA
6  6  Yes    <6m     BtheB       7      0      0      0      0
```

BtheBデータは横持ちなので、縦持ちに変換します。

```
data1 <- data %>%
  gather(key = "month",
         value = "bdi",
         `bdi.2m`,`bdi.4m`,`bdi.6m`,`bdi.8m`
         )
```

横持時の変数名を時系列変数に変換します。

```
data1$month <- ifelse(data1$month == "bdi.2m", 2, data1$month)
data1$month <- ifelse(data1$month == "bdi.4m", 4, data1$month)
data1$month <- ifelse(data1$month == "bdi.6m", 6, data1$month)
data1$month <- ifelse(data1$month == "bdi.8m", 8, data1$month)
 
subset(data1, ID %in% c("1", "2", "3"))
 
> subset(data1, ID %in% c("1", "2", "3"))
    ID drug length treatment bdi.pre month bdi
1    1   No    >6m       TAU      29     2   2
2    2  Yes    >6m     BtheB      32     2  16
3    3  Yes    <6m       TAU      25     2  20
101  1   No    >6m       TAU      29     4   2
102  2  Yes    >6m     BtheB      32     4  24
103  3  Yes    <6m       TAU      25     4  NA
201  1   No    >6m       TAU      29     6  NA
202  2  Yes    >6m     BtheB      32     6  17
203  3  Yes    <6m       TAU      25     6  NA
301  1   No    >6m       TAU      29     8  NA
302  2  Yes    >6m     BtheB      32     8  20
303  3  Yes    <6m       TAU      25     8  NA
```

# IDのrandom-slopeモデルでLMM (Linear Mixed Model)

```
> model01 <- lmer(bdi ~ treatment + month + bdi.pre + drug + length + (1|ID), data = data1)
> summary(model01)
Linear mixed model fit by REML ['lmerMod']
Formula: bdi ~ treatment + month + bdi.pre + drug + length + (1 | ID)
   Data: data1

REML criterion at convergence: 1859.8

Scaled residuals: 
    Min      1Q  Median      3Q     Max 
-2.7135 -0.4974 -0.0827  0.4084  3.7246 

Random effects:
 Groups   Name        Variance Std.Dev.
 ID       (Intercept) 51.41    7.170   
 Residual             25.52    5.052   
Number of obs: 280, groups:  ID, 97

Fixed effects:
               Estimate Std. Error t value
(Intercept)     4.46830    2.26253   1.975
treatmentBtheB -2.35589    1.70968  -1.378
month4         -1.42557    0.81954  -1.739
month6         -2.64780    0.89391  -2.962
month8         -4.37403    0.92965  -4.705
bdi.pre         0.63909    0.07968   8.021
drugYes        -2.79416    1.76710  -1.581
length>6m       0.23614    1.67690   0.141

Correlation of Fixed Effects:
            (Intr) trtmBB month4 month6 month8 bdi.pr drugYs
tretmntBthB -0.395                                          
month4      -0.141  0.021                                   
month6      -0.138  0.025  0.440                            
month8      -0.125  0.019  0.423  0.432                     
bdi.pre     -0.691  0.122  0.012  0.025  0.018              
drugYes     -0.076 -0.323 -0.016 -0.023 -0.023 -0.237       
length>6m   -0.247  0.001 -0.034 -0.039 -0.039 -0.241  0.158
> 
```

# broom()を用いてtidyな感じで固定効果を抽出

```
> library(broom)
> es.model01 <- tidy(model01, effects = "fixed", conf.int = TRUE)
> es.model01
            term   estimate  std.error  statistic    conf.low  conf.high
1    (Intercept)  4.4683042 2.26253164  1.9749135  0.03382372  8.9027848
2 treatmentBtheB -2.3558889 1.70967729 -1.3779729 -5.70679483  0.9950170
3         month4 -1.4255691 0.81954421 -1.7394658 -3.03184629  0.1807080
4         month6 -2.6477953 0.89390922 -2.9620405 -4.39982513 -0.8957654
5         month8 -4.3740325 0.92964553 -4.7050541 -6.19610425 -2.5519607
6        bdi.pre  0.6390882 0.07967549  8.0211392  0.48292710  0.7952493
7        drugYes -2.7941559 1.76710183 -1.5812082 -6.25761183  0.6693001
8      length>6m  0.2361410 1.67689698  0.1408202 -3.05051673  3.5227986
> 
```

大本のmodel01は線形混合モデルの結果です。
broom::tidy()を用いて綺麗にまとめたのがex.model01。引数conf.intをTRUEとすることで信頼区間を表示できます。信頼区間の水準はconf.levelで。effects = "fixed"を指定すると、固定効果のみを抽出できます。ランダム効果が効いてない場合にeffectsをデフォルト指定するとここでエラーを吐きます。
なお、glmer(family = binomial)、すなわちロジスティック回帰で一般化線形混合モデルを用いる場合、tidy関数のexponentiateは無効化されている模様 (glmではまだ有効？)。この辺、もう一工夫が必要そう。

# broom()でまとめたlmer/glmerの結果をforestplotで出力

```
# plotting modeled effects
plot.es <- forestplot(
  labeltext = es.model01$term, mean = es.model01$estimate, lower = es.model01$conf.low, upper = es.model01$conf.high, # 変数名、効果量、信頼区間 (下限)、信頼区間 (上限)
  is.summary = FALSE, # メタアナっぽく表を同時表示するならTRUEに
  
  fn.ci_norm = fpDrawCircleCI, # マーカーの形状を指定
  boxsize = 0.3, # 主にマーカーの大きさを指定
  col = fpColors(box = "royalblue", lines = "gray", zero = "darkred"), # グラフ要素の色指定。
  colgap = unit(6, "mm"), # 変数間の間隔
  
  # grid = structure(c(-1, 1, 2, 3), gp = gpar(lty = 2, col = "#CCCCFF")), # グリッドの設定。お好みで。
  lineheight = unit(1, "cm"), # default: auto
  zero = 0, lwd.zero = 2, # X軸の0の設定。対数軸ならzero = 1、そうでなければzero = 0 (default)
  
  xtics = seq(from = -6, to = 9, by = 3), # X軸の軸ラベルの設定
  xlab = "BDI scores",
  txt_gp = fpTxtGp(label = gpar(fontfamily = "", cex = 1),
                   ticks = gpar(fontfamily = "", cex = 1),
                   xlab  = gpar(fontfamily = "", cex = 1)), # 文字のサイズ設定
  
  mar = unit(rep(5, times = 4), "mm") # グラフの四隅からのマージンの設定
)
```

ここでプロットされるグラフはggplot2系ではないので、jpeg()/png()とdev.off()で挟んであげましょう。
出力されたグラフがこちら。

![plots.png](https://qiita-image-store.s3.amazonaws.com/0/227476/febaacf1-a6bb-6eda-61e1-0805f78c1170.png)

forestplot()のis.summary以降は描画の細かい設定で、効果量や信頼区間の限界値はlabeltextの行で指定済。推定結果をtibbleで返してくれるbroom::tidy()が活きてます。