Skip to content

Commit

Permalink
Cleaning up g_mlm_Bayes(), adding to exported functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
jepusto committed Feb 19, 2024
1 parent 07d4bae commit 5c7f341
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 119 deletions.
5 changes: 4 additions & 1 deletion .Rbuildignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@ images
^docs$
^pkgdown$
^CRAN-SUBMISSION$
revdep
revdep
^Dockerfile$
^heroku.yml$
rsconnect
1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Suggests:
rclipboard,
rvest,
testthat,
rstan,
brms
RoxygenNote: 7.2.3
Encoding: UTF-8
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export(effect_size_MB)
export(extract_varcomp)
export(g_REML)
export(g_mlm)
export(g_mlm_Bayes)
export(graph_SCD)
export(phase_pairs)
export(preprocess_SCD)
Expand Down
122 changes: 13 additions & 109 deletions R/calc_BCSMD.R
Original file line number Diff line number Diff line change
Expand Up @@ -118,105 +118,6 @@ write_formula <- function(powers, var_names) {
}


# calculate mlm effect size using Bayesian estimation methods
g_mlm_Bayes <- function(mod, p_const, r_const, rconst_base_var_index = 1) {

param_names <- brms::variables(mod)

# calculate the numerator of BCSMD

posterior_samples_fixed <- brms::as_draws_matrix(mod, variable = "^b_", regex = TRUE)

if ("b_sigma_Intercept" %in% param_names) {
samples_fixed <- posterior_samples_fixed[,!startsWith(colnames(posterior_samples_fixed), "b_sigma_")]
sigma_sq <- exp(2*(posterior_samples_fixed[,"b_sigma_Intercept"]))
} else {
samples_fixed <- posterior_samples_fixed
sigma <- brms::as_draws_matrix(mod, variable = "sigma", regex = TRUE)
sigma_sq <- sigma^2
}

es_num_vec <- apply(samples_fixed, 1, function(x) sum(x * p_const))

# calculate the denominator of BCSMD

samples_r_sd <- brms::as_draws_matrix(mod, variable = "^sd_", regex = TRUE)
samples_r_var <- samples_r_sd^2

if (sum(grepl("^cor_",param_names)) > 0) {
samples_r_cor <- brms::as_draws_matrix(mod, variable = "^cor_", regex = TRUE)
cor_names_split <- strsplit(colnames(samples_r_cor), split = "__")
cor_sd_suf <- lapply(cor_names_split, function(x) x[-1])
cor_sd_pre <- lapply(cor_names_split, function(x) paste0("sd_", gsub(".*\\_", "", x[[1]])))
cor_sd_names <- mapply(function(x,y) paste0(x, "__", y), cor_sd_pre, cor_sd_suf, SIMPLIFY = FALSE)
sd_prod <- sapply(cor_sd_names, function(x) samples_r_sd[,x[1]] * samples_r_sd[,x[2]])
samples_r_cov <- samples_r_cor * sd_prod
} else {
samples_r_cov <- NULL
}

samples_r_varcov <- cbind(samples_r_var, samples_r_cov, sigma_sq)

es_denom_sq <- apply(samples_r_varcov, 1, function(x) sum(x * r_const))
es_denom_vec <- sqrt(es_denom_sq)

# calculate BCSMD

es_vec <- es_num_vec / es_denom_vec

# calculate rho

if (rconst_base_var_index == 1) {
rho <- mean(samples_r_varcov[,1] / (samples_r_varcov[,1] + samples_r_varcov[,ncol(samples_r_varcov)]))
} else {
rho_level2 <- mean((samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index]) /
(samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index] +
samples_r_varcov[,ncol(samples_r_varcov)]))
rho_level2 <- round(rho_level2, 4)

rho_level3 <- mean(samples_r_varcov[,1] /
(samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index] +
samples_r_varcov[,ncol(samples_r_varcov)]))
rho_level3 <- round(rho_level3, 4)

