A priori power analysis

Published

September 16, 2026

Alternative a) Monte Carlo simulation based on Lavaan

Preparation

Effect values for each path

# --- TRUE POPULATION VALUES
true_params <- list(
  # Main effects of dummies on IM
  b_r1e1 = 0.25, #indirect effects
  b_r0e2 = 0.25,
  b_r1e2 = 0.25,
  b_r0e3 = 0.25,
  b_r1e3 = 0.25,
  
  # Main effects of TR, ADT, and PEB on IM
  b_TR  = 0.25,
  b_ADT = 0.25,
  b_PEB = 0.25,
  
  # EEF regressions
  c_r1e1 = 0.37,
  c_r0e2 = 0.37,
  c_r1e2 = 0.37,
  c_r0e3 = 0.37,
  c_r1e3 = 0.37,
  c_IM   = 0.57, #indirect effects
  c_TR   = 0.20,
  c_ADT  = 0.20,
  c_PEB  = 0.40,
  
  # EEC regressions
  d_r1e1 = 0.37,
  d_r0e2 = 0.37,
  d_r1e2 = 0.37,
  d_r0e3 = 0.37,
  d_r1e3 = 0.37,
  d_IM   = 0.57, #indirect effects
  d_TR   = 0.20,
  d_ADT  = 0.20,
  d_PEB  = 0.40,
  
  # Residual covariance/correlation between EEF and EEC
  d_EEF_EEC = 0.35
)

Dataset simulation

simulate_one_dataset <- function(N, true_params) {
  
  with(true_params, {
    
    # Exogenous continuous variables
    TR      <- rnorm(N, mean = 0, sd = 1)
    ADT     <- rnorm(N, mean = 0, sd = 1)
    PEB_yes <- rnorm(N, mean = 0, sd = 1)
    
    # Binary predictors
    r1e1 <- rbinom(N, size = 1, prob = 0.5)
    r0e2 <- rbinom(N, size = 1, prob = 0.5)
    r1e2 <- rbinom(N, size = 1, prob = 0.5)
    r0e3 <- rbinom(N, size = 1, prob = 0.5)
    r1e3 <- rbinom(N, size = 1, prob = 0.5)
    
    # IM
    lin_IM <-
      b_r1e1 * r1e1 +
      b_r0e2 * r0e2 +
      b_r1e2 * r1e2 +
      b_r0e3 * r0e3 +
      b_r1e3 * r1e3 +
      b_TR   * TR +
      b_ADT  * ADT +
      b_PEB  * PEB_yes
    
    IM <- lin_IM + rnorm(N, mean = 0, sd = 1)
    
    # EEF
    lin_EEF <-
      c_r1e1 * r1e1 +
      c_r0e2 * r0e2 +
      c_r1e2 * r1e2 +
      c_r0e3 * r0e3 +
      c_r1e3 * r1e3 +
      c_IM   * IM +
      c_TR   * TR +
      c_ADT  * ADT +
      c_PEB  * PEB_yes
    
    # EEC
    lin_EEC <-
      d_r1e1 * r1e1 +
      d_r0e2 * r0e2 +
      d_r1e2 * r1e2 +
      d_r0e3 * r0e3 +
      d_r1e3 * r1e3 +
      d_IM   * IM +
      d_TR   * TR +
      d_ADT  * ADT +
      d_PEB  * PEB_yes
    
    # Correlated residuals for EEF and EEC
    residuals <- MASS::mvrnorm(
      n = N,
      mu = c(0, 0),
      Sigma = matrix(
        c(
          1,           d_EEF_EEC,
          d_EEF_EEC,  1
        ),
        nrow = 2,
        byrow = TRUE
      )
    )
    
    # Outcomes
    EEF <- lin_EEF + residuals[, 1]
    EEC <- lin_EEC + residuals[, 2]
    
    # Return dataset
    dat <- data.frame(
      r1e1, r0e2, r1e2, r0e3, r1e3,
      IM, EEF, EEC, TR, ADT, PEB_yes
    )
    
    dat
  })
}

1. SEM model with mediation

