From 5fd4c698f9503081eb4160fadfd7f824dbb20f99 Mon Sep 17 00:00:00 2001 From: "Joshua F. Wiley" Date: Fri, 24 Jan 2025 15:54:59 +1100 Subject: [PATCH] remove macos due to errors, move to light-switch, updated vignette runs --- .github/workflows/R-CMD-check.yaml | 1 - .github/workflows/pkgdown.yaml | 6 - _pkgdown.yml | 2 +- vignettes/fixed-effects-marginaleffects.Rmd | 190 ++++++----- vignettes/location-scale-marginaleffects.Rmd | 156 +++++---- vignettes/mixed-effects-marginaleffects.Rmd | 313 ++++++++----------- 6 files changed, 338 insertions(+), 330 deletions(-) diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0f13fc4..73fba99 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -21,7 +21,6 @@ jobs: matrix: config: - {os: windows-latest, r: 'release'} - - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} env: diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index b193812..19702a4 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -37,12 +37,6 @@ jobs: extra-packages: any::pkgdown, local::. needs: website - - name: Install dependencies - run: | - install.packages("remotes") - remotes::install_github("amirmasoudabdol/preferably", type = "source") - shell: Rscript {0} - - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} diff --git a/_pkgdown.yml b/_pkgdown.yml index 863d5be..44fe092 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -24,8 +24,8 @@ footer: right: built_with template: - package: preferably bootstrap: 5 + light-switch: true opengraph: twitter: diff --git a/vignettes/fixed-effects-marginaleffects.Rmd b/vignettes/fixed-effects-marginaleffects.Rmd index 24c3f4b..d7fcecc 100644 --- a/vignettes/fixed-effects-marginaleffects.Rmd +++ b/vignettes/fixed-effects-marginaleffects.Rmd @@ -16,17 +16,18 @@ vignette: > -```r +``` r library(knitr) library(data.table) -#> data.table 1.14.2 using 24 threads (see ?getDTthreads). Latest news: r-datatable.com +#> data.table 1.16.4 using 16 threads (see ?getDTthreads). Latest news: r-datatable.com library(brms) #> Loading required package: Rcpp -#> Loading 'brms' package (version 2.17.0). Useful instructions +#> Loading 'brms' package (version 2.22.0). 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:stats': #> #> ar @@ -52,7 +53,7 @@ can look at a linear regression model of the association between for `mpg`. -```r +``` r m.linear <- lm(hp ~ am + mpg, data = mtcars) coef(m.linear)["mpg"] @@ -68,7 +69,7 @@ predicted difference in the outcome for a one unit difference in `mpg` from 0 to 1, holding `am = 0`. -```r +``` r yhat <- predict( m.linear, newdata = data.frame(am = 0, mpg = c(0, 1)), @@ -83,7 +84,7 @@ We can look at the same estimate but moving `mpg` from 10 to 11 instead 0 to 1, holding `am = 1`. -```r +``` r yhat <- predict( m.linear, newdata = data.frame(am = 1, mpg = c(10, 11)), @@ -106,7 +107,7 @@ probability scale. This is not convenient for interpretation, as the log odds scale is not the same scale as our outcome. -```r +``` r m.logistic <- glm(vs ~ am + mpg, data = mtcars, family = binomial()) coef(m.logistic)["mpg"] @@ -118,7 +119,7 @@ We can find predicted differences on the probability scale. Here moving `mpg` from 10 to 11 holding `am = 0`. -```r +``` r yhat <- predict( m.logistic, newdata = data.frame(am = 0, mpg = c(10, 11)), @@ -133,7 +134,7 @@ We can look at the same estimate but moving `mpg` from 20 to 21 instead 10 to 11 again holding `am = 0`. -```r +``` r yhat <- predict( m.logistic, newdata = data.frame(am = 0, mpg = c(20, 21)), @@ -148,7 +149,7 @@ We can look at the same estimate moving `mpg` from 20 to 21 as before, but this time holding `am = 1`. -```r +``` r yhat <- predict( m.logistic, newdata = data.frame(am = 1, mpg = c(20, 21)), @@ -182,7 +183,7 @@ on the probability scale for a one unit shift in the predictor, `mpg`, for each person. When we average all of these, we get the AME. -```r +``` r h <- .001 nd.1 <- nd.0 <- model.frame(m.logistic) @@ -208,7 +209,7 @@ predicted probabilities if everyone had `am = 0` and then again if everyone had `am = 1`. -```r +``` r nd.1 <- nd.0 <- model.frame(m.logistic) nd.0$am <- 0 nd.1$am <- 1 @@ -240,17 +241,28 @@ example calculating AMEs for `mpg` and `am`. First we will fit the same logistic regression model using `brms`. -```r +``` r bayes.logistic <- brm( vs ~ am + mpg, data = mtcars, family = "bernoulli", seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... +#> Loading required package: rstan +#> Loading required package: StanHeaders +#> +#> rstan version 2.32.6 (Stan version 2.32.2) +#> For execution on a local, multicore CPU with excess RAM we recommend calling +#> options(mc.cores = parallel::detectCores()). +#> To avoid recompilation of unchanged Stan programs, we recommend calling +#> rstan_options(auto_write = TRUE) +#> For within-chain threading using `reduce_sum()` or `map_rect()` Stan functions, +#> change `threads_per_chain` option: +#> rstan_options(threads_per_chain = 1) +#> Do not specify '-march=native' in 'LOCAL_CPPFLAGS' or a Makevars file ``` -```r +``` r summary(bayes.logistic) #> Family: bernoulli #> Links: mu = logit @@ -259,11 +271,11 @@ summary(bayes.logistic) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -16.03 5.49 -28.79 -7.15 1.00 1797 1897 -#> am -3.77 1.82 -7.87 -0.71 1.00 1689 1766 -#> mpg 0.86 0.30 0.38 1.57 1.00 1707 1842 +#> Intercept -16.12 5.67 -28.84 -7.42 1.00 1379 1959 +#> am -3.83 1.86 -7.97 -0.70 1.00 1568 1772 +#> mpg 0.87 0.31 0.40 1.56 1.00 1328 1711 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -282,7 +294,7 @@ summary of the posterior for the contrasts. Here we just have the one contrast, but multiple could have been specified. -```r +``` r h <- .001 ame1 <- brmsmargins( bayes.logistic, @@ -297,7 +309,9 @@ kable(ame1$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |-----:|----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-------| -| 0.071| 0.07| 0.054| 0.091| NA| NA| 0.95|HDI |NA |NA |AME MPG | +| 0.071| 0.07| 0.054| 0.092| NA| NA| 0.95|HDI |NA |NA |AME MPG | + + Now we can look at how we could calculate a discrete AME. This time we use the `at` argument instead of the `add` @@ -308,7 +322,7 @@ we also look at the summary of the posterior for the predictions. These predictions average across all values of `mpg`. -```r +``` r ame2 <- brmsmargins( bayes.logistic, at = data.frame(am = c(0, 1)), @@ -322,19 +336,23 @@ kable(ame2$Summary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---| -| 0.543| 0.544| 0.419| 0.653| NA| NA| 0.95|HDI |NA |NA | -| 0.284| 0.277| 0.180| 0.409| NA| NA| 0.95|HDI |NA |NA | +| 0.544| 0.547| 0.430| 0.662| NA| NA| 0.95|HDI |NA |NA | +| 0.283| 0.277| 0.175| 0.408| NA| NA| 0.95|HDI |NA |NA | -```r + + +``` r kable(ame2$ContrastSummary) ``` -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|---------:|----------:|----------:|----------:|-----------:|----------:|----:|:------|:----|:---|:------| -| -0.258856| -0.2648354| -0.4252779| -0.0862889| NA| NA| 0.95|HDI |NA |NA |AME am | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|----------:|----------:|----------:|----------:|-----------:|----------:|----:|:------|:----|:---|:------| +| -0.2607899| -0.2649507| -0.4289198| -0.0891496| NA| NA| 0.95|HDI |NA |NA |AME am | + + Note that by default, `brmsmargins()` uses the model frame from the model object as the dataset. This, however, can be overridden. @@ -350,7 +368,7 @@ count outcomes. We use a dataset drawn from: https://stats.oarc.ucla.edu/r/dae/poisson-regression/ -```r +``` r d <- fread("https://stats.oarc.ucla.edu/stat/data/poisson_sim.csv") d[, prog := factor(prog, levels = 1:3, labels = c("General", "Academic", "Vocational"))] @@ -360,11 +378,10 @@ bayes.poisson <- brm( family = "poisson", seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... ``` -```r +``` r summary(bayes.poisson) #> Family: poisson #> Links: mu = log @@ -373,12 +390,12 @@ summary(bayes.poisson) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -5.31 0.66 -6.66 -4.03 1.00 1933 2312 -#> progAcademic 1.15 0.38 0.46 1.97 1.00 2088 1947 -#> progVocational 0.39 0.47 -0.48 1.36 1.00 2174 2024 -#> math 0.07 0.01 0.05 0.09 1.00 2592 2120 +#> Intercept -5.32 0.68 -6.68 -4.01 1.00 2444 2292 +#> progAcademic 1.15 0.38 0.48 1.92 1.00 1949 1571 +#> progVocational 0.39 0.47 -0.48 1.34 1.00 1808 1427 +#> math 0.07 0.01 0.05 0.09 1.00 2729 2349 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -388,7 +405,7 @@ summary(bayes.poisson) AME for a continuous variable, using default CI interval and type. -```r +``` r h <- .001 ame1.p <- brmsmargins( bayes.poisson, @@ -402,13 +419,15 @@ kable(ame1.p$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:--------| -| 0.044| 0.044| 0.026| 0.065| NA| NA| 0.99|HDI |NA |NA |AME math | +| 0.044| 0.044| 0.025| 0.065| NA| NA| 0.99|HDI |NA |NA |AME math | + + AME for a categorical variable. Here we calculate pairwise contrasts for all three program types. These are the predicted number of awards. -```r +``` r ame2.p <- brmsmargins( bayes.poisson, at = data.frame( @@ -426,12 +445,14 @@ kable(ame2.p$Summary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---| -| 0.263| 0.253| 0.076| 0.526| NA| NA| 0.99|HDI |NA |NA | -| 0.781| 0.779| 0.600| 0.988| NA| NA| 0.99|HDI |NA |NA | -| 0.380| 0.368| 0.126| 0.710| NA| NA| 0.99|HDI |NA |NA | +| 0.262| 0.253| 0.077| 0.522| NA| NA| 0.99|HDI |NA |NA | +| 0.780| 0.776| 0.575| 0.992| NA| NA| 0.99|HDI |NA |NA | +| 0.380| 0.368| 0.158| 0.707| NA| NA| 0.99|HDI |NA |NA | -```r + + +``` r kable(ame2.p$ContrastSummary, digits = 3) ``` @@ -439,9 +460,11 @@ kable(ame2.p$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |------:|------:|------:|------:|-----------:|----------:|----:|:------|:----|:---|:-------------------------| -| -0.518| -0.524| -0.820| -0.176| NA| NA| 0.99|HDI |NA |NA |AME General v Academic | -| -0.117| -0.111| -0.503| 0.280| NA| NA| 0.99|HDI |NA |NA |AME General v Vocational | -| 0.400| 0.406| 0.019| 0.726| NA| NA| 0.99|HDI |NA |NA |AME Academic v Vocational | +| -0.518| -0.522| -0.806| -0.135| NA| NA| 0.99|HDI |NA |NA |AME General v Academic | +| -0.118| -0.115| -0.510| 0.248| NA| NA| 0.99|HDI |NA |NA |AME General v Vocational | +| 0.400| 0.409| 0.011| 0.759| NA| NA| 0.99|HDI |NA |NA |AME Academic v Vocational | + + ## AMEs for Negative Binomial Regression @@ -450,7 +473,7 @@ Here is a short example for Negative Binomial regression used for count outcomes. We use the same setup as for the Poisson regression example. -```r +``` r d <- read.csv("https://stats.oarc.ucla.edu/stat/data/poisson_sim.csv") d$prog <- factor(d$prog, levels = 1:3, labels = c("General", "Academic", "Vocational")) @@ -459,12 +482,23 @@ bayes.nb <- brm( family = "negbinomial", seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... +#> Warning: 569 of 4000 (14.0%) transitions ended with a divergence. +#> See https://mc-stan.org/misc/warnings for details. +#> Warning: 3431 of 4000 (86.0%) transitions hit the maximum treedepth limit of 10. +#> See https://mc-stan.org/misc/warnings for details. +#> Warning: 2 of 4 chains have a NaN E-BFMI. +#> See https://mc-stan.org/misc/warnings for details. ``` -```r +``` r summary(bayes.nb) +#> Warning: Parts of the model have not converged (some Rhats are > 1.05). Be +#> careful when analysing the results! We recommend running more iterations and/or +#> setting stronger priors. +#> Warning: There were 569 divergent transitions after warmup. Increasing +#> adapt_delta above 0.8 may help. See +#> http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup #> Family: negbinomial #> Links: mu = log; shape = identity #> Formula: num_awards ~ prog + math @@ -472,16 +506,20 @@ summary(bayes.nb) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -5.39 0.70 -6.82 -4.06 1.00 2813 2387 -#> progAcademic 1.14 0.38 0.42 1.89 1.00 2187 2343 -#> progVocational 0.40 0.47 -0.49 1.32 1.00 2155 2093 -#> math 0.07 0.01 0.05 0.09 1.00 3139 2500 +#> Intercept -7.43 2.17 -9.81 -5.00 Inf 4 NA +#> progAcademic 2.68 2.02 0.67 5.96 Inf 4 NA +#> progVocational -0.49 1.26 -2.66 0.42 Inf 4 NA +#> math 0.10 0.03 0.06 0.13 Inf 4 NA #> -#> Family Specific Parameters: -#> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> shape 19.82 35.82 1.91 112.92 1.00 2453 2214 +#> Further Distributional Parameters: +#> Estimate +#> shape 6464950012624176290022006848644662.00 +#> Est.Error l-95% CI +#> shape 11199021826335432527262864286824266.00 21779500000000.00 +#> u-95% CI Rhat Bulk_ESS Tail_ESS +#> shape 25859800000000002248084824408282220.00 Inf 4 NA #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -491,7 +529,7 @@ summary(bayes.nb) AME for a continuous variable, using default CI interval and type. -```r +``` r h <- .001 ame1.nb <- brmsmargins( bayes.nb, @@ -503,15 +541,17 @@ kable(ame1.nb$ContrastSummary, digits = 3) -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|----:|-----------:|----------:|----:|:------|:----|:---|:--------| -| 0.045| 0.045| 0.022| 0.07| NA| NA| 0.99|HDI |NA |NA |AME math | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:--------| +| 0.641| 0.189| 0.042| 2.145| NA| NA| 0.99|HDI |NA |NA |AME math | + + AME for a categorical variable. Here we calculate pairwise contrasts for all three program types. These are the predicted number of awards. -```r +``` r ame2.nb <- brmsmargins( bayes.nb, at = data.frame( @@ -527,24 +567,28 @@ kable(ame2.nb$Summary, digits = 3) -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---| -| 0.264| 0.251| 0.072| 0.562| NA| NA| 0.99|HDI |NA |NA | -| 0.781| 0.778| 0.565| 1.010| NA| NA| 0.99|HDI |NA |NA | -| 0.388| 0.374| 0.146| 0.756| NA| NA| 0.99|HDI |NA |NA | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | +|-----:|-----:|-----:|------:|-----------:|----------:|----:|:------|:----|:---| +| 0.210| 0.218| 0.061| 0.345| NA| NA| 0.99|HDI |NA |NA | +| 7.051| 2.021| 0.676| 23.486| NA| NA| 0.99|HDI |NA |NA | +| 0.209| 0.195| 0.015| 0.432| NA| NA| 0.99|HDI |NA |NA | -```r + + +``` r kable(ame2.nb$ContrastSummary, digits = 3) ``` -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|------:|------:|------:|------:|-----------:|----------:|----:|:------|:----|:---|:-------------------------| -| -0.517| -0.523| -0.840| -0.127| NA| NA| 0.99|HDI |NA |NA |AME General v Academic | -| -0.124| -0.120| -0.532| 0.270| NA| NA| 0.99|HDI |NA |NA |AME General v Vocational | -| 0.392| 0.405| -0.024| 0.768| NA| NA| 0.99|HDI |NA |NA |AME Academic v Vocational | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|------:|------:|-------:|------:|-----------:|----------:|----:|:------|:----|:---|:-------------------------| +| -6.841| -1.803| -23.425| -0.331| NA| NA| 0.99|HDI |NA |NA |AME General v Academic | +| 0.001| -0.045| -0.110| 0.206| NA| NA| 0.99|HDI |NA |NA |AME General v Vocational | +| 6.842| 1.851| 0.244| 23.421| NA| NA| 0.99|HDI |NA |NA |AME Academic v Vocational | + + ## References diff --git a/vignettes/location-scale-marginaleffects.Rmd b/vignettes/location-scale-marginaleffects.Rmd index 69badad..e3cdc94 100644 --- a/vignettes/location-scale-marginaleffects.Rmd +++ b/vignettes/location-scale-marginaleffects.Rmd @@ -16,17 +16,18 @@ vignette: > -```r +``` r library(knitr) library(data.table) -#> data.table 1.14.2 using 24 threads (see ?getDTthreads). Latest news: r-datatable.com +#> data.table 1.16.4 using 16 threads (see ?getDTthreads). Latest news: r-datatable.com library(brms) #> Loading required package: Rcpp -#> Loading 'brms' package (version 2.17.0). Useful instructions +#> Loading 'brms' package (version 2.22.0). 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:stats': #> #> ar @@ -63,7 +64,7 @@ To start with, we will look at a fixed effects only location scale model. We will simulate a dataset. -```r +``` r d <- withr::with_seed( seed = 12345, code = { nObs <- 1000L @@ -83,12 +84,23 @@ ls.fe <- brm(bf( data = d, seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... +#> Loading required package: rstan +#> Loading required package: StanHeaders +#> +#> rstan version 2.32.6 (Stan version 2.32.2) +#> For execution on a local, multicore CPU with excess RAM we recommend calling +#> options(mc.cores = parallel::detectCores()). +#> To avoid recompilation of unchanged Stan programs, we recommend calling +#> rstan_options(auto_write = TRUE) +#> For within-chain threading using `reduce_sum()` or `map_rect()` Stan functions, +#> change `threads_per_chain` option: +#> rstan_options(threads_per_chain = 1) +#> Do not specify '-march=native' in 'LOCAL_CPPFLAGS' or a Makevars file ``` -```r +``` r summary(ls.fe) #> Family: gaussian #> Links: mu = identity; sigma = log @@ -98,14 +110,14 @@ summary(ls.fe) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -0.09 0.12 -0.33 0.15 1.00 4829 3553 -#> sigma_Intercept 1.01 0.03 0.94 1.07 1.00 4696 3023 -#> x 1.62 0.45 0.75 2.49 1.00 4470 2830 -#> grp 1.02 0.35 0.34 1.69 1.00 2526 2684 -#> sigma_x 0.85 0.09 0.67 1.02 1.00 4878 3309 -#> sigma_grp 1.01 0.05 0.92 1.09 1.00 4425 2871 +#> Intercept -0.09 0.13 -0.34 0.16 1.00 5154 3686 +#> sigma_Intercept 1.01 0.03 0.95 1.07 1.00 4915 2872 +#> x 1.62 0.46 0.72 2.49 1.00 3944 2769 +#> grp 1.02 0.34 0.34 1.67 1.00 3055 2998 +#> sigma_x 0.85 0.09 0.67 1.02 1.00 4847 2766 +#> sigma_grp 1.01 0.04 0.92 1.09 1.00 4862 2775 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -121,7 +133,7 @@ regression coefficients. Here is an example continuous AME. -```r +``` r h <- .001 ame1 <- brmsmargins( ls.fe, @@ -135,14 +147,16 @@ knitr::kable(ame1$ContrastSummary, digits = 3) -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 1.623| 1.631| 0.746| 2.492| NA| NA| 0.95|ETI |NA |NA |AME x | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|-----:|-----:|-----:|----:|-----------:|----------:|----:|:------|:----|:---|:-----| +| 1.619| 1.629| 0.717| 2.49| NA| NA| 0.95|ETI |NA |NA |AME x | + + Here is an AME for discrete / categorical predictors. -```r +``` r ame2 <- brmsmargins( ls.fe, at = data.frame(grp = c(0, 1)), @@ -155,9 +169,11 @@ knitr::kable(ame2$ContrastSummary, digits = 3) -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|----:|-----:|----:|-----------:|----------:|----:|:------|:----|:---|:-------| -| 1.016| 1.02| 0.343| 1.69| NA| NA| 0.95|ETI |NA |NA |AME grp | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-------| +| 1.023| 1.033| 0.338| 1.672| NA| NA| 0.95|ETI |NA |NA |AME grp | + + In `brms` the scale parameter for Gaussian models, `sigma` uses a log link function. Therefore when back @@ -169,7 +185,7 @@ We specify that we want AMEs for `sigma` by setting: `dpar = "sigma"`. Here is a continuous example. -```r +``` r h <- .001 ame3 <- brmsmargins( ls.fe, @@ -185,12 +201,14 @@ knitr::kable(ame3$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 4.463| 4.456| 3.488| 5.442| NA| NA| 0.95|ETI |NA |NA |AME x | +| 4.473| 4.468| 3.491| 5.495| NA| NA| 0.95|ETI |NA |NA |AME x | + + Here is a discrete / categorical example. -```r +``` r ame4 <- brmsmargins( ls.fe, at = data.frame(grp = c(0, 1)), @@ -205,7 +223,9 @@ knitr::kable(ame4$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-------| -| 4.907| 4.905| 4.409| 5.436| NA| NA| 0.95|ETI |NA |NA |AME grp | +| 4.905| 4.898| 4.391| 5.433| NA| NA| 0.95|ETI |NA |NA |AME grp | + + These results are comparable to the mean difference in standard deviation by `grp`. Note that in general, these may not closely @@ -214,7 +234,7 @@ to be uncorrelated, the simple unadjusted results match the regression results closely. -```r +``` r d[, .(SD = sd(y)), by = grp][, diff(SD)] ``` @@ -226,7 +246,7 @@ We will simulate some multilevel location scale data for model and fit the mixed effects location scale model. -```r +``` r dmixed <- withr::with_seed( seed = 12345, code = { nGroups <- 100 @@ -260,8 +280,7 @@ ls.me <- brm(bf( data = dmixed, seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... -#> Warning: 102 of 4000 (3.0%) transitions hit the maximum treedepth limit of 10. +#> Warning: 967 of 4000 (24.0%) transitions hit the maximum treedepth limit of 10. #> See https://mc-stan.org/misc/warnings for details. ``` @@ -272,8 +291,11 @@ one would want to make adjustments to ensure good convergence and an adequate effective sample size. -```r +``` r summary(ls.me) +#> Warning: Parts of the model have not converged (some Rhats are > 1.05). Be +#> careful when analysing the results! We recommend running more iterations and/or +#> setting stronger priors. #> Family: gaussian #> Links: mu = identity; sigma = log #> Formula: y ~ 1 + x + (1 + x | ID) @@ -282,29 +304,29 @@ summary(ls.me) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Group-Level Effects: +#> Multilevel Hyperparameters: #> ~ID (Number of levels: 100) #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS -#> sd(Intercept) 1.19 0.09 1.03 1.37 1.02 211 -#> sd(x) 0.42 0.04 0.35 0.51 1.00 862 -#> sd(sigma_Intercept) 1.27 0.09 1.11 1.47 1.01 536 -#> sd(sigma_x) 0.50 0.05 0.41 0.61 1.00 1569 -#> cor(Intercept,x) -0.40 0.12 -0.61 -0.16 1.00 577 -#> cor(sigma_Intercept,sigma_x) -0.36 0.11 -0.55 -0.13 1.00 1516 +#> sd(Intercept) 1.19 0.09 1.04 1.38 1.01 363 +#> sd(x) 0.42 0.04 0.35 0.51 1.00 720 +#> sd(sigma_Intercept) 1.26 0.09 1.09 1.46 1.01 450 +#> sd(sigma_x) 0.50 0.05 0.40 0.61 1.00 1256 +#> cor(Intercept,x) -0.40 0.12 -0.61 -0.16 1.00 724 +#> cor(sigma_Intercept,sigma_x) -0.36 0.11 -0.56 -0.13 1.00 1486 #> Tail_ESS -#> sd(Intercept) 519 -#> sd(x) 1634 -#> sd(sigma_Intercept) 819 -#> sd(sigma_x) 2418 -#> cor(Intercept,x) 1159 -#> cor(sigma_Intercept,sigma_x) 2200 +#> sd(Intercept) 765 +#> sd(x) 1555 +#> sd(sigma_Intercept) 949 +#> sd(sigma_x) 2447 +#> cor(Intercept,x) 1206 +#> cor(sigma_Intercept,sigma_x) 2235 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -2.55 0.12 -2.77 -2.33 1.03 105 300 -#> sigma_Intercept -1.48 0.13 -1.72 -1.22 1.02 217 388 -#> x 0.94 0.06 0.83 1.06 1.00 572 1550 -#> sigma_x 0.97 0.06 0.85 1.09 1.00 1031 1892 +#> Intercept -2.55 0.13 -2.83 -2.31 1.06 70 185 +#> sigma_Intercept -1.47 0.13 -1.71 -1.22 1.01 242 491 +#> x 0.94 0.06 0.82 1.05 1.01 605 1639 +#> sigma_x 0.97 0.06 0.85 1.10 1.00 1208 2288 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -319,7 +341,7 @@ Here is an example treating `x` as continuous using only the fixed effects for the AME for the scale parameter, `sigma`. -```r +``` r h <- .001 ame1a.lsme <- brmsmargins( ls.me, @@ -335,13 +357,15 @@ knitr::kable(ame1a.lsme$ContrastSummary, digits = 3) | M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | |-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.408| 0.404| 0.283| 0.555| NA| NA| 0.99|HDI |NA |NA |AME x | +| 0.408| 0.405| 0.281| 0.565| NA| NA| 0.99|HDI |NA |NA |AME x | + + Here is the example again, this time integrating out the random effects, which results in a considerable difference in the estimate of the AME. -```r +``` r h <- .001 ame1b.lsme <- brmsmargins( ls.me, @@ -349,22 +373,18 @@ ame1b.lsme <- brmsmargins( contrasts = cbind("AME x" = c(-1 / h, 1 / h)), dpar = "sigma", effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_2[1,1], L_2[2,1], L_2[1,2], L_2[2,2]] knitr::kable(ame1b.lsme$ContrastSummary, digits = 3) +#> Error: object 'ame1b.lsme' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.804| 0.766| 0.391| 1.575| NA| NA| 0.99|HDI |NA |NA |AME x | - Here is an example treating `x` as discrete, using only the fixed effects. -```r +``` r ame2a.lsme <- brmsmargins( ls.me, at = data.frame(x = c(0, 1)), @@ -377,37 +397,35 @@ knitr::kable(ame2a.lsme$ContrastSummary) -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|---------:|---------:|---------:|--------:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.3782473| 0.3750965| 0.2673526| 0.509232| NA| NA| 0.99|HDI |NA |NA |AME x | +| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | +|---------:|--------:|---------:|---------:|-----------:|----------:|----:|:------|:----|:---|:-----| +| 0.3786418| 0.376048| 0.2635285| 0.5200144| NA| NA| 0.99|HDI |NA |NA |AME x | + + Here is the example again, this time integrating out the random effects, likely the more appropriate estimate for most use cases. -```r +``` r ame2b.lsme <- brmsmargins( ls.me, at = data.frame(x = c(0, 1)), contrasts = cbind("AME x" = c(-1, 1)), dpar = "sigma", effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_2[1,1], L_2[2,1], L_2[1,2], L_2[2,2]] knitr::kable(ame2b.lsme$ContrastSummary) +#> Error: object 'ame2b.lsme' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|---------:|---------:|---------:|--------:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.7127988| 0.6795497| 0.3513727| 1.382269| NA| NA| 0.99|HDI |NA |NA |AME x | - This also is relatively close calculating all the individual standard deviations and taking their differences, then averaging. -```r +``` r dmixed[, .(SD = sd(y)), by = .(ID, x) ][, .(SDdiff = diff(SD)), by = ID][, mean(SDdiff)] #> [1] 0.6281889 diff --git a/vignettes/mixed-effects-marginaleffects.Rmd b/vignettes/mixed-effects-marginaleffects.Rmd index 165d555..6c44eec 100644 --- a/vignettes/mixed-effects-marginaleffects.Rmd +++ b/vignettes/mixed-effects-marginaleffects.Rmd @@ -16,17 +16,18 @@ vignette: > -```r +``` r library(knitr) library(data.table) -#> data.table 1.14.2 using 24 threads (see ?getDTthreads). Latest news: r-datatable.com +#> data.table 1.16.4 using 16 threads (see ?getDTthreads). Latest news: r-datatable.com library(brms) #> Loading required package: Rcpp -#> Loading 'brms' package (version 2.17.0). Useful instructions +#> Loading 'brms' package (version 2.22.0). 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:stats': #> #> ar @@ -151,7 +152,7 @@ mixed effects logistic regression model with individual differences in both the intercept and slope. -```r +``` r d <- withr::with_seed( seed = 12345, code = { nGroups <- 100 @@ -183,18 +184,29 @@ mlogit <- brms::brm( data = d, seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... -#> Warning: 25 of 4000 (1.0%) transitions ended with a divergence. +#> Warning: 4 of 4000 (0.0%) transitions ended with a divergence. #> See https://mc-stan.org/misc/warnings for details. +#> Loading required package: rstan +#> Loading required package: StanHeaders +#> +#> rstan version 2.32.6 (Stan version 2.32.2) +#> For execution on a local, multicore CPU with excess RAM we recommend calling +#> options(mc.cores = parallel::detectCores()). +#> To avoid recompilation of unchanged Stan programs, we recommend calling +#> rstan_options(auto_write = TRUE) +#> For within-chain threading using `reduce_sum()` or `map_rect()` Stan functions, +#> change `threads_per_chain` option: +#> rstan_options(threads_per_chain = 1) +#> Do not specify '-march=native' in 'LOCAL_CPPFLAGS' or a Makevars file ``` -```r +``` r summary(mlogit) -#> Warning: There were 25 divergent transitions after warmup. Increasing -#> adapt_delta above may help. See http://mc-stan.org/misc/warnings.html#divergent- -#> transitions-after-warmup +#> Warning: There were 4 divergent transitions after warmup. Increasing +#> adapt_delta above 0.8 may help. See +#> http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup #> Family: bernoulli #> Links: mu = logit #> Formula: y ~ 1 + x + (1 + x | ID) @@ -202,17 +214,17 @@ summary(mlogit) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Group-Level Effects: +#> Multilevel Hyperparameters: #> ~ID (Number of levels: 100) #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> sd(Intercept) 1.17 0.19 0.85 1.58 1.01 1038 1913 -#> sd(x) 0.53 0.28 0.03 1.11 1.03 223 409 -#> cor(Intercept,x) -0.16 0.42 -0.81 0.82 1.01 1403 1047 +#> sd(Intercept) 1.18 0.18 0.85 1.57 1.01 1326 2085 +#> sd(x) 0.54 0.27 0.05 1.05 1.02 386 955 +#> cor(Intercept,x) -0.18 0.40 -0.81 0.76 1.00 1778 1373 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -2.47 0.20 -2.89 -2.10 1.01 1116 1292 -#> x 0.95 0.18 0.59 1.34 1.00 2152 2024 +#> Intercept -2.48 0.19 -2.88 -2.12 1.00 1722 2017 +#> x 0.95 0.19 0.59 1.34 1.00 3183 2542 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -229,23 +241,19 @@ here `k = 100L`, the default. More details are in: `?brmsmargins:::.predict` -```r +``` r h <- .001 ame1 <- brmsmargins( mlogit, add = data.frame(x = c(0, h)), contrasts = cbind("AME x" = c(-1 / h, 1 / h)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame1$ContrastSummary, digits = 3) +#> Error: object 'ame1' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.112| 0.112| 0.063| 0.169| NA| NA| 0.99|HDI |NA |NA |AME x | - We can follow a similar process getting discrete predictions at x held at 0 or 1. In this instance, the summary of predictions is more interesting as well, since they are at meaningfully @@ -253,41 +261,31 @@ different values of `x`. They also agree quite closely with the average probability at different `x` values calculated in the data. -```r +``` r ame2 <- brmsmargins( mlogit, at = data.frame(x = c(0, 1)), contrasts = cbind("AME x" = c(-1, 1)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] ``` Here is a summary of the predictions. -```r +``` r knitr::kable(ame2$Summary, digits = 3) +#> Error: object 'ame2' not found ``` - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---| -| 0.120| 0.118| 0.071| 0.174| NA| NA| 0.99|HDI |NA |NA | -| 0.231| 0.231| 0.157| 0.302| NA| NA| 0.99|HDI |NA |NA | - - -```r +``` r knitr::kable(ame2$ContrastSummary, digits = 3) +#> Error: object 'ame2' not found ``` - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.111| 0.111| 0.06| 0.169| NA| NA| 0.99|HDI |NA |NA |AME x | - - -```r +``` r knitr::kable(d[, .(M = mean(y)), by = .(ID, x)][, .(M = mean(M)), by = x]) ``` @@ -298,6 +296,8 @@ knitr::kable(d[, .(M = mean(y)), by = .(ID, x)][, .(M = mean(M)), by = x]) | 0| 0.117| | 1| 0.228| + + Note that when integrating out random effects, the random seed is quite important. If the `seed` argument is not specified, `brmsmargins()` will randomly select one. This would not matter @@ -315,23 +315,19 @@ Monte Carlo variation only, but with `k = 10L` the small error, when divided by `h = .001` becomes very large, impossibly so. -```r +``` r h <- .001 ame.error <- brmsmargins( mlogit, add = data.frame(x = c(0, 0)), contrasts = cbind("AME x" = c(-1 / h, 1 / h)), effects = "integrateoutRE", k = 10L, seed = c(1234, 54321)) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame.error$ContrastSummary, digits = 3) +#> Error: object 'ame.error' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|------:|-----:|--------:|-------:|-----------:|----------:|----:|:------|:----|:---|:-----| -| -0.703| -0.98| -162.906| 172.759| NA| NA| 0.99|HDI |NA |NA |AME x | - This disappears when we use the same seed for each row of the data used for predictions. Here we get all zeros for the difference, as we would expect. Note that you do not need to specify a seed for each @@ -339,23 +335,19 @@ row of the data. You can specify one seed (or rely on `brmsmargins()` default), which will then be used for all rows of the data. -```r +``` r h <- .001 ame.noerror <- brmsmargins( mlogit, add = data.frame(x = c(0, 0)), contrasts = cbind("AME x" = c(-1 / h, 1 / h)), effects = "integrateoutRE", k = 10L, seed = c(1234, 1234)) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame.noerror$ContrastSummary, digits = 3) +#> Error: object 'ame.noerror' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|--:|---:|--:|--:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0| 0| 0| 0| NA| NA| 0.99|HDI |NA |NA |AME x | - ### Marginal Coefficients The fixed effects coefficients are conditional on the random effects. @@ -366,9 +358,10 @@ population averaged coefficients. The function to do this is logistic regression that ignores the clustering in the data. -```r +``` r ## calculate marginal coefficients -mc.logit <- marginalcoef(mlogit, CI = 0.95) +mc.logit <- marginalcoef(mlogit, CI = 0.95, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] ## calculate single level logistic regression glm.logit <- glm(y ~ 1 + x, family = "binomial", data = d) @@ -379,7 +372,7 @@ glm.logit <- as.data.table(cbind(Est = coef(glm.logit), confint(glm.logit))) Now we can view and compare the results. -```r +``` r knitr::kable(cbind( mc.logit$Summary[, .( @@ -388,15 +381,9 @@ knitr::kable(cbind( glm.logit[, .( GLMCoef = sprintf("%0.3f", round(Est, 3)), GLMCI = sprintf("[%0.3f, %0.3f]", round(`2.5 %`, 3), round(`97.5 %`, 3)))])) +#> Error: object 'mc.logit' not found ``` - - -|MargCoef |MargCoefCI |GLMCoef |GLMCI | -|:--------|:----------------|:-------|:----------------| -|-2.010 |[-2.386, -1.666] |-2.021 |[-2.219, -1.833] | -|0.800 |[0.520, 1.083] |0.802 |[0.561, 1.047] | - ## Mixed Effects Poisson Regression We will simulate some multilevel poisson data for our @@ -404,7 +391,7 @@ mixed effects poisson regression model with individual differences in both the intercept and slope. -```r +``` r dpoisson <- withr::with_seed( seed = 12345, code = { nGroups <- 100 @@ -435,11 +422,10 @@ mpois <- brms::brm( data = dpoisson, seed = 1234, chains = 4L, cores = 4L, backend = "cmdstanr", silent = 2, refresh = 0, adapt_delta = 0.99) -#> Compiling Stan program... ``` -```r +``` r summary(mpois) #> Family: poisson #> Links: mu = log @@ -448,17 +434,17 @@ summary(mpois) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Group-Level Effects: +#> Multilevel Hyperparameters: #> ~ID (Number of levels: 100) #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> sd(Intercept) 1.18 0.15 0.91 1.52 1.00 1353 2327 -#> sd(x) 0.36 0.20 0.02 0.75 1.01 308 949 -#> cor(Intercept,x) -0.06 0.41 -0.75 0.85 1.00 1680 1536 +#> sd(Intercept) 1.18 0.15 0.92 1.52 1.01 1265 1683 +#> sd(x) 0.36 0.21 0.02 0.77 1.01 232 955 +#> cor(Intercept,x) -0.05 0.42 -0.77 0.87 1.00 1382 1310 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -2.47 0.17 -2.82 -2.14 1.00 1777 2484 -#> x 0.97 0.15 0.68 1.27 1.00 3307 2893 +#> Intercept -2.46 0.17 -2.83 -2.14 1.00 1375 1907 +#> x 0.97 0.15 0.68 1.27 1.00 2867 2782 #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -471,42 +457,34 @@ We use `brmsmargins()` in the same way as for the mixed effects logistic regress Here is an example with a numeric derivative treating `x` as continuous. -```r +``` r h <- .001 ame1.pois <- brmsmargins( mpois, add = data.frame(x = c(0, h)), contrasts = cbind("AME x" = c(-1 / h, 1 / h)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame1.pois$ContrastSummary, digits = 3) +#> Error: object 'ame1.pois' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.332| 0.312| 0.14| 0.731| NA| NA| 0.99|HDI |NA |NA |AME x | - Here is an example treating `x` as discrete. -```r +``` r ame2.pois <- brmsmargins( mpois, at = data.frame(x = c(0, 1)), contrasts = cbind("AME x" = c(-1, 1)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame2.pois$ContrastSummary) +#> Error: object 'ame2.pois' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|--------:|---------:|---------:|---------:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.294537| 0.2780058| 0.1076343| 0.5973478| NA| NA| 0.99|HDI |NA |NA |AME x | - ### Marginal Coefficients Just as for mixed effects logistic regression, @@ -517,9 +495,10 @@ Here is an example and comparison to results using a single level poisson regression that ignores the clustering in the data. -```r +``` r ## calculate marginal coefficients -mc.pois <- marginalcoef(mpois, CI = 0.95) +mc.pois <- marginalcoef(mpois, CI = 0.95, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] ## calculate single level logistic regression glm.pois <- glm(y ~ 1 + x, family = "poisson", data = d) @@ -530,7 +509,7 @@ glm.pois <- as.data.table(cbind(Est = coef(glm.pois), confint(glm.pois))) Now we can view and compare the results. -```r +``` r knitr::kable(cbind( mc.pois$Summary[, .( @@ -539,33 +518,38 @@ knitr::kable(cbind( glm.pois[, .( GLMCoef = sprintf("%0.3f", round(Est, 3)), GLMCI = sprintf("[%0.3f, %0.3f]", round(`2.5 %`, 3), round(`97.5 %`, 3)))])) +#> Error: object 'mc.pois' not found ``` - - -|MargCoef |MargCoefCI |GLMCoef |GLMCI | -|:--------|:----------------|:-------|:----------------| -|-1.765 |[-2.218, -1.239] |-1.858 |[-2.019, -1.705] | -|0.987 |[0.697, 1.279] |0.983 |[0.802, 1.170] | - ## Mixed Effects Negative Binomial Regression Negative binomial models work the same way as for poisson models. We use the same dataset, just for demonstration. -```r +``` r mnb <- brms::brm( y ~ 1 + x + (1 + x | ID), family = "negbinomial", data = dpoisson, seed = 1234, chains = 4L, cores = 4L, backend = "cmdstanr", silent = 2, refresh = 0, adapt_delta = 0.99) -#> Compiling Stan program... +#> Warning: 1638 of 4000 (41.0%) transitions ended with a divergence. +#> See https://mc-stan.org/misc/warnings for details. +#> Warning: 999 of 4000 (25.0%) transitions hit the maximum treedepth limit of 10. +#> See https://mc-stan.org/misc/warnings for details. +#> Warning: 1 of 4 chains have a NaN E-BFMI. +#> See https://mc-stan.org/misc/warnings for details. ``` -```r +``` r summary(mnb) +#> Warning: Parts of the model have not converged (some Rhats are > 1.05). Be +#> careful when analysing the results! We recommend running more iterations and/or +#> setting stronger priors. +#> Warning: There were 1638 divergent transitions after warmup. Increasing +#> adapt_delta above 0.99 may help. See +#> http://mc-stan.org/misc/warnings.html#divergent-transitions-after-warmup #> Family: negbinomial #> Links: mu = log; shape = identity #> Formula: y ~ 1 + x + (1 + x | ID) @@ -573,21 +557,23 @@ summary(mnb) #> Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1; #> total post-warmup draws = 4000 #> -#> Group-Level Effects: +#> Multilevel Hyperparameters: #> ~ID (Number of levels: 100) #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> sd(Intercept) 1.19 0.15 0.93 1.51 1.01 975 1816 -#> sd(x) 0.35 0.20 0.01 0.74 1.01 252 821 -#> cor(Intercept,x) -0.07 0.41 -0.77 0.84 1.00 1432 1277 +#> sd(Intercept) 1.31 0.22 0.96 1.59 1.47 8 29 +#> sd(x) 0.42 0.35 0.00 0.85 2.33 5 4 +#> cor(Intercept,x) -0.49 0.47 -1.00 0.68 2.02 5 4 #> -#> Population-Level Effects: +#> Regression Coefficients: #> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> Intercept -2.46 0.18 -2.83 -2.14 1.00 1057 1993 -#> x 0.98 0.15 0.70 1.28 1.00 2711 2153 +#> Intercept -2.62 0.24 -2.99 -2.22 1.62 7 6 +#> x 1.09 0.17 0.75 1.31 1.49 7 83 #> -#> Family Specific Parameters: -#> Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS -#> shape 61.06 63.22 8.15 243.02 1.00 5885 2745 +#> Further Distributional Parameters: +#> Estimate Est.Error l-95% CI +#> shape 615520000648203337728.00 1066245201681847287848.00 13.10 +#> u-95% CI Rhat Bulk_ESS Tail_ESS +#> shape 2462080000000000000000.00 2.48 5 NA #> #> Draws were sampled using sample(hmc). For each parameter, Bulk_ESS #> and Tail_ESS are effective sample size measures, and Rhat is the potential @@ -600,70 +586,57 @@ We use `brmsmargins()` in the same way as for the mixed effects poisson regressi Here is an example with a numeric derivative treating `x` as continuous. -```r +``` r h <- .001 ame1.nb <- brmsmargins( mnb, add = data.frame(x = c(0, h)), contrasts = cbind("AME x" = c(-1 / h, 1 / h)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame1.nb$ContrastSummary, digits = 3) +#> Error: object 'ame1.nb' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.335| 0.313| 0.141| 0.777| NA| NA| 0.99|HDI |NA |NA |AME x | - Here is an example treating `x` as discrete. -```r +``` r ame2.nb <- brmsmargins( mnb, at = data.frame(x = c(0, 1)), contrasts = cbind("AME x" = c(-1, 1)), effects = "integrateoutRE", k = 100L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame2.nb$ContrastSummary, digits = 3) +#> Error: object 'ame2.nb' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:-----| -| 0.298| 0.28| 0.141| 0.662| NA| NA| 0.99|HDI |NA |NA |AME x | - ### Marginal Coefficients Negative binomial models cannot be fit by the `glm()` function in `R` so we just show the population averaged values from `brms`. -```r +``` r ## calculate marginal coefficients -mc.nb <- marginalcoef(mnb, CI = 0.95) +mc.nb <- marginalcoef(mnb, CI = 0.95, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] ``` View the results. -```r +``` r knitr::kable( mc.nb$Summary[, .( MargCoef = sprintf("%0.3f", round(M, 3)), MargCoefCI = sprintf("[%0.3f, %0.3f]", round(LL, 3), round(UL, 3)))]) +#> Error: object 'mc.nb' not found ``` - - -|MargCoef |MargCoefCI | -|:--------|:----------------| -|-1.759 |[-2.225, -1.234] | -|0.988 |[0.725, 1.283] | - ## Centered Categorical Predictors In mixed effects models, it is common to center continuous predictors. @@ -761,7 +734,7 @@ This is more easily shown than said. Here we simulate some multilevel data with a binary predictor, `x` and a binary outcome, `y`. -```r +``` r d <- withr::with_seed( seed = 12345, code = { nGroups <- 100 @@ -796,7 +769,7 @@ so that variable is omitted. Any variation not explained by it will go into the random intercept, anyways. -```r +``` r ## within person centering binary predictor d[, xb := mean(x), by = ID] d[, xw := x - xb] @@ -806,7 +779,6 @@ mlogitcent <- brms::brm( data = d, seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... ``` Now comes the new part. Based on how we coded our predictor, `x`, @@ -830,7 +802,7 @@ The list must have two elements at a minimum: The table shows a few examples of what the dataset looks like. -```r +``` r xwid <- melt( d[, .(a = 0 - na.omit(xb)[1], @@ -855,6 +827,8 @@ knitr::kable(xwid[ID %in% c(1, 2, 4, 100)][order(ID)], digits = 2) | 100|a | -0.15| | 100|b | 0.85| + + This new dataset shows what values to use for moving from one category of `x` to another category of `x` looks like, for each ID. Note that sometimes these are the same. In the case @@ -879,38 +853,28 @@ for `xw = a` and `xw = b` which in our case refers to when **for that ID**. -```r +``` r ame.cent <- brmsmargins( object = mlogitcent, at = expand.grid(xw = c("a", "b")), wat = usewat, contrasts = cbind("AME xw" = c(-1, 1)), effects = "integrateoutRE", k = 20L) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame.cent$Summary, digits = 3) +#> Error: object 'ame.cent' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---| -| 0.122| 0.119| 0.051| 0.206| NA| NA| 0.99|HDI |NA |NA | -| 0.231| 0.228| 0.116| 0.345| NA| NA| 0.99|HDI |NA |NA | - Just as in other examples, we can get a summary of the contrast of these two values, our AME. -```r +``` r knitr::kable(ame.cent$ContrastSummary, digits = 3) +#> Error: object 'ame.cent' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|-----:|-----:|-----:|-----:|-----------:|----------:|----:|:------|:----|:---|:------| -| 0.109| 0.109| 0.041| 0.184| NA| NA| 0.99|HDI |NA |NA |AME xw | - ## Interactions and Marginal Effects As a final example, we will look at a model with an interaction. @@ -945,7 +909,7 @@ To show this example, we first simulate a dataset, using a similar process as in previous examples. -```r +``` r d <- withr::with_seed( seed = 12345, code = { nGroups <- 100 @@ -985,7 +949,7 @@ dataset with the within person hypothetical values for each person, `usewat`. -```r +``` r ## within person centering binary predictor d[, xb := mean(x), by = ID] d[, xw := x - xb] @@ -1007,13 +971,12 @@ between people randomized to the waitlist control or the intervention. The model can be fit as in the following code. -```r +``` r mlogitcentint <- brms::brm( y ~ 1 + xw * group + xb + (1 + xw | ID), family = "bernoulli", data = d, seed = 1234, silent = 2, refresh = 0, chains = 4L, cores = 4L, backend = "cmdstanr") -#> Compiling Stan program... ``` We will generate average marginal predicted probabilities @@ -1022,7 +985,7 @@ none and any. The grid of possibilities is created and stored in `use.at` and shown in the following table. -```r +``` r use.at <- expand.grid(group = 0:1, xw = c("a", "b")) knitr::kable(use.at, digits = 0) ``` @@ -1036,6 +999,8 @@ knitr::kable(use.at, digits = 0) | 0|b | | 1|b | + + From this grid, there are three contrasts of interest. 1. What is the average marginal effect (AME) of @@ -1056,7 +1021,7 @@ The code to do this and the resulting contrast matrix with labels is shown in the following code and table. -```r +``` r contr.mat <- cbind(xw = c(-1, 1)) %x% diag(2) contr.mat <- cbind( contr.mat, @@ -1078,6 +1043,8 @@ knitr::kable(contr.mat, digits = 0) | 1| 0| -1| | 0| 1| 1| + + Now we can use `brmsmargins()` with the model, our grid of values to generate average marginal predictions, and our contrast matrix to generate the AMEs and answer our @@ -1088,7 +1055,7 @@ Once run, we can see the average marginal predictions for our grid of values in `use.at` in the following table. -```r +``` r ame.centint <- brmsmargins( object = mlogitcentint, at = use.at, @@ -1096,19 +1063,12 @@ ame.centint <- brmsmargins( contrasts = contr.mat, CI = 0.95, ROPE = c(-.02, .02), MID = c(-.05, .05), effects = "integrateoutRE", k = 20L, seed = 1234) +#> Error in `[.data.table`(data, , ..n): column(s) not found: [L_1[1,1], L_1[2,1], L_1[1,2], L_1[2,2]] knitr::kable(ame.centint$Summary[, .(M, LL, UL, CI, CIType)], digits = 3) +#> Error: object 'ame.centint' not found ``` - - -| M| LL| UL| CI|CIType | -|-----:|-----:|-----:|----:|:------| -| 0.117| 0.053| 0.187| 0.95|HDI | -| 0.145| 0.073| 0.221| 0.95|HDI | -| 0.045| 0.016| 0.081| 0.95|HDI | -| 0.268| 0.168| 0.379| 0.95|HDI | - Finally we can look at the contrast summary to answer our three questions of interest: the two simple AMEs of within person stressor exposure and the difference between intervention conditions in those AMEs. @@ -1144,18 +1104,11 @@ with their sleep that night, and that the emotion regulation intervention moderates the association between within person stressor exposure and sleep. -```r +``` r knitr::kable(ame.centint$ContrastSummary, digits = 3) +#> Error: object 'ame.centint' not found ``` - - -| M| Mdn| LL| UL| PercentROPE| PercentMID| CI|CIType |ROPE |MID |Label | -|------:|------:|------:|------:|-----------:|----------:|----:|:------|:-------------|:--------------------------------|:-------------| -| -0.071| -0.068| -0.138| -0.016| 3.150| 74.625| 0.95|HDI |[-0.02, 0.02] |[-Inf, -0.05] | [0.05, Inf] |AME xw: Grp 0 | -| 0.124| 0.122| 0.038| 0.214| 0.875| 95.625| 0.95|HDI |[-0.02, 0.02] |[-Inf, -0.05] | [0.05, Inf] |AME xw: Grp 1 | -| 0.195| 0.191| 0.100| 0.302| 0.000| 99.900| 0.95|HDI |[-0.02, 0.02] |[-Inf, -0.05] | [0.05, Inf] |delta AME xw | - ## References Potentially useful references.