#' Invariance matrices
#'
#' \code{InvarianceMatrices} creates weak, strong, and strict invariance
#' matrices and returns them as a list.
#'
#' This function takes tests each pair of groups for weak, strong, and strict
#' measurement invariance. The change in the CFI is calculated for each type of
#' measurement invariance, as recommended by Cheung & Rensovld (2002).
#'
#' Because each pair of groups is examined, computational load (and thus
#' runtime) can become high when there are many groups. For this reason, pairs
#' of groups can be assessed in parallel with the \code{number.cores} argument.
#'
#' @param data A data frame that contains the data for each group as well as
#' a column that indicates the group of each row. The data should be organized
#' such that each indicator has 1 column with data from all groups,
#' @param group.variable A string indicating the name of the grouping column in
#' \code{data}
#' @param lavaan.equations A string describing the structural equation model
#' to examine measurement invariance (typically a confirmatory factor analysis
#' model). This should be written in lavaan model syntax. See
#' \code{\link[lavaan]{lavaan}} and
#' \url{http://lavaan.ugent.be/tutorial/syntax1.html}.
#' @param number.cores An integer specifying the number of (logical) cores to
#' use. By default, only 1 core is used. Using all cores could slow down other
#' programs running simultaneously. Thus, it is recommended to use 1 or 2 less
#' cores than the maximum possible. See \code{\link[parallel]{detectCores}}.
#' @return Thus function returns a list containing the weak, strong, and strict
#' invariance matrices.
#'
#' @export
InvarianceMatrices <- function(data, group.variable, lavaan.equations,
number.cores = 1L){
# setting up parallel processing
parallel.cluster <- parallel::makeCluster(number.cores)
doParallel::registerDoParallel(parallel.cluster)
# creating a matrix that contains all group pairs
group.combinations <- t(combn(unique(data[, group.variable]), 2))
# copying %dopar% so that it works
'%dopar%' <- foreach::'%dopar%'
# calculating delta CFIs for each pair of groups
invariance.by.pair <- foreach::foreach(
group.combinations.row = 1:nrow(group.combinations),
.combine = rbind, .inorder = T, .packages = "lavaan") %dopar% {
# setting groups for this combination
temp.group.1 <- group.combinations[group.combinations.row, 1]
temp.group.2 <- group.combinations[group.combinations.row, 2]
# subsetting data to just those groups
temp.data <- data[grep(paste0(temp.group.1, "|", temp.group.2),
data[, group.variable]), ]
# running CFA models and extracting CFIs
temp.configural.model <- lavaan::cfa(lavaan.equations, temp.data,
group = group.variable)
temp.configural.cfi <- lavaan::fitMeasures(temp.configural.model,
fit.measures = "cfi")
temp.weak.model <- lavaan::cfa(lavaan.equations, temp.data,
group = group.variable,
group.equal = "loadings")
temp.weak.cfi <- lavaan::fitMeasures(temp.weak.model, fit.measures = "cfi")
temp.strong.model <- lavaan::cfa(lavaan.equations, temp.data,
group = group.variable,
group.equal = c("loadings", "intercepts"))
temp.strong.cfi <- lavaan::fitMeasures(temp.strong.model,
fit.measures = "cfi")
temp.strict.model <- lavaan::cfa(lavaan.equations, temp.data,
group = group.variable,
group.equal = c("loadings", "intercepts",
"residuals"))
temp.strict.cfi <- lavaan::fitMeasures(temp.strict.model,
fit.measures = "cfi")
# calculating delta CFIs
temp.weak.delta.cfi <- temp.configural.cfi - temp.weak.cfi
temp.strong.delta.cfi <- temp.weak.cfi - temp.strong.cfi
temp.strict.delta.cfi <- temp.strong.cfi - temp.strict.cfi
temp.delta.cfi.matrix <- matrix(data = c(temp.weak.delta.cfi,
temp.strong.delta.cfi,
temp.strict.delta.cfi),
nrow = 1, ncol = 3)
rownames(temp.delta.cfi.matrix) <- paste(temp.group.1, temp.group.2)
colnames(temp.delta.cfi.matrix) <- c("weak.delta.cfi", "strong.delta.cfi",
"strict.delta.cfi")
return(temp.delta.cfi.matrix)
}
# stopping parallel processing cluster
parallel::stopCluster(parallel.cluster)
# creating invariance matrices from delta CFIs for each pair of groups
# creating matrix, naming rows and columns, then filling in matrix
# weak
weak.invariance.matrix <- matrix(
nrow = length(unique(data[, group.variable])),
ncol = length(unique(data[, group.variable])))
colnames(weak.invariance.matrix) <- rownames(weak.invariance.matrix) <-
unique(data[, group.variable])
weak.invariance.matrix[lower.tri(weak.invariance.matrix)] <-
invariance.by.pair[, 1]
weak.invariance.matrix <- t(weak.invariance.matrix)
weak.invariance.matrix[lower.tri(weak.invariance.matrix)] <-
invariance.by.pair[, 1]
# strong
strong.invariance.matrix <- matrix(
nrow = length(unique(data[, group.variable])),
ncol = length(unique(data[, group.variable])))
colnames(strong.invariance.matrix) <- rownames(strong.invariance.matrix) <-
unique(data[, group.variable])
strong.invariance.matrix[lower.tri(strong.invariance.matrix)] <-
invariance.by.pair[, 2]
strong.invariance.matrix <- t(strong.invariance.matrix)
strong.invariance.matrix[lower.tri(strong.invariance.matrix)] <-
invariance.by.pair[, 2]
# strict
strict.invariance.matrix <- matrix(
nrow = length(unique(data[, group.variable])),
ncol = length(unique(data[, group.variable])))
colnames(strict.invariance.matrix) <- rownames(strict.invariance.matrix) <-
unique(data[, group.variable])
strict.invariance.matrix[lower.tri(strict.invariance.matrix)] <-
invariance.by.pair[, 3]
strict.invariance.matrix <- t(strict.invariance.matrix)
strict.invariance.matrix[lower.tri(strict.invariance.matrix)] <-
invariance.by.pair[, 3]
#returning invariance matices
return(list(weak.invariance.matrix = weak.invariance.matrix,
strong.invariance.matrix = strong.invariance.matrix,
strict.invariance.matrix = strict.invariance.matrix))
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.