rho <- paste0("Level2: ", rho_level2, " Level3: ", rho_level3)
}

# get the corStruct and varStruct param

if (sum(grepl("^ar",param_names)) > 0) {
autocor_draw <- brms::as_draws_matrix(mod, variable = "^ar", regex = TRUE)
autocor_param <- mean(autocor_draw)
} else if (sum(grepl("^ma",param_names)) > 0) {
autocor_draw <- brms::as_draws_matrix(mod, variable = "^ma", regex = TRUE)
autocor_param <- mean(autocor_draw)
} else {
autocor_param <- NA_real_
}

if (sum(grepl("^b_sigma_",param_names)) > 0) {
var_param_name <- setdiff(param_names[startsWith(param_names, "b_sigma_")], "b_sigma_Intercept")
var_param_draw <- brms::as_draws_matrix(mod, variable = var_param_name, regex = TRUE)
var_param <- exp(mean(var_param_draw))
} else {
var_param <- NA_real_
}


g <- mean(es_vec)
SE_g <- sd(es_vec)
nu <- 2 * (mean(es_denom_vec))^2 / var(es_denom_vec)
CI_L <- quantile(es_vec, .025)
CI_U <- quantile(es_vec, .975)

res <- list(g_AB = g, SE_g_AB = SE_g, nu = nu, CI_L = CI_L, CI_U = CI_U,
es_num_vec = es_num_vec, es_denom_vec = es_denom_vec,
autocor_param = autocor_param, var_param = var_param, rho = rho)

return(res)

}


