I’m trying to simulate a nested dataset for a trial of a family intervention where families are assigned to treatment or an inactive control. Data will be collected from several eligible members in each family. Thus, there will be nesting of the form:
family members (d) < families (c) < groups (b) < group facilitators (a)
I’m not sure I have my terminology correct, but I believe this is partial nesting. Each lower level unit is in one and only one higher level unit, but families (c) in the control arm won’t have groups (b) or group facilitators (a).
I’m unsure about two things:
- How to simulate and set the random intercepts for all random effects.
- How to specify the
brms()
model to account for this partial nesting.
1. How to simulate and set the random intercepts for all random effects
Here is my attempt at setting up the data structure:
library(faux)
library(tidyverse)
library(brms)
set.seed(1)
b0 <- 2
b1 <- 0.2
df1 <- add_random(a = 3) %>% # 3 facilitators
add_random(b = 2, .nested_in = "a") %>% # 2 groups per facilitator
add_random(c = 3, .nested_in = "b") %>% # 3 families per group
add_random(d = 3, .nested_in = "c") %>% # 3 members per family
add_within("d", time = c("pre", "post")) %>%
add_between(.by = "b",
arm = c("treatment", "control")) %>%
# add dummy
add_recode("arm", "treatment", control = 0, treatment = 1) %>%
add_recode("time", "post", pre = 0, post = 1) %>%
select(-arm) %>%
# add random intercepts
add_ranef("a", u0a = 0.01) %>%
add_ranef("b", u0b = 0.05) %>%
add_ranef("c", u0c = 0.1) %>%
add_ranef("d", u0d = 0.2) %>%
add_ranef(sigma = 0.4) %>%
# calculate DV
mutate(dv = b0 + u0a + u0b + u0c + u0d +
(b1 * treatment * post) + sigma) %>%
select(-post)
This yields:
df1
# A tibble: 108 × 12
a b c d time treatment u0a u0b u0c u0d sigma dv
<chr> <chr> <chr> <chr> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 a1 b1 c01 d01 pre 1 -0.00626 0.0798 -0.0305 -0.294 -0.0541 1.69
2 a1 b1 c01 d01 post 1 -0.00626 0.0798 -0.0305 -0.294 0.471 2.42
3 a1 b1 c01 d02 pre 1 -0.00626 0.0798 -0.0305 -0.0956 -0.609 1.34
4 a1 b1 c01 d02 post 1 -0.00626 0.0798 -0.0305 -0.0956 0.238 2.38
5 a1 b1 c01 d03 pre 1 -0.00626 0.0798 -0.0305 0.0836 0.133 2.26
6 a1 b1 c01 d03 post 1 -0.00626 0.0798 -0.0305 0.0836 0.425 2.75
7 a1 b1 c02 d04 pre 1 -0.00626 0.0798 0.151 0.272 -0.122 2.37
8 a1 b1 c02 d04 post 1 -0.00626 0.0798 0.151 0.272 0.148 2.84
9 a1 b1 c02 d05 pre 1 -0.00626 0.0798 0.151 -0.0206 0.107 2.31
10 a1 b1 c02 d05 post 1 -0.00626 0.0798 0.151 -0.0206 -0.217 2.19
# … with 98 more rows
(I don’t think I’m simulating the pre data correctly)
Let’s look at family c04
:
df1 %>% filter(c=="c04")
# A tibble: 6 × 12
a b c d time treatment u0a u0b u0c u0d sigma dv
<chr> <chr> <chr> <chr> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 a1 b2 c04 d10 pre 0 -0.00626 0.0165 -0.0621 -0.0789 -0.189 1.68
2 a1 b2 c04 d10 post 0 -0.00626 0.0165 -0.0621 -0.0789 -0.248 1.62
3 a1 b2 c04 d11 pre 0 -0.00626 0.0165 -0.0621 -0.0119 0.0168 1.95
4 a1 b2 c04 d11 post 0 -0.00626 0.0165 -0.0621 -0.0119 -0.364 1.57
5 a1 b2 c04 d12 pre 0 -0.00626 0.0165 -0.0621 0.220 0.0632 2.23
6 a1 b2 c04 d12 post 0 -0.00626 0.0165 -0.0621 0.220 -0.262 1.91
This family is assigned to the control arm, so they are not part of any group (b) and therefore are not led by any group facilitator (a). Would I just set u0a
and u0b
to 0
where treatment==0
? Relatedly, would I do the same for everyone where time=="pre"
since there is no nesting by group/facilitator for the baseline round of data collection?
2. How to specify the brms()
model to account for this partial (?) nesting
Assume for the moment that I simulated the data correctly. We can reshape it for analysis as follows:
df2 <- df1 %>%
pivot_wider(id_cols = c(a, b, c, d, treatment),
names_from = time,
values_from = dv) %>%
rename(y_pre = pre,
y_post = post)
Which gives us:
# A tibble: 54 × 7
a b c d treatment y_pre y_post
<chr> <chr> <chr> <chr> <dbl> <dbl> <dbl>
1 a1 b1 c01 d01 1 1.69 2.42
2 a1 b1 c01 d02 1 1.34 2.38
3 a1 b1 c01 d03 1 2.26 2.75
4 a1 b1 c02 d04 1 2.37 2.84
5 a1 b1 c02 d05 1 2.31 2.19
6 a1 b1 c02 d06 1 2.79 2.97
7 a1 b1 c03 d07 1 2.38 2.94
8 a1 b1 c03 d08 1 2.06 1.53
9 a1 b1 c03 d09 1 1.80 1.74
10 a1 b2 c04 d10 0 1.68 1.62
# … with 44 more rows
This is how I’ve specified the nesting, but I’m not certain it’s correct since the families in the control arm are not nested in groups or facilitators.
brm(y_post ~ 0 + Intercept + treatment + y_pre + (1 | a:b:c),
data = df2,
control = list(adapt_delta = 0.9),
cores = parallel::detectCores())
Group-Level Effects:
~a:b:c (Number of levels: 18)
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept) 0.08 0.06 0.00 0.24 1.00 1527 1297
Population-Level Effects:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept 1.76 0.32 1.14 2.36 1.00 2521 2691
treatment 0.27 0.13 0.02 0.53 1.00 4034 3059
y_pre 0.12 0.15 -0.16 0.40 1.00 2675 2711
Family Specific Parameters:
Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma 0.42 0.04 0.35 0.52 1.00 4477 2410
Info:
R version 4.0.2 (2020-06-22)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Catalina 10.15.7
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.0/Resources/lib/libRlapack.dylib
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
attached base packages:
[1] stats graphics grDevices utils datasets methods
[7] base
other attached packages:
[1] sjPlot_2.8.9 tidybayes_3.0.2 cmdstanr_0.2.2
[4] brms_2.16.3