Purpose:
#simulate IQ data with measurement and a group difference, then recover the true size
library(kirkegaard)
## Loading required package: tidyverse
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.2 ──
## ✔ ggplot2 3.4.0 ✔ purrr 0.3.5
## ✔ tibble 3.1.8 ✔ dplyr 1.0.10
## ✔ tidyr 1.2.1 ✔ stringr 1.4.1
## ✔ readr 2.1.3 ✔ forcats 0.5.2
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag() masks stats::lag()
## Loading required package: magrittr
##
##
## Attaching package: 'magrittr'
##
##
## The following object is masked from 'package:purrr':
##
## set_names
##
##
## The following object is masked from 'package:tidyr':
##
## extract
##
##
## Loading required package: weights
##
## Loading required package: Hmisc
##
## Loading required package: lattice
##
## Loading required package: survival
##
## Loading required package: Formula
##
##
## Attaching package: 'Hmisc'
##
##
## The following objects are masked from 'package:dplyr':
##
## src, summarize
##
##
## The following objects are masked from 'package:base':
##
## format.pval, units
##
##
## Loading required package: assertthat
##
##
## Attaching package: 'assertthat'
##
##
## The following object is masked from 'package:tibble':
##
## has_name
##
##
## Loading required package: psych
##
##
## Attaching package: 'psych'
##
##
## The following object is masked from 'package:Hmisc':
##
## describe
##
##
## The following objects are masked from 'package:ggplot2':
##
## %+%, alpha
##
##
##
## Attaching package: 'kirkegaard'
##
##
## The following object is masked from 'package:psych':
##
## rescale
##
##
## The following object is masked from 'package:assertthat':
##
## are_equal
##
##
## The following objects are masked from 'package:purrr':
##
## is_logical, is_numeric
##
##
## The following object is masked from 'package:base':
##
## +
library(lavaan)
## This is lavaan 0.6-12
## lavaan is FREE software! Please report any bugs.
##
## Attaching package: 'lavaan'
##
## The following object is masked from 'package:psych':
##
## cor2cov
g_means = c(0, 1)
n_per_group = 1000
#error loading
error_loading = function(x) sqrt(1 - x^2)
#repeat simulation N times
set.seed(1)
sim_results = map_df(1:100, function(i) {
#g loadings
g_loadings = runif(10, min = .50, max = .90)
#simulate data
d = map_df(g_means, function(g_mean) {
#g
dd = tibble(
group = g_mean,
g = rnorm(n_per_group, mean = g_mean, sd = 1)
)
#make data for 10 tests
for (v in 1:10) {
dd[[str_glue("test_{v}")]] = dd$g * g_loadings[v] + rnorm(n_per_group, mean = 0, sd = error_loading(g_loadings[v]))
}
dd
})
#factor analysis
(fa_g = fa(d[-c(1:2)]))
g_efa_scores = fa_g$scores %>% as_tibble() %>% mutate(group = d$group)
#gap
cohen.d(g_efa_scores$MR1, group = g_efa_scores$group) ->
g_efa_gap
#recover factor loadings in EFA?
cor(fa_g$loadings[, 1], g_loadings)
#SEM
model1 = str_glue("
g_sem =~ {str_c('test_' + 1:10, collapse = ' + ')}
")
model1_fit = sem(model1, data = d, std.lv = T)
#recover loadings in SEM?
cor(
parameterEstimates(model1_fit, standardized = T) %>%
filter(rhs %in% ("test_" + 1:10), lhs == "g_sem") %>%
pull(std.all),
g_loadings
)
#g gap model
model2 = str_glue(
"
g_sem =~ {str_c('test_' + 1:10, collapse = ' + ')}
g_sem ~ group
")
model2_fit = sem(model2, data = d, std.lv = T)
parameterEstimates(model2_fit, standardized = T) %>%
filter(lhs == "g_sem", rhs == "group") %>%
pull(est) ->
g_sem_gap
tibble(
g_sem_gap = g_sem_gap,
g_efa_gap = g_efa_gap$cohen.d[2]
)
})
#results
#gap is 1, so SEM should be close to 1, and EFA should be a little lower
sim_results %>% describe2()
#raw results
sim_results