Chapter 9 Statistical Power for all parameters via Monte Carlo simulation (p. .

Begin with a Plausible Model

As explained in the book, one simple way to proceed is to specify a model with plausible values. In this case, variables with unit variance and intra-class correlations of .49. The correlation between the factors is assumed to be 0.8

Plausible Initial Model
Plausible Initial Model
# ============================================================
# Monte Carlo in R (lavaan) with:
#  - Data generation from hand-entered Sigma (MVN)
#  - "True values" parsed from pop_model_raw (value*LABEL*var style),
#    including labeled intercepts like: Alc3M~0*IAlc*1;
# 1.) Specify missingness
# 2.) Specify the Covariance Model
# 3.) Specify the true model values
# 4.) Specify the (possibly mis-specified) model
#  - For ALL labeled target parameters, report:
#      * Mean & Median estimates
#      * Coverage (95% CI) + MCSE
#      * # significant + proportion
#      * Bias reporting (gold-standard):
#          - Relative Bias (%)  when true != 0
#          - Scaled Bias (×100) when true == 0   [= 100*(est-true)]
#          - Absolute Bias      [= mean(est-true)]
#          - RMSE               [= sqrt(mean((est-true)^2))]
#          - Std. Bias          [= (mean(est-true))/sd(est)]  (flag if sd=0)
#      * MCSE for Absolute Bias and RMSE (via SD/sqrt(m) approximation)
#        and MCSE for Coverage (binomial)
#  - Grouped APA-ish table (flextable), with blocks
# ============================================================

library(MASS)      # mvrnorm
library(lavaan)
## Warning: package 'lavaan' was built under R version 4.5.2
## This is lavaan 0.6-21
## lavaan is FREE software! Please report any bugs.
library(flextable)
## Warning: package 'flextable' was built under R version 4.5.2
set.seed(12345)

# ---------------- SETTINGS ----------------
N     <- 100
R     <- 1000  # Use 1000+ for stable MCSEs
alpha <- 0.05

# 1.) Optional MCAR missingness. Use zeros if no missing values are anticipated
pmiss <- c(Alc3M = 0.1, FivePlus = 0.1, Thurs = 0.1, Fri = 0.1, Sat = 0.1)

make_mcar_missing <- function(dat, pmiss) {
  for (v in names(pmiss)) dat[runif(nrow(dat)) < pmiss[[v]], v] <- NA
  dat
}

# ---------------- HAND-ENTERED COVARIANCE MATRIX ----------------
vars <- c("Alc3M", "FivePlus", "Thurs", "Fri", "Sat")

# 2.) Use the predicted covariance matrix from Onys as a quick way to get this
Sigma <- matrix(
  c(
    1.0000, 0.4900, 0.3920, 0.3920, 0.3920,
    0.4900, 1.0000, 0.3920, 0.3920, 0.3920,
    0.3920, 0.3920, 1.0000, 0.4900, 0.4900,
    0.3920, 0.3920, 0.4900, 1.0000, 0.4900,
    0.3920, 0.3920, 0.4900, 0.4900, 1.0000
  ),
  nrow = 5, byrow = TRUE,
  dimnames = list(vars, vars)
)

mu <- setNames(rep(0, length(vars)), vars)

# 3,)- POPULATION PARAMETER MODEL (TRUTH) Label the parameters with start values as shown
pop_model_raw <- "
Use=~0.7*L1*Alc3M
Use=~0.7*L2*FivePlus
PastWl=~0.7*L3*Thurs
PastWl=~0.7*L4*Fri
PastWl=~0.7*L5*Sat

Alc3M ~~ 0.51*VAR_Alc3M*Alc3M
FivePlus ~~ 0.51*VAR_FivePlus*FivePlus
Thurs ~~ 0.51*VAR_Thurs*Thurs
Fri ~~ 0.51*VAR_Fri*Fri
Sat ~~ 0.51*VAR_Sat*Sat

Use ~~ 1.0*Use
PastWl ~~ 1.0*PastWl
Use ~~ 0.8*Corr*PastWl

Alc3M~0*IAlc*1;
FivePlus~0*I5Plus*1;
Thurs~0*ITh*1;
Fri~0*IFr*1;
Sat~0*ISa*1;
"

# ============================================================
# Parse TRUE VALUES from pop_model_raw, including labeled intercepts
# ============================================================
parse_true_values <- function(raw) {
  lines <- trimws(unlist(strsplit(raw, "\n", fixed = TRUE)))
  lines <- lines[nzchar(lines)]
  lines <- lines[!grepl("^!", lines)]
  lines <- gsub(";", "", lines, fixed = TRUE)
  lines <- gsub("\\s+", " ", lines)
  
  true <- list()
  
  load_re <- "^([A-Za-z][A-Za-z0-9_]*)\\s*=~\\s*([+-]?[0-9]*\\.?[0-9]+)\\s*\\*\\s*(L[0-9]+)\\s*\\*\\s*([A-Za-z][A-Za-z0-9_]*)$"
  resv_re <- "^([A-Za-z][A-Za-z0-9_]*)\\s*~~\\s*([+-]?[0-9]*\\.?[0-9]+)\\s*\\*\\s*(VAR_[A-Za-z0-9_]+)\\s*\\*\\s*\\1$"
  cov_re  <- "^([A-Za-z][A-Za-z0-9_]*)\\s*~~\\s*([+-]?[0-9]*\\.?[0-9]+)\\s*\\*\\s*([A-Za-z][A-Za-z0-9_]*)\\s*\\*\\s*([A-Za-z][A-Za-z0-9_]*)$"
  int_lab_re <- "^([A-Za-z][A-Za-z0-9_]*)\\s*~\\s*([+-]?[0-9]*\\.?[0-9]+)\\s*\\*\\s*([A-Za-z][A-Za-z0-9_]*)\\s*\\*\\s*1$"
  
  for (ln in lines) {
    if (grepl("=~", ln, fixed = TRUE)) {
      rr <- regmatches(ln, regexec(load_re, ln))[[1]]
      if (length(rr) == 5) true[[ rr[4] ]] <- as.numeric(rr[3])   # Lk
      next
    }
    if (grepl("~~", ln, fixed = TRUE) && grepl("VAR_", ln, fixed = TRUE)) {
      rr <- regmatches(ln, regexec(resv_re, ln))[[1]]
      if (length(rr) == 4) true[[ rr[4] ]] <- as.numeric(rr[3])   # VAR_*
      next
    }
    if (grepl("~~", ln, fixed = TRUE) && grepl("\\*", ln)) {
      rr <- regmatches(ln, regexec(cov_re, ln))[[1]]
      if (length(rr) == 5) true[[ rr[4] ]] <- as.numeric(rr[3])   # Corr (or other)
      next
    }
    if (grepl("~", ln, fixed = TRUE) && !grepl("~~", ln, fixed = TRUE) && !grepl("=~", ln, fixed = TRUE)) {
      rr <- regmatches(ln, regexec(int_lab_re, ln))[[1]]
      if (length(rr) == 4) true[[ rr[4] ]] <- as.numeric(rr[3])   # IAlc etc.
      next
    }
  }
  
  unlist(true)
}

true_by_label <- parse_true_values(pop_model_raw)

# 4.) Specify the ANALYSIS MODEL (to be estimated each replication) ----------------
analysis_model <- "
  Use    =~ L1*Alc3M + L2*FivePlus
  PastWl =~ L3*Thurs + L4*Fri + L5*Sat

  Alc3M    ~~ VAR_Alc3M*Alc3M
  FivePlus ~~ VAR_FivePlus*FivePlus
  Thurs    ~~ VAR_Thurs*Thurs
  Fri      ~~ VAR_Fri*Fri
  Sat      ~~ VAR_Sat*Sat

  Use ~~ 1*Use
  PastWl ~~ 1*PastWl
  Use ~~ Corr*PastWl

  Alc3M    ~ IAlc*1
  FivePlus ~ I5Plus*1
  Thurs    ~ ITh*1
  Fri      ~ IFr*1
  Sat      ~ ISa*1
"

extract_labeled <- function(fit) {
  pe <- parameterEstimates(fit, ci = TRUE)
  pe <- pe[pe$label != "", ]
  data.frame(
    label = pe$label,
    est = pe$est,
    p = pe$pvalue,
    ci.lower = pe$ci.lower,
    ci.upper = pe$ci.upper,
    stringsAsFactors = FALSE
  )
}

# Determine targets (labels) from a probe fit, then intersect with true values
fit_probe <- lavaan(
  analysis_model,
  sample.cov = Sigma,
  sample.mean = as.numeric(mu),
  sample.nobs = 1e5,
  meanstructure = TRUE,
  fixed.x = FALSE
)

targets <- extract_labeled(fit_probe)$label
targets <- targets[targets %in% names(true_by_label)]
K <- length(targets)
if (K == 0) stop("No overlapping parameter labels between analysis model and population model.")

# ---------------- STORAGE ----------------
conv      <- logical(R)
est_store <- matrix(NA, R, K, dimnames = list(NULL, targets))
cover     <- matrix(NA, R, K, dimnames = list(NULL, targets))
sig       <- matrix(NA, R, K, dimnames = list(NULL, targets))

# For bias/RMSE we also store raw error (est-true)
err_store <- matrix(NA, R, K, dimnames = list(NULL, targets))

# ---------------- MONTE CARLO ----------------
tv_all <- as.numeric(true_by_label[targets])

for (r in 1:R) {
  X <- MASS::mvrnorm(n = N, mu = mu, Sigma = Sigma)
  dat <- as.data.frame(X); names(dat) <- vars
  dat <- make_mcar_missing(dat, pmiss)
  
  fit <- tryCatch(
    lavaan(analysis_model, data = dat, missing = "fiml", fixed.x = FALSE),
    error = function(e) NULL
  )
  if (is.null(fit) || !inspect(fit, "converged")) next
  
  conv[r] <- TRUE
  
  est <- extract_labeled(fit)
  est <- est[match(targets, est$label), ]
  
  est_store[r, ] <- est$est
  err_store[r, ] <- est$est - tv_all
  
  cover[r, ] <- (tv_all >= est$ci.lower) & (tv_all <= est$ci.upper)
  sig[r, ]   <- (est$p < alpha)
}
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_data_full():  
##    some cases are empty and will be ignored: 65.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    some estimated ov variances are negative
## Warning: lavaan->lav_object_post_check():  
##    some estimated ov variances are negative
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_data_full():  
##    some cases are empty and will be ignored: 25.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
## Warning: lavaan->lav_object_post_check():  
##    covariance matrix of latent variables is not positive definite ; use 
##    lavInspect(fit, "cov.lv") to investigate.
idx <- which(conv)
m <- length(idx)
cat("Replications:", R, "\n")
## Replications: 1000
cat("Converged:", m, "\n")
## Converged: 1000
cat("Convergence rate:", round(mean(conv), 3), "\n\n")
## Convergence rate: 1
if (m == 0) stop("No converged replications.")

# ---------------- SUMMARIES ----------------
mean_est   <- colMeans(est_store[idx, , drop = FALSE], na.rm = TRUE)
median_est <- apply(est_store[idx, , drop = FALSE], 2, median, na.rm = TRUE)

# Coverage + MCSE
coverage <- colMeans(cover[idx, , drop = FALSE], na.rm = TRUE)
mcse_cov <- sqrt(coverage * (1 - coverage) / m)

# Significance
n_sig    <- colSums(sig[idx, , drop = FALSE], na.rm = TRUE)
prop_sig <- n_sig / m

# Errors and bias metrics
err_mat <- err_store[idx, , drop = FALSE]

abs_bias <- colMeans(err_mat, na.rm = TRUE)                    # mean(est-true)
rmse     <- sqrt(colMeans(err_mat^2, na.rm = TRUE))            # sqrt(mean((est-true)^2))

# MCSE for absolute bias: sd(errors)/sqrt(m)
mcse_abs_bias <- apply(err_mat, 2, function(x) {
  x <- x[is.finite(x)]
  if (length(x) <= 1) return(NA_real_)
  sd(x) / sqrt(length(x))
})

# MCSE for RMSE: delta-method-ish approximation using SD of squared error
mcse_rmse <- apply(err_mat, 2, function(x) {
  x <- x[is.finite(x)]
  if (length(x) <= 2) return(NA_real_)
  se_mse <- sd(x^2) / sqrt(length(x))
  mse <- mean(x^2)
  if (!is.finite(mse) || mse <= 0) return(NA_real_)
  se_mse / (2 * sqrt(mse))
})

# Standardized bias: mean(error)/sd(est)
sd_est <- apply(est_store[idx, , drop = FALSE], 2, sd, na.rm = TRUE)
std_bias <- ifelse(sd_est > 0, abs_bias / sd_est, NA_real_)

# Relative bias (%) for true != 0, scaled bias (×100) for true==0
true_vals <- tv_all

rel_bias_pct <- ifelse(abs(true_vals) > .Machine$double.eps,
                       (mean_est - true_vals) / true_vals * 100,
                       NA_real_)

scaled_bias_100 <- ifelse(abs(true_vals) <= .Machine$double.eps,
                          100 * (mean_est - true_vals),
                          NA_real_)

# MCSE for relative/scaled bias computed from replication-level quantities
# (use stored errors + scaling rules)
mcse_rel_or_scaled <- function(j) {
  tv <- true_vals[j]
  e  <- err_mat[, j]
  e  <- e[is.finite(e)]
  if (length(e) <= 1) return(NA_real_)
  if (abs(tv) > .Machine$double.eps) {
    z <- (e / tv) * 100
  } else {
    z <- 100 * e
  }
  sd(z) / sqrt(length(z))
}
mcse_rel_scaled <- vapply(seq_len(K), mcse_rel_or_scaled, numeric(1))

# ---------------- GROUP PARAMETER BLOCKS ----------------
param_block <- function(lbl) {
  if (grepl("^L\\d+$", lbl)) return("Loadings (λ)")
  if (lbl == "Corr")         return("Factor covariance (ψ)")
  if (grepl("^VAR_", lbl))   return("Residual variances (θ)")
  if (grepl("^I", lbl))      return("Intercepts (τ)")  # IAlc, I5Plus, ...
  "Other"
}

results <- data.frame(
  Block = vapply(targets, param_block, character(1)),
  Parameter = targets,
  TrueValue = true_vals,
  MeanEstimate = mean_est,
  MedianEstimate = median_est,
  Coverage95 = coverage,
  MCSE_Coverage = mcse_cov,
  N_Significant = n_sig,
  Prop_Significant = prop_sig,
  RelBiasPct = rel_bias_pct,              # only for true != 0
  ScaledBias_x100 = scaled_bias_100,      # only for true == 0
  MCSE_RelOrScaled = mcse_rel_scaled,
  AbsBias = abs_bias,
  MCSE_AbsBias = mcse_abs_bias,
  RMSE = rmse,
  MCSE_RMSE = mcse_rmse,
  StdBias = std_bias,
  row.names = NULL,
  check.names = FALSE
)

block_order <- c("Loadings (λ)", "Factor covariance (ψ)", "Residual variances (θ)", "Intercepts (τ)", "Other")
results$Block <- factor(results$Block, levels = block_order)
results <- results[order(results$Block, results$Parameter), ]

# If the true value is zero (as for the intercepts here) specify a bias just as the difference (i.e., not divided by true value)
print(results, digits = 3, row.names = FALSE)
##                   Block    Parameter TrueValue MeanEstimate MedianEstimate
##            Loadings (λ)           L1      0.70     0.686509        0.68856
##            Loadings (λ)           L2      0.70     0.698018        0.69998
##            Loadings (λ)           L3      0.70     0.693350        0.69675
##            Loadings (λ)           L4      0.70     0.693870        0.69269
##            Loadings (λ)           L5      0.70     0.700019        0.69959
##   Factor covariance (ψ)         Corr      0.80     0.794285        0.79475
##  Residual variances (θ)    VAR_Alc3M      0.51     0.499778        0.50429
##  Residual variances (θ) VAR_FivePlus      0.51     0.487079        0.48662
##  Residual variances (θ)      VAR_Fri      0.51     0.493988        0.49555
##  Residual variances (θ)      VAR_Sat      0.51     0.496877        0.49743
##  Residual variances (θ)    VAR_Thurs      0.51     0.500974        0.50065
##          Intercepts (τ)       I5Plus      0.00    -0.004116       -0.00479
##          Intercepts (τ)         IAlc      0.00    -0.000308        0.00594
##          Intercepts (τ)          IFr      0.00     0.001470        0.00248
##          Intercepts (τ)          ISa      0.00     0.001687        0.00212
##          Intercepts (τ)          ITh      0.00    -0.000609        0.00208
##  Coverage95 MCSE_Coverage N_Significant Prop_Significant RelBiasPct
##       0.944       0.00727          1000            1.000   -1.92731
##       0.942       0.00739          1000            1.000   -0.28307
##       0.945       0.00721          1000            1.000   -0.94998
##       0.940       0.00751          1000            1.000   -0.87570
##       0.950       0.00689          1000            1.000    0.00268
##       0.955       0.00656          1000            1.000   -0.71439
##       0.947       0.00708           929            0.929   -2.00423
##       0.969       0.00548           922            0.922   -4.49440
##       0.919       0.00863           992            0.992   -3.13952
##       0.939       0.00757           990            0.990   -2.57308
##       0.930       0.00807           992            0.992   -1.76983
##       0.936       0.00774            64            0.064         NA
##       0.941       0.00745            59            0.059         NA
##       0.949       0.00696            51            0.051         NA
##       0.953       0.00669            47            0.047         NA
##       0.943       0.00733            57            0.057         NA
##  ScaledBias_x100 MCSE_RelOrScaled   AbsBias MCSE_AbsBias  RMSE MCSE_RMSE
##               NA            0.545 -1.35e-02      0.00381 0.121   0.00278
##               NA            0.558 -1.98e-03      0.00391 0.123   0.00268
##               NA            0.498 -6.65e-03      0.00349 0.110   0.00239
##               NA            0.503 -6.13e-03      0.00352 0.112   0.00263
##               NA            0.489  1.88e-05      0.00343 0.108   0.00245
##               NA            0.453 -5.72e-03      0.00362 0.115   0.00296
##               NA            0.874 -1.02e-02      0.00446 0.141   0.00373
##               NA            0.816 -2.29e-02      0.00416 0.134   0.00310
##               NA            0.705 -1.60e-02      0.00360 0.115   0.00290
##               NA            0.688 -1.31e-02      0.00351 0.112   0.00271
##               NA            0.713 -9.03e-03      0.00364 0.115   0.00273
##          -0.4116            0.339 -4.12e-03      0.00339 0.107   0.00241
##          -0.0308            0.329 -3.08e-04      0.00329 0.104   0.00233
##           0.1470            0.321  1.47e-03      0.00321 0.101   0.00228
##           0.1687            0.324  1.69e-03      0.00324 0.102   0.00229
##          -0.0609            0.327 -6.09e-04      0.00327 0.103   0.00237
##    StdBias
##  -0.111893
##  -0.016042
##  -0.060268
##  -0.055005
##   0.000173
##  -0.049883
##  -0.072537
##  -0.174131
##  -0.140731
##  -0.118230
##  -0.078522
##  -0.038362
##  -0.002956
##   0.014497
##   0.016455
##  -0.005883
# ---------------- APA-ish table (flextable) ----------------
apa_sim_table <- function(df, digits = 3) {
  tab <- df
  
  # round numeric columns
  num_cols <- setdiff(names(tab), c("Block", "Parameter"))
  for (cc in num_cols) tab[[cc]] <- round(tab[[cc]], digits)
  
  # Choose a manuscript-friendly subset of columns
  tab <- tab[, c(
    "Block", "Parameter", "TrueValue", "MeanEstimate", "MedianEstimate",
    "Coverage95", "MCSE_Coverage",
    "Prop_Significant",
    "RelBiasPct", "ScaledBias_x100", "MCSE_RelOrScaled",
    "AbsBias", "MCSE_AbsBias",
    "RMSE", "MCSE_RMSE",
    "StdBias"
  )]
  
  ft <- flextable(tab)
  ft <- theme_booktabs(ft)
  
  ft <- align(ft, j = 1:2, align = "left", part = "all")
  ft <- align(ft, j = 3:ncol(tab), align = "center", part = "all")
  
  ft <- merge_v(ft, j = "Block")
  ft <- valign(ft, j = "Block", valign = "top")
  
  ft <- set_header_labels(
    ft,
    Block = "",
    Parameter = "Parameter",
    TrueValue = "True",
    MeanEstimate = "Mean est.",
    MedianEstimate = "Median est.",
    Coverage95 = "Coverage",
    MCSE_Coverage = "MCSE(cov)",
    Prop_Significant = "Prop. sig",
    RelBiasPct = "Rel. bias (%)",
    ScaledBias_x100 = "Scaled bias (×100)",
    MCSE_RelOrScaled = "MCSE(bias)",
    AbsBias = "Abs. bias",
    MCSE_AbsBias = "MCSE(abs)",
    RMSE = "RMSE",
    MCSE_RMSE = "MCSE(RMSE)",
    StdBias = "Std. bias"
  )
  
  ft <- autofit(ft)
  ft
}

ft <- apa_sim_table(results, digits = 3)
ft

Parameter

True

Mean est.

Median est.

Coverage

MCSE(cov)

Prop. sig

Rel. bias (%)

Scaled bias (×100)

MCSE(bias)

Abs. bias

MCSE(abs)

RMSE

MCSE(RMSE)

Std. bias

Loadings (λ)

L1

0.70

0.687

0.689

0.944

0.007

1.000

-1.927

0.545

-0.013

0.004

0.121

0.003

-0.112

L2

0.70

0.698

0.700

0.942

0.007

1.000

-0.283

0.558

-0.002

0.004

0.123

0.003

-0.016

L3

0.70

0.693

0.697

0.945

0.007

1.000

-0.950

0.498

-0.007

0.003

0.110

0.002

-0.060

L4

0.70

0.694

0.693

0.940

0.008

1.000

-0.876

0.503

-0.006

0.004

0.112

0.003

-0.055

L5

0.70

0.700

0.700

0.950

0.007

1.000

0.003

0.489

0.000

0.003

0.108

0.002

0.000

Factor covariance (ψ)

Corr

0.80

0.794

0.795

0.955

0.007

1.000

-0.714

0.453

-0.006

0.004

0.115

0.003

-0.050

Residual variances (θ)

VAR_Alc3M

0.51

0.500

0.504

0.947

0.007

0.929

-2.004

0.874

-0.010

0.004

0.141

0.004

-0.073

VAR_FivePlus

0.51

0.487

0.487

0.969

0.005

0.922

-4.494

0.816

-0.023

0.004

0.134

0.003

-0.174

VAR_Fri

0.51

0.494

0.496

0.919

0.009

0.992

-3.140

0.705

-0.016

0.004

0.115

0.003

-0.141

VAR_Sat

0.51

0.497

0.497

0.939

0.008

0.990

-2.573

0.688

-0.013

0.004

0.112

0.003

-0.118

VAR_Thurs

0.51

0.501

0.501

0.930

0.008

0.992

-1.770

0.713

-0.009

0.004

0.115

0.003

-0.079

Intercepts (τ)

I5Plus

0.00

-0.004

-0.005

0.936

0.008

0.064

-0.412

0.339

-0.004

0.003

0.107

0.002

-0.038

IAlc

0.00

0.000

0.006

0.941

0.007

0.059

-0.031

0.329

0.000

0.003

0.104

0.002

-0.003

IFr

0.00

0.001

0.002

0.949

0.007

0.051

0.147

0.321

0.001

0.003

0.101

0.002

0.014

ISa

0.00

0.002

0.002

0.953

0.007

0.047

0.169

0.324

0.002

0.003

0.102

0.002

0.016

ITh

0.00

-0.001

0.002

0.943

0.007

0.057

-0.061

0.327

-0.001

0.003

0.103

0.002

-0.006