MediationModel <- '
  #### structural regressions
  IM ~ b_r1e1*r1e1 +
       b_r0e2*r0e2 +
       b_r1e2*r1e2 +
       b_r0e3*r0e3 +
       b_r1e3*r1e3 +
       b_TR*TR +
       b_ADT*ADT +
       b_PEB*PEB_yes

  EEF ~ c_r1e1*r1e1 +
        c_r0e2*r0e2 +
        c_r1e2*r1e2 +
        c_r0e3*r0e3 +
        c_r1e3*r1e3 +
        c_IM*IM +
        c_TR*TR +
        c_ADT*ADT +
        c_PEB*PEB_yes

  EEC ~ d_r1e1*r1e1 +
        d_r0e2*r0e2 +
        d_r1e2*r1e2 +
        d_r0e3*r0e3 +
        d_r1e3*r1e3 +
        d_IM*IM +
        d_TR*TR +
        d_ADT*ADT +
        d_PEB*PEB_yes
      
        EEF ~~ EEC
        
  # Indirect effects through IM -> EEF
  ind_r1e1_EEF := b_r1e1*c_IM
  ind_r0e2_EEF := b_r0e2*c_IM
  ind_r1e2_EEF := b_r1e2*c_IM
  ind_r0e3_EEF := b_r0e3*c_IM
  ind_r1e3_EEF := b_r1e3*c_IM

  # Indirect effects through IM -> EEC
  ind_r1e1_EEC := b_r1e1*d_IM
  ind_r0e2_EEC := b_r0e2*d_IM
  ind_r1e2_EEC := b_r1e2*d_IM
  ind_r0e3_EEC := b_r0e3*d_IM
  ind_r1e3_EEC := b_r1e3*d_IM
'

Power analysis for different sample sizes

Indirect effects

# Summary across samples
cat("Summary:\n")
Summary:
power_summary <- do.call(
  rbind,
  lapply(names(results), function(N) {
    data.frame(
      N = as.numeric(N),
      as.list(results[[N]]$power),
      check.names = FALSE
    )
  })
)
rownames(power_summary) <- NULL
print(power_summary)
    N ind_r1e1_EEF ind_r0e2_EEF ind_r1e2_EEF ind_r0e3_EEF ind_r1e3_EEF
1 200       0.3925       0.4090       0.3975       0.4255        0.422
2 250       0.4950       0.4830       0.4860       0.4890        0.473
3 270       0.5355       0.5015       0.5105       0.5225        0.537
  ind_r1e1_EEC ind_r0e2_EEC ind_r1e2_EEC ind_r0e3_EEC ind_r1e3_EEC
1       0.3900       0.4090       0.4000       0.4225       0.4180
2       0.5000       0.4840       0.4815       0.4895       0.4770
3       0.5385       0.5055       0.5185       0.5235       0.5365

2. SEM without mediation

NoMediationModel <- '
  #### structural regressions
  EEF ~ c_r1e1*r1e1 +
        c_r0e2*r0e2 +
        c_r1e2*r1e2 +
        c_r0e3*r0e3 +
        c_r1e3*r1e3 +
        c_IM*IM +
        c_TR*TR +
        c_ADT*ADT +
        c_PEB*PEB_yes

  EEC ~ d_r1e1*r1e1 +
        d_r0e2*r0e2 +
        d_r1e2*r1e2 +
        d_r0e3*r0e3 +
        d_r1e3*r1e3 +
        d_IM*IM +
        d_TR*TR +
        d_ADT*ADT +
        d_PEB*PEB_yes
      
        EEF ~~ EEC
'

Power analysis for main effects

Summary:
    N c_r1e1 c_r0e2 c_r1e2 c_r0e3 c_r1e3 d_r1e1 d_r0e2 d_r1e2 d_r0e3 d_r1e3
1 200 0.7365 0.7240  0.733  0.738 0.7280 0.7320 0.7315 0.7305 0.7390 0.7385
2 250 0.8230 0.8095  0.828  0.815 0.8245 0.8315 0.8140 0.8180 0.8050 0.8155
3 270 0.8485 0.8500  0.853  0.843 0.8450 0.8500 0.8550 0.8565 0.8495 0.8480

Alternative b) SIMSEM