#' @title Calculate constants
#'
Expand Down Expand Up @@ -456,14 +357,15 @@ calc_consts <- function(method, design, center = 0,
#' FE_base = 0, RE_base = 0, FE_trt = 0,
#' data = Laski)
#'
#' if (requireNamespace("brms", quietly = TRUE)) withAutoprint({
#' \dontrun{
#' # Bayesian estimation
#' calc_BCSMD(design = "MBP",
#' case = case, phase = treatment,
#' session = time, outcome = outcome,
#' FE_base = 0, RE_base = 0, FE_trt = 0,
#' Bayesian = TRUE,
#' data = Laski)
#' })
#' }
#'
#' # Model with linear time trends in baseline and treatment phases,
#' # random baseline slopes, fixed treatment effects
Expand All @@ -473,14 +375,15 @@ calc_consts <- function(method, design, center = 0,
#' FE_base = c(0,1), RE_base = c(0,1), FE_trt = c(0,1),
#' data = Laski)
#'
#' if (requireNamespace("brms", quietly = TRUE)) withAutoprint({
#' \dontrun{
#' # Bayesian estimation
#' calc_BCSMD(design = "MBP",
#' case = case, phase = treatment,
#' session = time, outcome = outcome, center = 4,
#' FE_base = c(0,1), RE_base = c(0,1), FE_trt = c(0,1),
#' Bayesian = TRUE,
#' data = Laski)
#' })
#' }
#'
#' data(Anglesea)
#' calc_BCSMD(design = "TR",
Expand All @@ -507,8 +410,9 @@ calc_consts <- function(method, design, center = 0,
#' FE_base = c(0,1), RE_base = 0, RE_base_2 = 0,
#' FE_trt = c(0,1), RE_trt = NULL, RE_trt_2 = NULL,
#' data = Bryant2018)
#'
#' if (requireNamespace("brms", quietly = TRUE)) withAutoprint({
#'
#' \dontrun{
#' # Bayesian estimation
#' calc_BCSMD(design = "CMB",
#' cluster = group, case = case, phase = treatment,
#' session = session, outcome = outcome, center = 49,
Expand All @@ -517,8 +421,7 @@ calc_consts <- function(method, design, center = 0,
#' FE_trt = c(0,1), RE_trt = 1, RE_trt_2 = NULL,
#' Bayesian = TRUE,
#' data = Bryant2018)
#' })
#'
#' }
#'

calc_BCSMD <- function(design,
Expand Down Expand Up @@ -820,10 +723,11 @@ calc_BCSMD <- function(design,


#' @export

summary.bcsmd <- function(object, digits = 3, ...) {

converged <- if(isTRUE(object$converged)) "Yes" else "No"
converged <- if (inherits(object, "Bayes-bcsmd")) NA_character_ else if (isTRUE(object$converged)) "Yes" else "No"

ES_summary <- data.frame(
ES = object$g_AB,
SE = object$SE_g_AB,
Expand Down
147 changes: 147 additions & 0 deletions R/g_mlm_Bayes.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# calculate mlm effect size using Bayesian estimation methods
#' @title Calculates a between-case standardized mean difference effect size estimate
#'
#' @description Estimates a standardized mean difference effect size from a
#' multi-level model estimated using \code{brms::brm}.
#'
#' @param mod Fitted model of class brmsfit (estimated using
#' \code{brms::brm()}), from which to estimate the effect size.
#' @param p_const Vector of constants for calculating numerator of effect size.
#' Must be the same length as fixed effects in \code{mod}.
#' @param r_const Vector of constants for calculating denominator of effect
#' size. Must be the same length as the number of variance component
#' parameters in \code{mod_denom}.
#' @param rconst_base_var_index Something, not really sure what.
#' @param cover Confidence level.
#'
#' @export
#'
#' @return A list with the following components
#' \tabular{ll}{
#' \code{g_AB} \tab Posterior mean effect size estimate \cr
#' \code{SE_g_AB} \tab Approximate standard error of mean effect size estimate \cr
#' \code{nu} \tab Estimated denominator degrees of freedom \cr
#' \code{CI_L} \tab Lower bound of credible interval for effect size \cr
#' \code{CI_U} \tab Upper bound of credible interval for effect size \cr
#' \code{es_num_vec} \tab Posterior samples of effect size numerator \cr
#' \code{es_denom_vec} \tab Posterior samples of squared denominator of effect size \cr
#' \code{autocor_param} \tab Posterior mean auto-correlation \cr
#' \code{var_param} \tab Posterior mean of level-1 variance model parameter \cr
#' \code{rho} \tab Posterior mean intra-class correlation \cr
#' }
#'
#' @references Pustejovsky, J. E., Hedges, L. V., & Shadish, W. R. (2014).
#' Design-comparable effect sizes in multiple baseline designs: A general
#' modeling framework. \emph{Journal of Educational and Behavioral Statistics,
#' 39}(4), 211-227. \doi{10.3102/1076998614547577}
#'

g_mlm_Bayes <- function(mod, p_const, r_const, rconst_base_var_index = 1, cover = 95) {

param_names <- brms::variables(mod)

# calculate the numerator of BCSMD

posterior_samples_fixed <- brms::as_draws_matrix(mod, variable = "^b_", regex = TRUE)

if ("b_sigma_Intercept" %in% param_names) {
samples_fixed <- posterior_samples_fixed[,!startsWith(colnames(posterior_samples_fixed), "b_sigma_")]
sigma_sq <- exp(2*(posterior_samples_fixed[,"b_sigma_Intercept"]))
} else {
samples_fixed <- posterior_samples_fixed
sigma <- brms::as_draws_matrix(mod, variable = "sigma", regex = TRUE)
sigma_sq <- sigma^2
}

es_num_vec <- apply(samples_fixed, 1, function(x) sum(x * p_const))

# calculate the denominator of BCSMD

samples_r_sd <- brms::as_draws_matrix(mod, variable = "^sd_", regex = TRUE)
samples_r_var <- samples_r_sd^2

if (sum(grepl("^cor_",param_names)) > 0) {
samples_r_cor <- brms::as_draws_matrix(mod, variable = "^cor_", regex = TRUE)
cor_names_split <- strsplit(colnames(samples_r_cor), split = "__")
cor_sd_suf <- lapply(cor_names_split, function(x) x[-1])
cor_sd_pre <- lapply(cor_names_split, function(x) paste0("sd_", gsub(".*\\_", "", x[[1]])))
cor_sd_names <- mapply(function(x,y) paste0(x, "__", y), cor_sd_pre, cor_sd_suf, SIMPLIFY = FALSE)
sd_prod <- sapply(cor_sd_names, function(x) samples_r_sd[,x[1]] * samples_r_sd[,x[2]])
samples_r_cov <- samples_r_cor * sd_prod
} else {
samples_r_cov <- NULL
}

samples_r_varcov <- cbind(samples_r_var, samples_r_cov, sigma_sq)

es_denom_sq <- apply(samples_r_varcov, 1, function(x) sum(x * r_const))
es_denom_vec <- sqrt(es_denom_sq)

# calculate BCSMD

es_vec <- es_num_vec / es_denom_vec

# calculate rho

if (rconst_base_var_index == 1) {
rho <- mean(samples_r_varcov[,1] / (samples_r_varcov[,1] + samples_r_varcov[,ncol(samples_r_varcov)]))
} else {
rho_level2 <- mean((samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index]) /
(samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index] +
samples_r_varcov[,ncol(samples_r_varcov)]))
rho_level2 <- round(rho_level2, 4)

rho_level3 <- mean(samples_r_varcov[,1] /
(samples_r_varcov[,1] + samples_r_varcov[,rconst_base_var_index] +
samples_r_varcov[,ncol(samples_r_varcov)]))
rho_level3 <- round(rho_level3, 4)

rho <- paste0("Level2: ", rho_level2, " Level3: ", rho_level3)
}

# get the corStruct and varStruct param

if (sum(grepl("^ar",param_names)) > 0) {
autocor_draw <- brms::as_draws_matrix(mod, variable = "^ar", regex = TRUE)
autocor_param <- mean(autocor_draw)
} else if (sum(grepl("^ma",param_names)) > 0) {
autocor_draw <- brms::as_draws_matrix(mod, variable = "^ma", regex = TRUE)
autocor_param <- mean(autocor_draw)
} else {
autocor_param <- NA_real_
}

if (sum(grepl("^b_sigma_",param_names)) > 0) {
var_param_name <- setdiff(param_names[startsWith(param_names, "b_sigma_")], "b_sigma_Intercept")
var_param_draw <- brms::as_draws_matrix(mod, variable = var_param_name, regex = TRUE)
var_param <- exp(mean(var_param_draw))
} else {
var_param <- NA_real_
}


g <- mean(es_vec)
SE_g <- sd(es_vec)
nu <- 2 * (mean(es_denom_vec))^2 / var(es_denom_vec)
CI_L <- quantile(es_vec, (1 - cover / 100) / 2)
CI_U <- quantile(es_vec, (1 + cover / 100) / 2)

res <- list(
g_AB = g, SE_g_AB = SE_g,
CI_L = CI_L, CI_U = CI_U,
nu = nu,
es_num_vec = es_num_vec,
es_denom_vec = es_denom_vec,
phi = autocor_param,
var_param = var_param,
rho = rho,
CI_cover = cover,
converged = NA
)

class(res) <- c("Bayes-bcsmd", "bcsmd")

return(res)

}

2 changes: 1 addition & 1 deletion R/scdhlm.R
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
#'

shine_scd <- function(dataset = NULL, ...) {
# modify is no brms or rstan, can still run other two
# modify if no brms or rstan, can still run other two
req_pkgs <- c("shiny","ggplot2","markdown","glue","rclipboard","readxl","janitor")
missing_pkgs <- unlist(lapply(req_pkgs, check_for_package))

Expand Down
Loading

0 comments on commit 5c7f341

Please sign in to comment.