6 The Haunted DAG & the Causal Terror
Preface
合流点を固定してしまうと、あるいは合流点の値で選抜を行ってしまうと、疑相関が生じてしまう。
例えば、交付金申請に通った研究内のみで「研究の信頼性」と「研究の話題性」の相関を調べる場合。
本来それらには相関がなくても、申請に通った研究(両者の合計点が高い研究)のみに絞ると、負の相関が生じる。
theme_set(theme_classic())
set.seed(200)
<- 500 # number of proposal
n <- 0.1 # prop to select
p
<- tibble(newsworthiness = rnorm(n),
d trustworthiness = rnorm(n))
%>%
d ggplot(aes(x=newsworthiness,y=trustworthiness))+
geom_point()+
theme(aspect.ratio=1)+
geom_smooth(method = "lm", color = "black")
合計点が上位10%のみの研究に絞ると…。
%>%
d mutate(total = trustworthiness + newsworthiness) %>%
mutate(selected = ifelse(total >= quantile(total, 1-p), TRUE, FALSE)) -> d
head(d)
## # A tibble: 6 × 4
## newsworthiness trustworthiness total selected
## <dbl> <dbl> <dbl> <lgl>
## 1 0.0848 -0.354 -0.269 FALSE
## 2 0.226 -1.93 -1.70 FALSE
## 3 0.433 -0.761 -0.328 FALSE
## 4 0.558 2.34 2.90 TRUE
## 5 0.0598 -2.07 -2.01 FALSE
## 6 -0.115 -1.04 -1.15 FALSE
<-
text tibble(newsworthiness = c(2, 1),
trustworthiness = c(2.25, -2.5),
selected = c(TRUE, FALSE),
label = c("selected", "rejected"))
%>%
d ggplot(aes(x = newsworthiness, y=trustworthiness,
color = selected))+
geom_point(aes(shape = selected), alpha =3/4)+
geom_text(data = text,
aes(label = label))+
scale_shape_manual(values=c(1,19))+
scale_color_manual(values=c("black", "navyblue"))+
geom_smooth(data = . %>% filter(selected == "TRUE"),
method = "lm", color = "navyblue",
se = F, size = 1/2, fullrange = T)+
theme(legend.position = "none",
aspect.ratio=1)
本章では、回帰モデルを使用する際にどの変数を加えるべきで、どの変数を加えるべきでないかを検討する。
6.1 Multicollinearity
両脚の長さから身長を予測するモデルを考える。
set.seed(909)
<- 100
n
<- tibble(height = rnorm(n, 10, 2),
d leg_prop = runif(n, 0.4, 0.5)) %>%
mutate(leg_left = leg_prop*height +
rnorm(n,0,0.02),
leg_right = leg_prop*height +
rnorm(n, 0, 0.02))
当然、左右の脚の長さ同士は強く相関するし、どちらも身長と強く相関する。
%>%
d ::select(-leg_prop) %>%
dplyrcor()
## height leg_left leg_right
## height 1.0000000 0.9557084 0.9559273
## leg_left 0.9557084 1.0000000 0.9997458
## leg_right 0.9559273 0.9997458 1.0000000
%>%
d ggplot(aes(x=leg_left, y=leg_right))+
geom_point()+
theme(aspect.ratio=1)-> p1
%>%
d ggplot(aes(x=leg_left, y=height))+
geom_point()+
theme(aspect.ratio=1) -> p2
%>%
d ggplot(aes(x=leg_right, y=height))+
geom_point()+
theme(aspect.ratio=1) -> p3
+p2+p3 p1
そこで、モデルを回してみると…。
どちらも身長にほとんど影響していないように見えるし、両脚が異なる影響を与えているような推定結果になってしまう。
.1 <-
b6brm(data = d,
family = gaussian,
formula = height ~ 1 + leg_left + leg_right,
prior = c(prior(normal(10,100),class=Intercept),
prior(normal(2,10),class=b),
prior(exponential(1),class = sigma)),
iter=2000,warmup=1000,chains=4,
backend = "cmdstanr",
file = "output/Chapter6/b6.1")
posterior_summary(b6.1) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0.98 0.29 0.4 1.56
## 2 b_leg_left 0.17 2.59 -4.94 5.17
## 3 b_leg_right 1.83 2.59 -3.17 6.96
## 4 sigma 0.63 0.05 0.55 0.73
## 5 lprior -12.7 0.12 -13.0 -12.6
## 6 lp__ -109. 1.48 -113. -107.
posterior_samples(b6.1) %>%
::select(-lp__) %>%
dplyrmcmc_intervals()
繰り返しやってみる。 推定結果にかなりばらつきが出る。
<- function(seed, n = 100) {
sim_and_fit <- n
n set.seed(seed)
<-
d tibble(height = rnorm(n, mean = 10, sd = 2),
leg_prop = runif(n, min = 0.4, max = 0.5)) %>%
mutate(leg_left = leg_prop * height + rnorm(n, mean = 0, sd = 0.02),
leg_right = leg_prop * height + rnorm(n, mean = 0, sd = 0.02))
<- update(b6.1, newdata = d)
fit
}
#sim <-
# tibble(seed = 1:4) %>%
#mutate(post = purrr::map(seed, ~sim_and_fit(.) %>%
# posterior_samples()))
<- readRDS("output/Chapter6/sim.rds")
sim
%>%
sim unnest(post) %>%
pivot_longer(b_Intercept:sigma) %>%
mutate(seed = str_c("seed ", seed)) %>%
ggplot(aes(x = value, y = name)) +
stat_pointinterval(.width = .95, color = "forestgreen") +
labs(x = "posterior", y = NULL) +
theme(axis.text.y = element_text(hjust = 0),
panel.border = element_rect(color = "black", fill = "transparent"),
panel.grid.minor = element_blank(),
strip.text = element_text(hjust = 0)) +
facet_wrap(~ seed, ncol = 1)
両脚の係数は強く相関する。
posterior_samples(b6.1) %>%
::select(b_leg_right,b_leg_left) %>%
dplyrggplot(aes(x=b_leg_right,y=b_leg_left))+
geom_point()
また、両者の合計はおおよそどちらか一つの変数のみを説明変数に入れたときの係数(2程度)と一致する。
posterior_samples(b6.1) %>%
mutate(sum_b = b_leg_left+b_leg_right) %>%
::select(sum_b) %>%
dplyrggplot(aes(x=sum_b))+
geom_histogram()
これは、左右の脚の長さが強く相関しているため、モデルが以下の式のように近似できると考えると上手く説明できる。
$ \[\begin{aligned} y_{i} &\sim Normal(\mu_{i}, \sigma)\\ \mu_{i} &= \alpha + (\beta_{1} + \beta_{2})x_{i} \end{aligned}\]$
.1_2 <-
b6brm(data = d,
family = gaussian,
formula = height ~ 1 + leg_right,
prior = c(prior(normal(10,100),class=Intercept),
prior(normal(2,10),class=b),
prior(exponential(1),class = sigma)),
iter=2000,warmup=1000,chains=4,
backend = "cmdstanr",
file = "output/Chapter6/b6.1_2")
posterior_summary(b6.1_2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0.98 0.29 0.4 1.56
## 2 b_leg_right 2 0.06 1.87 2.12
## 3 sigma 0.63 0.05 0.55 0.72
## 4 lprior -9.38 0.05 -9.47 -9.3
## 5 lp__ -105. 1.24 -108. -104.
6.1.1 Multicollinear milk
霊長類の母乳のデータを用いて考える。
脂肪分の割合(F)とラクトースの割合(L)でカロリー(K)を予測することを考える。
FとLは強く負に相関する。
data(milk)
<- milk %>%
d2 mutate(K = standardize(kcal.per.g),
F = standardize(perc.fat),
L = standardize(perc.lactose))
head(d2)
## clade species kcal.per.g perc.fat perc.protein
## 1 Strepsirrhine Eulemur fulvus 0.49 16.60 15.42
## 2 Strepsirrhine E macaco 0.51 19.27 16.91
## 3 Strepsirrhine E mongoz 0.46 14.11 16.85
## 4 Strepsirrhine E rubriventer 0.48 14.91 13.18
## 5 Strepsirrhine Lemur catta 0.60 27.28 19.50
## 6 New World Monkey Alouatta seniculus 0.47 21.22 23.58
## perc.lactose mass neocortex.perc K F L
## 1 67.98 1.95 55.16 -0.9400408 -1.2172427 1.3072619
## 2 63.82 2.09 NA -0.8161263 -1.0303552 1.0112855
## 3 69.04 2.51 NA -1.1259125 -1.3915310 1.3826790
## 4 71.91 1.62 NA -1.0019980 -1.3355347 1.5868743
## 5 53.22 2.19 NA -0.2585112 -0.4696927 0.2571148
## 6 55.20 5.25 64.54 -1.0639553 -0.8938643 0.3979882
%>%
d2 ::select(F,K,L) %>%
dplyrpairs()
library(GGally)
ggpairs(data = d2, columns = 9:11)
それぞれの変数でモデリングしてみる。
.2_F <-
b6brm(data = d2,
family = gaussian,
formula = K ~ 1 + F,
prior = c(prior(normal(0,0.2),class=Intercept),
prior(normal(0,0.5),class=b),
prior(exponential(1),class=sigma)),
iter=4000, warmup = 3000, chains=4,
backend = "cmdstanr",
file = "output/Chapter6/b6.2_F")
.2_L <-
b6brm(data = d2,
family = gaussian,
formula = K ~ 1 + L,
prior = c(prior(normal(0,0.2),class=Intercept),
prior(normal(0,0.5),class=b),
prior(exponential(1),class=sigma)),
iter=4000, warmup = 3000, chains=4,
backend = "cmdstanr",
file = "output/Chapter6/b6.2_L")
posterior_summary(b6.2_F) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.08 -0.17 0.15
## 2 b_F 0.86 0.1 0.66 1.04
## 3 sigma 0.49 0.07 0.38 0.65
## 4 lprior -1.6 0.36 -2.37 -0.98
## 5 lp__ -22.1 1.3 -25.5 -20.6
posterior_summary(b6.2_L) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.07 -0.14 0.14
## 2 b_L -0.9 0.08 -1.05 -0.74
## 3 sigma 0.41 0.06 0.32 0.55
## 4 lprior -1.63 0.3 -2.29 -1.12
## 5 lp__ -17.4 1.3 -20.8 -15.9
次に、両方の変数を説明変数に入れてモデリング。係数が変化しており、ばらつきも大きくなっている。
.2_LF <-
b6brm(data = d2,
family = gaussian,
formula = K ~ 1 + L + F,
prior = c(prior(normal(0,0.2),class=Intercept),
prior(normal(0,0.5),class=b),
prior(exponential(1),class=sigma)),
iter=4000, warmup = 3000, chains=4,
backend = "cmdstanr",
file = "output/Chapter6/b6.2_LF")
posterior_summary(b6.2_LF) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.07 -0.14 0.14
## 2 b_L -0.67 0.2 -1.06 -0.25
## 3 b_F 0.25 0.19 -0.14 0.64
## 4 sigma 0.42 0.06 0.32 0.54
## 5 lprior -1.42 0.43 -2.56 -0.87
## 6 lp__ -17.3 1.44 -20.9 -15.4
posterior_summary(b6.2_LF) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.07 -0.14 0.14
## 2 b_L -0.67 0.2 -1.06 -0.25
## 3 b_F 0.25 0.19 -0.14 0.64
## 4 sigma 0.42 0.06 0.32 0.54
## 5 lprior -1.42 0.43 -2.56 -0.87
## 6 lp__ -17.3 1.44 -20.9 -15.4
およらく、データには以下のような因果関係がある。
library(ggdag)
<-
dag_coords tibble(name = c("L", "D", "F", "K"),
x = c(1, 2, 3, 2),
y = c(2, 2, 2, 1))
dagify(L ~ D,
~ D,
F ~ L + F,
K coords = dag_coords) %>%
ggplot(aes(x = x, y = y, xend = xend, yend = yend)) +
geom_dag_point(aes(color = name == "D"),
alpha = 1/2, size = 6.5, show.legend = F) +
geom_point(x = 2, y = 2,
size = 6.5, shape = 1, stroke = 1, color = "orange") +
geom_dag_text(color = "black") +
geom_dag_edges() +
scale_color_manual(values = c("steelblue", "orange")) +
scale_x_continuous(NULL, breaks = NULL, expand = c(.1, .1)) +
scale_y_continuous(NULL, breaks = NULL, expand = c(.1, .1))
6.2 Post-treatment bias
土中の真菌に対する処置を行った場合に植物の成長が早くなるかを検討する。
ここでは、処置前の植物の高さ(h0)、処置の有無(T)、処置後の真菌の量(F)、処置後の植物の高さ(h1)を考える。どの変数をモデルに含めるべきだろうか?
もし処置の結果を知りたいのであれば、Fを説明変数に入れてはいけない。それはpost-treatment effectだからである。
set.seed(71)
<- 100
n
<- tibble(
d3 h0 = rnorm(n, 10,2),
T = rep(0:1,each=n/2),
F = rbinom(n, size = 1, prob = .5 - T*0.4),
h1 = h0 + rnorm(n, mean = 5-3*F, sd=1))
%>%
d3 pivot_longer(everything()) %>%
group_by(name) %>%
mean_qi(.width = .89) %>%
mutate_if(is.double, round, digits = 2)
## # A tibble: 4 × 7
## name value .lower .upper .width .point .interval
## <chr> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1 F 0.23 0 1 0.89 mean qi
## 2 h0 9.96 6.57 13.1 0.89 mean qi
## 3 h1 14.4 10.6 17.9 0.89 mean qi
## 4 T 0.5 0 1 0.89 mean qi
まず、処置前の高さのみを入れた以下のモデルを考える。
このとき、\(p\)は必ず0以上になるので、事前分布として対数正規分布を用いる。
\(h_{1,i} \sim Normal(\mu_{i}, \sigma)\)
\(\mu_{i} = h_{0,i}×p\)
\(p \sim Lognormal(0, 0.25)\)
\(\sigma \sim Exponential(1)\)
推定結果は以下の通り。
40%強の成長が見込まれる。
.3_h <-
b6brm(data=d3,
family = gaussian,
formula = h1 ~ 0 + h0,
prior = c(prior(lognormal(0, 0.25),class = b),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4,
backend = "cmdstanr",
file = "output/Chapter6/b6.3_h")
posterior_summary(b6.3_h) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 4 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_h0 1.43 0.02 1.39 1.46
## 2 sigma 1.83 0.13 1.59 2.11
## 3 lprior -2.72 0.16 -3.07 -2.44
## 4 lp__ -204. 1.02 -207. -203.
続いて、処置の有無(T)と処置後の真菌の有無(F)もいれた以下のモデルを考える。
\(h_{1,i} \sim Normal(\mu_{i}, \sigma)\)
\(\mu_{i} = h_{0,i}×p\)
\(p = \alpha + \beta_{T}T_{i} + \beta_{F}F_{i}\)
\(\alpha \sim Lognormal(0, 0.25)\)
\(\beta \sim Normal(0, 0.5)\)
\(\sigma \sim Exponential(1)\)
結果、処置の結果が0に近いと推定される。
.3_htf <-
b6brm(
data = d3,
family = gaussian,
formula = bf(h1 ~ h0*(a + t*T + f*F),
+t+f ~ 1,
anl = TRUE),
prior=c(prior(lognormal(0,0.2),nlpar = a,lb = 0),
prior(normal(0, 0.5), nlpar = t),
prior(normal(0, 0.5), nlpar = f),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4,
seed=6,
backend = "cmdstanr",
file = "output/Chapter6/b6.3_htf"
)
posterior_summary(b6.3_htf) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_a_Intercept 1.48 0.03 1.43 1.53
## 2 b_t_Intercept 0 0.03 -0.06 0.06
## 3 b_f_Intercept -0.27 0.04 -0.34 -0.19
## 4 sigma 1.45 0.1 1.26 1.67
## 5 lprior -3.68 0.23 -4.17 -3.26
## 6 lp__ -182. 1.5 -186. -180.
pairs(b6.3_htf)
6.2.1 Blocked by consequence
これは、処置後の真菌を入れたことによって、T -> h1へのパスがブロックされたからである。(h1とTについてバックドア基準を満たさない)
よって、Fを除外してモデリングする必要がある。
.3_ht <-
b6brm(
data = d3,
family = gaussian,
formula = bf(h1 ~ h0*(a + t*T),
+t ~ 1,
anl = TRUE),
prior=c(prior(lognormal(0,0.2),nlpar = a,lb = 0),
prior(normal(0, 0.5), nlpar = t),
prior(exponential(1), class = sigma)),
iter = 2000, warmup = 1000, chains = 4,
seed =6,
backend = "cmdstanr",
file = "output/Chapter6/b6.3_ht"
)
posterior_summary(b6.3_ht) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_a_Intercept 1.38 0.03 1.33 1.43
## 2 b_t_Intercept 0.08 0.04 0.02 0.15
## 3 sigma 1.79 0.13 1.56 2.05
## 4 lprior -2.97 0.2 -3.39 -2.6
## 5 lp__ -202. 1.23 -205. -201.
6.2.2 Fungus and d-separation
DAGを用いて考えてみる。
<-
dag_coords tibble(name = c("H0", "T", "F", "H1"),
x = c(1, 5, 4, 3),
y = c(2, 2, 1.5, 1))
<-
dag dagify(F ~ T,
~ H0 + F,
H1 coords = dag_coords)
上記の因果モデルにおいて、条件付き独立を求めると、Fで条件づけたときにH1とTが独立になってしまうことが分かる。
このとき、H1とTはFで条件づけたときにd分離しているという。
impliedConditionalIndependencies(dag)
## F _||_ H0
## H0 _||_ T
## H1 _||_ T | F
続いて、以下のような因果モデルを考える。
このモデルでは、H0と水分(M、非観測変数)のみがH1に影響を与え、MとTがFに影響を与えるとする。このとき,
Tだけを説明変数に入れてもH1との関連は見られないが、Fを入れるとTとH1に関連がみられる。
<-
dag_coords tibble(name = c("H0", "H1", "M", "F", "T"),
x = c(1, 2, 2.5, 3, 4),
y = c(2, 2, 1, 2, 2))
# save our DAG
<-
dag dagify(F ~ M + T,
~ H0 + M,
H1 coords = dag_coords)
%>%
dag ggplot(aes(x = x, y = y, xend = xend, yend = yend)) +
geom_dag_point(aes(color = name == "M"),
alpha = 1/2, size = 6.5, show.legend = F) +
geom_point(x = 2.5, y = 1,
size = 6.5, shape = 1, stroke = 1, color = "orange") +
geom_dag_text(color = "black") +
geom_dag_edges() +
scale_color_manual(values = c("steelblue", "orange")) +
theme_dag()
set.seed(71)
<- 1000
n
<- tibble(
d4 h0 = rnorm(n, 10,2),
T = rep(0:1, each = n/2),
M = rbern(n),
F = rbinom(n, size=1, prob = 0.5-T*0.4+0.4*M),
h1 = h0 + rnorm(n, mean =5 + 3*M, sd=1)
)
head(d4)
## # A tibble: 6 × 5
## h0 T M F h1
## <dbl> <int> <int> <int> <dbl>
## 1 9.14 0 0 0 14.8
## 2 9.11 0 0 0 15.3
## 3 9.04 0 1 1 16.4
## 4 10.8 0 1 1 19.1
## 5 9.16 0 1 1 17.2
## 6 7.63 0 0 0 13.4
.3_ht2 <-
b6update(b6.3_ht,
newdata = d4,
seed =6,
file = "output/Chapter6/b6.3_ht2")
.3_htf2 <-
b6update(b6.3_htf,
newdata = d4,
seed =6,
file = "output/Chapter6/b6.3_htf2")
posterior_summary(b6.3_ht2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_a_Intercept 1.62 0.01 1.6 1.64
## 2 b_t_Intercept -0.01 0.01 -0.04 0.02
## 3 sigma 2.21 0.05 2.12 2.31
## 4 lprior -5.17 0.09 -5.36 -4.99
## 5 lp__ -2216. 1.21 -2219. -2215.
posterior_summary(b6.3_htf2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_a_Intercept 1.52 0.01 1.5 1.55
## 2 b_t_Intercept 0.05 0.01 0.02 0.08
## 3 b_f_Intercept 0.14 0.01 0.12 0.17
## 4 sigma 2.11 0.05 2.02 2.2
## 5 lprior -4.54 0.11 -4.75 -4.34
## 6 lp__ -2169. 1.41 -2172. -2167.
これはなぜだろうか?
次節でこの問題を見ていく。
6.3 Collider bias
再び、本章冒頭の例を考える。
DAGは以下のようになる。
<-
dag_coords tibble(name = c("T", "S", "N"),
x = c(1, 2, 3),
y = c(2, 1, 2))
<- dagify(S ~ T,
dag ~ N,
S coords = dag_coords)
gg_simple_dag(dag)
このとき、Sについて条件づけると、TとNの間に疑似相関が生じてしまう。
6.3.1 Collider of false sorrow
年齢(A)が幸福(H)にどう影響するかを検討したいとする。
ここでは、幸せは生まれた瞬間に決定し、年齢とは関係ないと仮定する。一方で、年齢と幸福が結婚(M)に影響を与えるとする。
DAGは以下の通り。
<-
dag_coords tibble(name = c("H", "M", "A"),
x = c(1, 2, 3),
y = c(2, 1, 2))
<- dagify(M ~ H,
dag ~ A,
M coords = dag_coords)
gg_simple_dag(dag)
シミュレーションをする。
<- sim_happiness(seed = 1977, N_years = 1000)
d5
posterior_summary(d5) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 3 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 age 33 18.8 2 64
## 2 married 0.3 0.46 0 1
## 3 happiness 0 1.21 -2 2
%>%
d5 ggplot(aes(x = age, y = happiness, fill= as.factor(married)))+
geom_point(size=2.3, shape=21, color = "black") +
scale_fill_manual(values = c("white","blue"))+
theme_bw()+
theme(legend.position = "none")
まず、以下のモデルを考え、結婚状態をコントロールうえで年齢が幸福度に与える影響を考える。
\(\mu_{i} = \alpha_{MID[i]} + \beta_{A}A_{i}\)
%>%
d5 filter(age >17) %>%
mutate(A = (age-18)/(65-18)) %>%
mutate(M = factor(married + 1, labels = c("single", "married"))) -> d6
モデルを回すと、年齢が幸福と負に関連していることになる。
.4 <-
b6brm(data = d6,
family = gaussian,
formula = happiness ~0 + M + A,
prior = c(prior(normal(0,1),class = b,
coef=Mmarried),
prior(normal(0,1),class = b,
coef = Msingle),
prior(normal(0,2),class = b,
coef = A),
prior(exponential(1), class = sigma)),
iter = 2000, warmup=1000,
backend = "cmdstanr",
file = "output/Chapter6/b6.4")
posterior_summary(b6.4) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Msingle -0.24 0.06 -0.36 -0.11
## 2 b_Mmarried 1.26 0.09 1.09 1.43
## 3 b_A -0.75 0.12 -0.97 -0.53
## 4 sigma 0.99 0.02 0.95 1.04
## 5 lprior -5.34 0.12 -5.6 -5.12
## 6 lp__ -1360. 1.43 -1364. -1359.
一方で、年齢のみを説明変数に入れると、年齢の影響はなくなる。
つまり、結婚の有無を変数に入れたことによって年齢と幸福度の間に疑似相関が生じていた。
.4_2 <-
b6brm(data = d6,
family = gaussian,
formula = happiness ~1 + A,
prior = c(prior(normal(0,1),class = b),
prior(normal(0,2),class = Intercept),
prior(exponential(1), class = sigma)),
iter = 2000, warmup=1000,
backend = "cmdstanr",
file = "output/Chapter6/b6.4_2")
posterior_summary(b6.4_2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 5 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.08 -0.15 0.15
## 2 b_A 0 0.13 -0.26 0.25
## 3 sigma 1.22 0.03 1.16 1.27
## 4 lprior -3.76 0.03 -3.82 -3.7
## 5 lp__ -1553. 1.23 -1556. -1552.
6.3.2 The haunted DAG
以下の例を考える。
両親(P)と祖父母(G)が子どもの学業成績(C)に直接影響を与えているが、GはPを通してもCに影響を与えているとする。
また、未観測の変数(U)がPとCに影響を与えているとする。
<-
dag_coords tibble(name = c("G", "P", "C", "U"),
x = c(1, 2, 2, 2.5),
y = c(2, 2, 1, 1.5))
<-
dag dagify(C ~ G + P + U,
~ G + U,
P coords = dag_coords)
gg_fancy_dag(dag, x=2.5,y=1.5,circle = "U")
以下のようにシミュレーションする。
ここで、GがCに直接与える影響は0と仮定する。
<- 200
n
<- 0
b_GC <- 1
b_GP <- 1
b_PC <- 2
b_U
set.seed(1)
<- tibble(
d7 U = 2*rbern(n, 0.5)-1,
G = rnorm(n),
P = rnorm(n, b_GP*G + b_U*U,sd=1),
C = rnorm(n, b_PC*P + b_U*U + b_GC*G,sd=1)
)
head(d7)
## # A tibble: 6 × 4
## U G P C
## <dbl> <dbl> <dbl> <dbl>
## 1 -1 -0.620 -1.73 -3.65
## 2 -1 0.0421 -3.01 -5.30
## 3 1 -0.911 3.06 3.88
## 4 1 0.158 1.77 3.79
## 5 -1 -0.655 -1.00 -2.01
## 6 1 1.77 5.28 8.87
ggpairs(d7)
C ~ G + Pのモデリングを行うと…。
Pの影響が2倍近くになっており、Gが負の影響を与えているという結果になってしまっている。
.5 <-
b6brm(data = d7,
family = gaussian,
formula = C ~ 0 + Intercept+P+G,
prior = c(prior(normal(0,1),class = b),
prior(exponential(1),class=sigma)),
backend = "cmdstanr",
seed =6, file = "output/Chapter6/b6.5")
posterior_summary(b6.5) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept -0.12 0.1 -0.32 0.07
## 2 b_P 1.79 0.04 1.7 1.87
## 3 b_G -0.84 0.11 -1.06 -0.62
## 4 sigma 1.43 0.07 1.29 1.58
## 5 lprior -6.15 0.16 -6.47 -5.85
## 6 lp__ -361. 1.43 -365. -359.
これは、Pを固定(例えば40-60%のものにデータを限定)してみるとよくわかる。
Pを固定したとき、Gが低ければUは高くなるし、Gが高ければUは低くなる(U -> P <- Gなので)。そして、Uが高いほどはCも高いので、結果的にGが低いほどCも高いという結果が得られることになる。
%>%
d7 mutate(C = standardize(C), G = standardize(G)) %>%
mutate(P40_60 = ifelse(P < quantile(P,probs = .6)& P > quantile(P, probs = .4), TRUE,FALSE)) %>%
ggplot(aes(x=G, y=C))+
geom_point(aes(shape = as.factor(P40_60),
color = as.factor(U)),
size=3.5, stroke=3/4)+
scale_color_manual(values =
c("black","navy"))+
scale_shape_manual(values =c(1,19))+
geom_smooth(data = . %>% filter(P40_60=="TRUE"),
method = "lm", se = FALSE, fullrange=T,
color = "black", size=1/2)+
labs(x="granpa education (G)",
y = "grandchild eduaction (C)",
title = "Parents in 45th and 60th centiles")+
theme(aspect.ratio=1,
legend.position = "none")+
annotate(geom="text", x=-2,y=2,label="U = 1",
color = "navy")+
annotate(geom="text", x=2,y=-2,label="U = -1",
color = "black")
ここで、Uが観測できたとしてモデリングを行うと、Gの効果はなくなり、Pの効果も正しく推定できるようになる。
.5_2 <-
b6brm(data = d7,
family = gaussian,
formula = C ~ 0 + Intercept+P+G+U,
prior = c(prior(normal(0,1),class = b),
prior(exponential(1),class=sigma)),
backend = "cmdstanr",
seed =6, file = "output/Chapter6/b6.5_2")
posterior_summary(b6.5_2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 7 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept -0.12 0.07 -0.26 0.02
## 2 b_P 1.02 0.07 0.88 1.14
## 3 b_G -0.05 0.1 -0.24 0.15
## 4 b_U 1.98 0.15 1.69 2.27
## 5 sigma 1.04 0.05 0.94 1.14
## 6 lprior -7.23 0.24 -7.74 -6.8
## 7 lp__ -298. 1.61 -302. -296.
6.4 Confronting confoundings
上記のように、モデルに入れる変数を身長に選ばなければ交絡が生じてしまう。
ゆえに、バックドア基準を満たすような変数を入れることが肝要である。
因果モデルはどんなに複雑であろうと、以下の4つの関係の組み合わせからなる。
The fork
Zで条件づけられれば、XとYは独立になる。
The Pipe
Zで条件づけるとXからYへのパスがふさがれてしまう。
The Collider
Zで条件づけると、XとYの間に疑似相関が生じてしまう。
The Descendant
Dで条件づけるとZも部分的に条件づけられてしまうため、XとYの間に疑似相関が生じてしまう。
6.5 Practice
6.5.1 6M1
Modify the DAG on page 186 to include the variable V, an unobserved cause of C and Y: C←V→Y. Reanalyze the DAG. How many paths connect X to Y? Which must be closed? Which variables should you condition on now?
<- dagitty("dag { U [unobserved]
new_dag V [unobserved]
X -> Y
X <- U <- A -> C -> Y
U -> B <- C
C <- V -> Y }")
adjustmentSets(new_dag, exposure = "X", outcome = "Y")
## { A }
6.5.2 6M2
Sometimes, in order to avoid multicollinearity, people inspect pairwise correlations among predictors before including them in a model. This is a bad procedure, because what matters is the conditional association, not the association before the variables are included in the model. To highlight this, consider the DAG X→Z→Y. Simulate data from this DAG so that the correlation between X and Z is very large. Then include both in a model prediction Y . Do you observe any multicollinearity? Why or why not? What is different from the legs example in the chapter?
set.seed(1984)
<- 100
n <- tibble(x = rnorm(n,0,1)) %>%
dat mutate(z = rnorm(n,x,0.4),
y = rnorm(n,z,1))
cor(dat)
## x z y
## x 1.0000000 0.9325230 0.7431466
## z 0.9325230 1.0000000 0.8063858
## y 0.7431466 0.8063858 1.0000000
pairs(dat)
上手く推定できてると思われる。
<-
b6M2 brm(
data = dat,
family = gaussian,
formula = y ~ 1 + z + x,
prior = c(prior(normal(0,0.2),class = Intercept),
prior(normal(0,0.5),class =b),
prior(exponential(1),class=sigma)),
iter=4000, warmup=2000, chains =4, seed=1234,
backend = "cmdstanr",
file = "output/Chapter6/b6M2"
)
posterior_summary(b6M2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept -0.12 0.09 -0.29 0.06
## 2 b_z 1.08 0.21 0.66 1.49
## 3 b_x 0.14 0.23 -0.31 0.6
## 4 sigma 0.96 0.07 0.84 1.11
## 5 lprior -3.54 0.86 -5.6 -2.29
## 6 lp__ -142. 1.45 -145. -140.
posterior_samples(b6M2) %>%
as_tibble() %>%
::select(-lp__) %>%
dplyrpivot_longer(everything()) %>%
ggplot(aes(x = value, y = name)) +
stat_halfeye(.width = c(0.67, 0.89, 0.97))
pairs(b6M2)
繰り返してみる。
推定結果も両脚と身長の例ほどはばらついていない。
<- function(seed, n = 100) {
sim_and_fit <- n
n set.seed(seed)
<- tibble(x = rnorm(n,0,1)) %>%
dat mutate(z = rnorm(n,x,0.4),
y = rnorm(n,z,1))
<- update(b6M2, newdata = dat)
fit
}
#sim2 <-
# tibble(seed = 1:4) %>%
#mutate(post = purrr::map(seed, ~sim_and_fit(.) %>%
# posterior_samples()))
<- readRDS("output/Chapter6/sim2.rds")
sim2
%>%
sim2 unnest(post) %>%
pivot_longer(b_Intercept:sigma) %>%
mutate(seed = str_c("seed ", seed)) %>%
ggplot(aes(x = value, y = name)) +
stat_pointinterval(.width = .95, color = "forestgreen") +
labs(x = "posterior", y = NULL) +
theme(axis.text.y = element_text(hjust = 0),
panel.border = element_rect(color = "black", fill = "transparent"),
panel.grid.minor = element_blank(),
strip.text = element_text(hjust = 0)) +
facet_wrap(~ seed, ncol = 1)
これは、身長の例では両脚(L,R)がどちらも身長(H)を予測するのに対して、今回の例ではZのみがYを予測し、XからYへのパスはブロックされているから。
このように、多重共線性が生じるかは単純に変数間の相関だけではわからず、因果モデルを考える必要がある。
6.5.3 6H1
Use the Waffle House data,
data(WaffleDivorce)
, to find the total causal influence of number of Waffle Houses on divorce rate. Justify your model or models with a causal graph.
data("WaffleDivorce")
<- WaffleDivorce %>%
dat2 mutate(M = standardize(Marriage),
A = standardize(MedianAgeMarriage),
D = standardize(Divorce),
S = factor(South + 1, labels = c("North", "South")),
W = standardize(WaffleHouses))
Sを入れてモデリング。
Wはほとんど影響していないことが分かる。
<-
b6H1 brm(data = dat2,
family = gaussian,
formula = D ~ 0 + W + S,
prior = c(prior(normal(0,0.5),class =b),
prior(exponential(1),class = sigma)),
iter=4000, warmup=2000,
backend = "cmdstanr",
file = "output/Chapter6/b6H1")
posterior_summary(b6H1) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_W 0.09 0.16 -0.23 0.41
## 2 b_SNorth -0.16 0.17 -0.49 0.17
## 3 b_SSouth 0.36 0.27 -0.17 0.88
## 4 sigma 0.97 0.1 0.79 1.19
## 5 lprior -2.22 0.47 -3.48 -1.67
## 6 lp__ -71.4 1.42 -75.0 -69.6
6.5.4 6H2
Build a series of models to test the implied conditional independencies of the causal graph you used in the previous problem. If any of the tests fail, how do you think the graph needs to be ammended? Does the graph need more or fewer arrows? Feel free to nominate variables that aren’t in the data.
DAGの条件付き独立がどのような時に起こるかを調べてみる。 以下の3つをモデリングしてみる。
<-
dag_coords tibble(name = c("A", "D", "M", "S", "W"),
x = c(1, 3, 2, 1, 3),
y = c(1, 1, 2, 3, 3))
<-
waffle_dag dagify(A ~ S,
~ A + M + W,
D ~ A + S,
M ~ S,
W coords = dag_coords)
impliedConditionalIndependencies((waffle_dag))
## A _||_ W | S
## D _||_ S | A, M, W
## M _||_ W | S
モデルの結果、1と3については条件付き独立を支持する結果が得られたた。
2については信用区間が0にまたがっているが若干あいまいな結果だった。これはおそらくまだ他の要因が関わっているからだと思われる。
<- brm(data = dat2,
b6H2_1 family = gaussian,
formula = A ~ 1 + W + South,
prior = c(prior(normal(0,0.2),class = Intercept),
prior(normal(0,0.5),class =b),
prior(exponential(1),class = sigma)),
iter=4000, warmup=2000,
backend = "cmdstanr",
file = "output/Chapter6/b6H2_1")
<- brm(data = dat2,
b6H2_2 family = gaussian,
formula = D ~ 1 + A + M + W + South,
prior = c(prior(normal(0,0.2),class = Intercept),
prior(normal(0,0.5),class =b),
prior(exponential(1),class = sigma)),
iter=4000, warmup=2000,
backend = "cmdstanr",
file = "output/Chapter6/b6H2_2")
<- brm(data = dat2,
b6H2_3 family = gaussian,
formula = M ~ 1 + W + South,
prior = c(prior(normal(0,0.2),class = Intercept),
prior(normal(0,0.5),class =b),
prior(exponential(1),class = sigma)),
iter=4000, warmup=2000,
backend = "cmdstanr",
file = "output/Chapter6/b6H2_3")
posterior_summary(b6H2_1) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0.11 0.15 -0.17 0.4
## 2 b_W 0.01 0.17 -0.33 0.33
## 3 b_South -0.4 0.32 -1.02 0.25
## 4 sigma 1 0.1 0.82 1.23
## 5 lprior -1.51 0.66 -3.22 -0.75
## 6 lp__ -72.1 1.44 -75.7 -70.3
posterior_summary(b6H2_2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 8 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept -0.07 0.13 -0.32 0.18
## 2 b_A -0.55 0.16 -0.86 -0.23
## 3 b_M -0.04 0.16 -0.34 0.27
## 4 b_W 0.11 0.14 -0.17 0.38
## 5 b_South 0.23 0.29 -0.33 0.8
## 6 sigma 0.81 0.09 0.66 1
## 7 lprior -2.21 0.54 -3.53 -1.43
## 8 lp__ -62.7 1.81 -67.2 -60.2
posterior_summary(b6H2_3) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept -0.04 0.15 -0.33 0.25
## 2 b_W -0.02 0.17 -0.35 0.31
## 3 b_South 0.15 0.32 -0.48 0.76
## 4 sigma 1.02 0.11 0.84 1.26
## 5 lprior -1.27 0.48 -2.49 -0.72
## 6 lp__ -73.1 1.44 -76.8 -71.3
6.5.5 6H3
Use a model to infer the total causal influence of area on weight. Would increasing the area available to each fox make it heavier (healthier)? You might want to standardize the variables. Regardless, use prior predictive simulation to show that your model’s prior predictions stay within the possible outcome range.
areaとweightは他の変数を条件づけなくてもバックドア基準を満たすため、どの変数も入れなくてよい。
<- dagitty(
dag_fox "dag{
area -> avgfood -> weight <- groupsize ;
avgfood -> groupsize
}"
)
adjustmentSets(dag_fox, exposure = "area", outcome ="weight")
## {}
data(foxes)
<- foxes %>%
dat3 as_tibble() %>%
::select(area, avgfood, weight, groupsize) %>%
dplyrmutate(across(everything(),standardize))
head(dat3)
## # A tibble: 6 × 4
## area avgfood weight groupsize
## <dw_trnsf> <dw_trnsf> <dw_trnsf> <dw_trnsf>
## 1 -2.239596 -1.924829 0.4141347 -1.524089
## 2 -2.239596 -1.924829 -1.4270464 -1.524089
## 3 -1.205508 -1.118035 0.6759540 -1.524089
## 4 -1.205508 -1.118035 1.3009421 -1.524089
## 5 -1.130106 -1.319734 1.1151348 -1.524089
## 6 -1.130106 -1.319734 -1.0807692 -1.524089
pairs(dat3)
<-
b6H3 brm(data = dat3,
family = gaussian,
formula = weight ~ 1 + area,
prior = c(prior(normal(0, 0.2),class=Intercept),
prior(normal(0, 0.5), class = b,),
prior(exponential(1), class = sigma)),
iter = 4000, warmup = 2000, chains = 4, cores = 4, seed = 1234,
backend = "cmdstanr",
file = "output/Chapter6/b6H3")
areaはほとんど影響していなかった。
posterior_samples(b6H3) %>%
as_tibble() %>%
::select(-lp__) %>%
dplyrpivot_longer(everything()) %>%
mutate(name = factor(name, levels = c("b_Intercept", "b_area", "sigma"))) %>%
ggplot(aes(x = value, y = fct_rev(name))) +
stat_halfeye(.width = c(0.67, 0.89, 0.97)) +
labs(x = "Parameter Estimate", y = "Parameter")
6.5.6 6H4
Now infer the causal impact of adding food to a territory. Would this make foxes heavier? Which covariates do you need to adjust for to estimate the total causal influence of food?
続いて、えさの量が体重に与える影響を考える。
えさの量が体重に与える影響を評価するために他の変数を加える必要はない。
adjustmentSets(dag_fox, exposure = "avgfood", outcome ="weight")
## {}
えさの量も影響せず。
<-
b6H4 brm(data = dat3,
family = gaussian,
formula = weight ~ 1 + avgfood,
prior = c(prior(normal(0, 0.2),class=Intercept),
prior(normal(0, 0.5), class = b,),
prior(exponential(1), class = sigma)),
iter = 4000, warmup = 2000, chains = 4, cores = 4, seed = 1234,
backend = "cmdstanr",
file = "output/Chapter6/b6H4")
posterior_samples(b6H4) %>%
as_tibble() %>%
::select(-lp__) %>%
dplyrpivot_longer(everything()) %>%
mutate(name = factor(name, levels = c("b_Intercept", "b_avgfood", "sigma"))) %>%
ggplot(aes(x = value, y = fct_rev(name))) +
stat_halfeye(.width = c(0.67, 0.89, 0.97)) +
labs(x = "Parameter Estimate", y = "Parameter")
6.5.7 6H5
Now infer the causal impact of group size. Which covariates do you need to adjust for? Looking at the posterior distribution of the resulting model, what do you think explains these data? That is, can you explain the estimates for all three problems? How do they go together?
最後にgroupsizeの影響を見る。その際には、avgfoodも説明変数に入れる必要がある。
その結果、groupsizeはavgfoodを固定したときに負の影響を与えていることが分かった。また、avgfoodはgroupsizeを固定したときに正の影響を与えることが分かった。
ただし、avgfoodがweightに与える全体的な影響は前問で見たように0である。これは、avgfoodが増えればgroupsizeも増えてしまうからである。
<-
b6H5 brm(data = dat3,
family = gaussian,
formula = weight ~ 1 + avgfood + groupsize,
prior = c(prior(normal(0, 0.2),class=Intercept),
prior(normal(0, 0.5), class = b,),
prior(exponential(1), class = sigma)),
iter = 4000, warmup = 2000, chains = 4, cores = 4, seed = 1234,
file = "output/Chapter6/b6H5")
posterior_samples(b6H5) %>%
as_tibble() %>%
::select(-lp__) %>%
dplyrpivot_longer(everything()) %>%
mutate(name = factor(name, levels = c("b_Intercept", "b_avgfood","b_groupsize", "sigma"))) %>%
ggplot(aes(x = value, y = fct_rev(name))) +
stat_halfeye(.width = c(0.67, 0.89, 0.97)) +
labs(x = "Parameter Estimate", y = "Parameter")
posterior_summary(b6H5) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 6 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.08 -0.16 0.16
## 2 b_avgfood 0.47 0.18 0.1 0.82
## 3 b_groupsize -0.56 0.18 -0.91 -0.2
## 4 sigma 0.96 0.07 0.85 1.1
## 5 lprior -2.01 0.74 -3.75 -0.92
## 6 lp__ -162. 1.41 -166. -160.
6.6 おまけ ランダム効果を入れてみる
<- foxes %>%
dat3 as_tibble() %>%
mutate(across(2:5,standardize))
head(dat3)
## # A tibble: 6 × 5
## group avgfood groupsize area weight
## <int> <dw_trnsf> <dw_trnsf> <dw_trnsf> <dw_trnsf>
## 1 1 -1.924829 -1.524089 -2.239596 0.4141347
## 2 1 -1.924829 -1.524089 -2.239596 -1.4270464
## 3 2 -1.118035 -1.524089 -1.205508 0.6759540
## 4 2 -1.118035 -1.524089 -1.205508 1.3009421
## 5 3 -1.319734 -1.524089 -1.130106 1.1151348
## 6 3 -1.319734 -1.524089 -1.130106 -1.0807692
<-
b6H5_2 brm(data = dat3,
family = gaussian,
formula = weight ~ 1 + avgfood + groupsize + (1|group),
prior = c(prior(normal(0, 0.2),class=Intercept),
prior(normal(0, 0.5), class = b,),
prior(exponential(1), class = sigma)),
iter = 4000, warmup = 2000, chains = 4, cores = 4, seed = 1234,
backend = "cmdstanr",
file = "output/Chapter6/b6H5_2")
posterior_summary(b6H5_2) %>%
round(2) %>%
data.frame() %>%
rownames_to_column(var = "parameter") %>%
as_tibble()
## # A tibble: 37 × 5
## parameter Estimate Est.Error Q2.5 Q97.5
## <chr> <dbl> <dbl> <dbl> <dbl>
## 1 b_Intercept 0 0.09 -0.17 0.18
## 2 b_avgfood 0.46 0.2 0.07 0.84
## 3 b_groupsize -0.55 0.2 -0.93 -0.16
## 4 sd_group__Intercept 0.18 0.12 0.01 0.44
## 5 sigma 0.95 0.07 0.83 1.09
## 6 r_group[1,Intercept] -0.04 0.2 -0.5 0.35
## 7 r_group[2,Intercept] 0.06 0.2 -0.29 0.53
## 8 r_group[3,Intercept] -0.02 0.2 -0.48 0.38
## 9 r_group[4,Intercept] -0.02 0.19 -0.46 0.38
## 10 r_group[5,Intercept] 0.08 0.2 -0.27 0.59
## # … with 27 more rows