A paper I reviewed conducted an RI-CLPM, I wanted to verify the proposed sample size for 80% power. The authors proposed n = 400. I thought I would share the code, in case anybody wanted to have a go at a power analysis for RI-CLPM.
##
## Loaded powRICLPM (0.2.1):
##
## Author: Jeroen D. Mulder
## Documentation: https://jeroendmulder.github.io/powRICLPM
## Questions & issues: https://github.com/JeroenDMulder/powRICLPM/issues
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.2 ✔ tibble 3.3.0
## ✔ lubridate 1.9.3 ✔ tidyr 1.3.1
## ✔ purrr 1.0.4
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library("future")
library("furrr")
library("progressr")
library("ggplot2")
library("ggthemes")
library("wesanderson")
library("grateful")
library("papaja")
## Loading required package: tinylabels
Values from Manual.
## According to this `Phi`, the lagged effects are:
## • Autoregressive effect of A: 0.4
## • Autoregressive effect of B: 0.3
## • Cross-lagged effect of A -> B: 0.15
## • Cross-lagged effect of B -> A: 0.1
Below uses reasonable starting values from the manual.
However, I changed a key path of interest, this is .15 (A on B). (Authors proposed r = .15)
Number of reps is perhaps smallish (but I boosted it from example), you might want to boost it to 10k but for our purpose 1,000 seems sufficient. Note that this takes some time.
I have hidden the warnings error as lavaan
will print
various warnings for individual models.
## powRICLPM (0.2.1) simulated power for 153 experimental conditions.
##
##
## Table: SUMMARY OF ANALYSIS PER EXPERIMENTAL CONDITION
##
## Sample size Time points ICC Reliability Error Not converged Inadmissible
## ------------ ------------ ---- ------------ ------ -------------- -------------
## 200 3 0.3 1 0 4 177
## 250 3 0.3 1 0 6 135
## 300 3 0.3 1 0 3 88
## 350 3 0.3 1 0 5 50
## 400 3 0.3 1 0 3 58
## 450 3 0.3 1 0 4 47
## 500 3 0.3 1 0 3 27
## 550 3 0.3 1 0 6 16
## 600 3 0.3 1 0 8 22
## 650 3 0.3 1 0 4 11
## 700 3 0.3 1 0 5 11
## 750 3 0.3 1 0 5 8
## 800 3 0.3 1 0 10 10
## 850 3 0.3 1 0 3 5
## 900 3 0.3 1 0 10 1
## 950 3 0.3 1 0 7 1
## 1000 3 0.3 1 0 8 3
## 200 4 0.3 1 0 0 25
## 250 4 0.3 1 0 0 17
## 300 4 0.3 1 0 0 3
## 350 4 0.3 1 0 0 1
## 400 4 0.3 1 0 0 1
## 450 4 0.3 1 0 0 1
## 500 4 0.3 1 0 0 1
## 550 4 0.3 1 0 0 0
## 600 4 0.3 1 0 0 0
## 650 4 0.3 1 0 0 0
## 700 4 0.3 1 0 0 0
## 750 4 0.3 1 0 0 0
## 800 4 0.3 1 0 0 0
## 850 4 0.3 1 0 0 0
## 900 4 0.3 1 0 0 0
## 950 4 0.3 1 0 0 0
## 1000 4 0.3 1 0 0 0
## 200 5 0.3 1 0 0 5
## 250 5 0.3 1 0 0 0
## 300 5 0.3 1 0 0 0
## 350 5 0.3 1 0 0 0
## 400 5 0.3 1 0 0 0
## 450 5 0.3 1 0 0 0
## 500 5 0.3 1 0 0 0
## 550 5 0.3 1 0 0 0
## 600 5 0.3 1 0 0 0
## 650 5 0.3 1 0 0 0
## 700 5 0.3 1 0 0 0
## 750 5 0.3 1 0 0 0
## 800 5 0.3 1 0 0 0
## 850 5 0.3 1 0 0 0
## 900 5 0.3 1 0 0 0
## 950 5 0.3 1 0 0 0
## 1000 5 0.3 1 0 0 0
## 200 3 0.5 1 0 2 31
## 250 3 0.5 1 0 4 28
## 300 3 0.5 1 0 5 12
## 350 3 0.5 1 0 4 7
## 400 3 0.5 1 0 4 2
## 450 3 0.5 1 0 7 1
## 500 3 0.5 1 0 6 1
## 550 3 0.5 1 0 11 0
## 600 3 0.5 1 0 4 0
## 650 3 0.5 1 0 4 1
## 700 3 0.5 1 0 8 0
## 750 3 0.5 1 0 6 0
## 800 3 0.5 1 0 5 0
## 850 3 0.5 1 0 10 0
## 900 3 0.5 1 0 15 0
## 950 3 0.5 1 0 10 0
## 1000 3 0.5 1 0 8 0
## 200 4 0.5 1 0 0 1
## 250 4 0.5 1 0 0 0
## 300 4 0.5 1 0 0 0
## 350 4 0.5 1 0 0 1
## 400 4 0.5 1 0 0 0
## 450 4 0.5 1 0 0 0
## 500 4 0.5 1 0 0 0
## 550 4 0.5 1 0 0 0
## 600 4 0.5 1 0 0 0
## 650 4 0.5 1 0 0 0
## 700 4 0.5 1 0 0 0
## 750 4 0.5 1 0 0 0
## 800 4 0.5 1 0 0 0
## 850 4 0.5 1 0 0 0
## 900 4 0.5 1 0 0 0
## 950 4 0.5 1 0 0 0
## 1000 4 0.5 1 0 0 0
## 200 5 0.5 1 0 0 0
## 250 5 0.5 1 0 0 0
## 300 5 0.5 1 0 0 0
## 350 5 0.5 1 0 0 0
## 400 5 0.5 1 0 0 0
## 450 5 0.5 1 0 0 0
## 500 5 0.5 1 0 0 0
## 550 5 0.5 1 0 0 0
## 600 5 0.5 1 0 0 0
## 650 5 0.5 1 0 0 0
## 700 5 0.5 1 0 0 0
## 750 5 0.5 1 0 0 0
## 800 5 0.5 1 0 0 0
## 850 5 0.5 1 0 0 0
## 900 5 0.5 1 0 0 0
## 950 5 0.5 1 0 0 0
## 1000 5 0.5 1 0 0 0
## 200 3 0.7 1 0 11 10
## 250 3 0.7 1 0 5 4
## 300 3 0.7 1 0 8 2
## 350 3 0.7 1 0 9 0
## 400 3 0.7 1 0 8 1
## 450 3 0.7 1 0 10 0
## 500 3 0.7 1 0 8 0
## 550 3 0.7 1 0 10 0
## 600 3 0.7 1 0 12 0
## 650 3 0.7 1 0 7 0
## 700 3 0.7 1 0 10 0
## 750 3 0.7 1 0 11 0
## 800 3 0.7 1 0 7 0
## 850 3 0.7 1 0 15 0
## 900 3 0.7 1 0 16 0
## 950 3 0.7 1 0 5 0
## 1000 3 0.7 1 0 8 0
## 200 4 0.7 1 0 0 1
## 250 4 0.7 1 0 0 0
## 300 4 0.7 1 0 0 0
## 350 4 0.7 1 0 0 0
## 400 4 0.7 1 0 0 0
## 450 4 0.7 1 0 0 0
## 500 4 0.7 1 0 0 0
## 550 4 0.7 1 0 0 0
## 600 4 0.7 1 0 0 0
## 650 4 0.7 1 0 0 0
## 700 4 0.7 1 0 0 0
## 750 4 0.7 1 0 0 0
## 800 4 0.7 1 0 0 0
## 850 4 0.7 1 0 0 0
## 900 4 0.7 1 0 0 0
## 950 4 0.7 1 0 0 0
## 1000 4 0.7 1 0 0 0
## 200 5 0.7 1 0 0 0
## 250 5 0.7 1 0 0 0
## 300 5 0.7 1 0 0 0
## 350 5 0.7 1 0 0 0
## 400 5 0.7 1 0 0 0
## 450 5 0.7 1 0 0 0
## 500 5 0.7 1 0 0 0
## 550 5 0.7 1 0 0 0
## 600 5 0.7 1 0 0 0
## 650 5 0.7 1 0 0 0
## 700 5 0.7 1 0 0 0
## 750 5 0.7 1 0 0 0
## 800 5 0.7 1 0 0 0
## 850 5 0.7 1 0 0 0
## 900 5 0.7 1 0 0 0
## 950 5 0.7 1 0 0 0
## 1000 5 0.7 1 0 0 0
A on B –> .15
The graph could be further beautified, but I have left it here. And perhaps Tufte’s theme is not ideal.
plot(out_preliminary, parameter = "wB2~wA1") + theme_tufte() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
scale_color_manual(values = wes_palette("GrandBudapest1")) + scale_fill_manual(values = wes_palette("GrandBudapest1")) + guides(shape = "none")
This is a weaker effect.
B on A –> .1
plot(out_preliminary, parameter = "wA2~wB1") + theme_tufte() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) +
scale_color_manual(values = wes_palette("GrandBudapest1")) + scale_fill_manual(values = wes_palette("GrandBudapest1")) + guides(shape = "none")
The above simulations suggest that n = 400 does not seem to satisfy 80% power, under any scenario. The above code can be modified to other scenarios. Note that as described by the package author, there are assumptions made (e.g., Multivariate normality, etc) – see the paper by Mulder (2022). So, a broader range of scenarios might need to be explored for a particular design.
r_refs(file = "r-analysis-references.bib")
my_citation <- cite_r(file = "r-analysis-references.bib")
R (Version 4.4.2; R Core Team 2024) and the R-packages dplyr (Version 1.1.4; Wickham et al. 2023), forcats (Version 1.0.0; Wickham 2023a), furrr (Version 0.3.1; Vaughan and Dancho 2022), ggplot2 (Version 3.5.2; Wickham 2016), ggthemes (Version 5.1.0; Arnold 2024), grateful (Version 0.2.10; Rodriguez-Sanchez and Jackson 2023), lubridate (Version 1.9.3; Grolemund and Wickham 2011), papaja (Version 0.1.3; Aust and Barth 2024), powRICLPM (Version 0.2.1; Jeroen D. Mulder 2022), progressr (Version 0.15.1; Bengtsson 2024), purrr (Version 1.0.4; Wickham and Henry 2025), readr (Version 2.1.5; Wickham, Hester, and Bryan 2024), RJ-2021-048 (Bengtsson 2021, 2021, 2021, 2021, 2021, 2021), stringr (Version 1.5.1; Wickham 2023b), tibble (Version 3.3.0; Müller and Wickham 2025), tidyr (Version 1.3.1; Wickham, Vaughan, and Girlich 2024), tidyverse (Version 2.0.0; Wickham et al. 2019), tinylabels (Version 0.2.5; Barth 2025), and wesanderson (Version 0.3.7; Ram and Wickham 2023)
## R version 4.4.2 (2024-10-31)
## Platform: aarch64-apple-darwin20
## Running under: macOS Sequoia 15.5
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.4-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.0
##
## Random number generation:
## RNG: L'Ecuyer-CMRG
## Normal: Inversion
## Sample: Rejection
##
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
##
## time zone: Europe/London
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] papaja_0.1.3 tinylabels_0.2.5 grateful_0.2.10 wesanderson_0.3.7
## [5] ggthemes_5.1.0 progressr_0.15.1 furrr_0.3.1 future_1.49.0
## [9] lubridate_1.9.3 forcats_1.0.0 stringr_1.5.1 dplyr_1.1.4
## [13] purrr_1.0.4 readr_2.1.5 tidyr_1.3.1 tibble_3.3.0
## [17] ggplot2_3.5.2 tidyverse_2.0.0 powRICLPM_0.2.1
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.52 bslib_0.9.0
## [4] bayestestR_0.15.0 insight_0.20.5 lattice_0.22-6
## [7] tzdb_0.5.0 quadprog_1.5-8 vctrs_0.6.5
## [10] tools_4.4.2 generics_0.1.4 sandwich_3.1-1
## [13] stats4_4.4.2 parallel_4.4.2 datawizard_0.13.0
## [16] pkgconfig_2.0.3 Matrix_1.7-1 RColorBrewer_1.1-3
## [19] lifecycle_1.0.4 compiler_4.4.2 farver_2.1.2
## [22] mnormt_2.1.1 codetools_0.2-20 htmltools_0.5.8.1
## [25] sass_0.4.10 yaml_2.3.10 pillar_1.10.2
## [28] jquerylib_0.1.4 MASS_7.3-61 cachem_1.1.0
## [31] multcomp_1.4-26 parallelly_1.44.0 lavaan_0.6-19
## [34] tidyselect_1.2.1 digest_0.6.37 mvtnorm_1.3-3
## [37] stringi_1.8.7 listenv_0.9.1 labeling_0.4.3
## [40] splines_4.4.2 fastmap_1.2.0 grid_4.4.2
## [43] cli_3.6.5 magrittr_2.0.3 survival_3.7-0
## [46] TH.data_1.1-2 future.apply_1.11.3 pbivnorm_0.6.0
## [49] withr_3.0.2 scales_1.4.0 estimability_1.5.1
## [52] timechange_0.3.0 rmarkdown_2.29 emmeans_1.10.5
## [55] globals_0.18.0 zoo_1.8-14 hms_1.1.3
## [58] coda_0.19-4.1 evaluate_1.0.3 knitr_1.50
## [61] parameters_0.23.0 rlang_1.1.6 xtable_1.8-4
## [64] glue_1.8.0 rstudioapi_0.17.1 jsonlite_2.0.0
## [67] effectsize_0.8.9 R6_2.6.1