Subset data to include only participants used in the FA.
MI: Gender
# i) Configural model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "sex",
estimator = "MLR") -> fit.tp_sex_configural.cfa
# ii) Metric model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "sex",
estimator = "MLR",
group.equal = "loadings") -> fit.tp_sex_weak.cfa
## iii) Scalar model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "sex",
estimator = "MLR",
group.equal = c("loadings", "intercepts")) -> fit.tp_sex_strong.cfa
# iv) Residual model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df),
effect.coding = T,
missing = "pairwise",
meanstructure = T,
group = "sex",
estimator = "MLR",
group.equal = c("loadings", "intercepts", "residuals")) -> fit.tp_sex_strict.cfa
Results
Intepretation of results:
Configural vs metric (weak) model: If the chi-square difference
test between the configural and metric model is non-significant (p >
0.05), it suggests that constraining the loadings to be equal across
groups does not significantly worsen the model fit. This supports metric
invariance.
Metric (Weak) vs. Scalar (Strong) model: If the chi-square
difference test between the metric and scalar model is non-significant
(p > 0.05), it indicates that constraining both the loadings and
intercepts to be equal across groups does not significantly worsen the
model fit. This supports scalar invariance.
Scalar (Strong) vs. Residual (Strict) Model: If the chi-square
difference test between the scalar and residual model is non-significant
(p > 0.05), it suggests that constraining the residual variances to
be equal across groups does not significantly worsen the model
fit
# Pairwise comparisons using anova function
anova_configural_metric <- anova(fit.tp_sex_configural.cfa, fit.tp_sex_weak.cfa)
anova_metric_scalar <- anova(fit.tp_sex_weak.cfa, fit.tp_sex_strong.cfa)
anova_scalar_residual <- anova(fit.tp_sex_strong.cfa, fit.tp_sex_strict.cfa)
# Print pairwise comparison results
print(anova_configural_metric)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_configural.cfa 52 93415 93821 1864.5
## fit.tp_sex_weak.cfa 59 93431 93787 1894.8 3.5536 7 0.8295
print(anova_metric_scalar)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_weak.cfa 59 93431 93787 1894.8
## fit.tp_sex_strong.cfa 66 93442 93747 1919.6 24.937 7 0.0007785 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
print(anova_scalar_residual)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_strong.cfa 66 93442 93747 1919.6
## fit.tp_sex_strict.cfa 75 93786 94025 2281.4 26.68 9 0.001579 **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
# Extract fit indices for each model
fit_indices_configural <- fitMeasures(fit.tp_sex_configural.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_metric <- fitMeasures(fit.tp_sex_weak.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_scalar <- fitMeasures(fit.tp_sex_strong.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_residual <- fitMeasures(fit.tp_sex_strict.cfa, c("cfi", "tli", "rmsea", "srmr"))
# Calculate fit indices differences
cfi_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["cfi"] - fitmeasures(fit.tp_sex_strong.cfa)["cfi"]
tli_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["tli"] - fitmeasures(fit.tp_sex_strong.cfa)["tli"]
rmsea_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["rmsea"] - fitmeasures(fit.tp_sex_strong.cfa)["rmsea"]
srmr_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["srmr"] - fitmeasures(fit.tp_sex_strong.cfa)["srmr"]
cfi_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["cfi"] - fitmeasures(fit.tp_sex_strict.cfa)["cfi"]
tli_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["tli"] - fitmeasures(fit.tp_sex_strict.cfa)["tli"]
rmsea_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["rmsea"] - fitmeasures(fit.tp_sex_strict.cfa)["rmsea"]
srmr_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["srmr"] - fitmeasures(fit.tp_sex_strict.cfa)["srmr"]
# Create a data frame with the results
results_sex <- data.frame(
Comparison = c("Configural vs Metric", "Metric vs Scalar", "Scalar vs Residual"),
Chi_Square_Diff = c(anova_configural_metric$Chisq[2], anova_metric_scalar$Chisq[2], anova_scalar_residual$Chisq[2]),
DF_Diff = c(anova_configural_metric$Df[2], anova_metric_scalar$Df[2], anova_scalar_residual$Df[2]),
p_value = c(anova_configural_metric$`Pr(>Chisq)`[2], anova_metric_scalar$`Pr(>Chisq)`[2], anova_scalar_residual$`Pr(>Chisq)`[2]),
CFI = c(fit_indices_configural["cfi"], cfi_diff_metric_scalar, cfi_diff_scalar_residual),
TLI = c(fit_indices_configural["tli"], tli_diff_metric_scalar, tli_diff_scalar_residual),
RMSEA = c(fit_indices_configural["rmsea"], rmsea_diff_metric_scalar, rmsea_diff_scalar_residual),
SRMR = c(fit_indices_configural["srmr"], srmr_diff_metric_scalar, srmr_diff_scalar_residual)
)
# output model comparison indices
results_sex |>
kbl() |>
kable_styling(full_width = F)
|
Comparison
|
Chi_Square_Diff
|
DF_Diff
|
p_value
|
CFI
|
TLI
|
RMSEA
|
SRMR
|
|
Configural vs Metric
|
1894.784
|
59
|
0.8295162
|
0.9615500
|
0.9467615
|
0.0814967
|
0.0300867
|
|
Metric vs Scalar
|
1919.565
|
66
|
0.0007785
|
0.0003772
|
-0.0046290
|
0.0038460
|
-0.0003722
|
|
Scalar vs Residual
|
2281.380
|
75
|
0.0015795
|
0.0074846
|
0.0020377
|
-0.0017173
|
-0.0048051
|
# Compare the models
anova_results_sex <- anova(fit.tp_sex_configural.cfa, fit.tp_sex_weak.cfa, fit.tp_sex_strong.cfa, fit.tp_sex_strict.cfa)
# Print the comparison results
print(anova_results_sex)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_configural.cfa 52 93415 93821 1864.5
## fit.tp_sex_weak.cfa 59 93431 93787 1894.8 3.5536 7 0.8295162
## fit.tp_sex_strong.cfa 66 93442 93747 1919.6 24.9373 7 0.0007785
## fit.tp_sex_strict.cfa 75 93786 94025 2281.4 26.6804 9 0.0015795
##
## fit.tp_sex_configural.cfa
## fit.tp_sex_weak.cfa
## fit.tp_sex_strong.cfa ***
## fit.tp_sex_strict.cfa **
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova_results_sex |>
kbl() |>
kable_styling(full_width = F)
|
|
Df
|
AIC
|
BIC
|
Chisq
|
Chisq diff
|
Df diff
|
Pr(>Chisq)
|
|
fit.tp_sex_configural.cfa
|
52
|
93414.81
|
93821.30
|
1864.496
|
NA
|
NA
|
NA
|
|
fit.tp_sex_weak.cfa
|
59
|
93431.10
|
93786.77
|
1894.784
|
3.553598
|
7
|
0.8295162
|
|
fit.tp_sex_strong.cfa
|
66
|
93441.88
|
93746.74
|
1919.565
|
24.937263
|
7
|
0.0007785
|
|
fit.tp_sex_strict.cfa
|
75
|
93785.69
|
94025.23
|
2281.380
|
26.680420
|
9
|
0.0015795
|
# tabulate additional fit indices
fit_indices_sex <- data.frame(
Model = c("Configural", "Metric", "Scalar", "Residual"),
CFI = c(
fitMeasures(fit.tp_sex_configural.cfa, "cfi"),
fitMeasures(fit.tp_sex_weak.cfa, "cfi"),
fitMeasures(fit.tp_sex_strong.cfa, "cfi"),
fitMeasures(fit.tp_sex_strict.cfa, "cfi")
),
TLI = c(
fitMeasures(fit.tp_sex_configural.cfa, "tli"),
fitMeasures(fit.tp_sex_weak.cfa, "tli"),
fitMeasures(fit.tp_sex_strong.cfa, "tli"),
fitMeasures(fit.tp_sex_strict.cfa, "tli")
),
RMSEA = c(
fitMeasures(fit.tp_sex_configural.cfa, "rmsea"),
fitMeasures(fit.tp_sex_weak.cfa, "rmsea"),
fitMeasures(fit.tp_sex_strong.cfa, "rmsea"),
fitMeasures(fit.tp_sex_strict.cfa, "rmsea")
)
)
# output fit indices
fit_indices_sex |>
kbl() |>
kable_styling(full_width = F)
|
Model
|
CFI
|
TLI
|
RMSEA
|
|
Configural
|
0.9615500
|
0.9467615
|
0.0814967
|
|
Metric
|
0.9610559
|
0.9524751
|
0.0769995
|
|
Scalar
|
0.9606787
|
0.9571041
|
0.0731535
|
|
Residual
|
0.9531942
|
0.9550664
|
0.0748708
|
Latent means
plot
lavaan::parameterestimates(fit.tp_sex_strict.cfa) %>%
filter(grepl("abuse_.$", lhs) & op == "~1") %>%
ggplot(aes(x = lhs,
y = est,
group = as.factor(group),
color = as.factor(group),
ymin = ci.lower,
ymax = ci.upper)) +
geom_point(size = 3, alpha = 0.7) +
geom_errorbar(width = 0.3, position = position_dodge(width = 0.5), alpha = 0.7) +
geom_line(size = 1, position = position_dodge(width = 0.5), alpha = 0.7) +
# scale_color_brewer(palette = "Set1", labels = c("Female", "Male")) +
scale_color_lancet(labels=c("Female", "Male")) +
labs(x = "Subscale", y = "Latent mean", color = "Sex",
title = "Latent Means by Sex") +
# scale_x_discrete(labels = c("Authoritarianism",
# "Mediation",
# "Monitoring by Proxy",
# "Boundary Negotiation",
# "Technological Monitoring",
# "Co-Use")) +
theme_minimal() +
theme(
legend.position = "top",
legend.title = element_text(size = 12, face = "bold"),
legend.text = element_text(size = 10),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
axis.text.y = element_text(size = 10, color = "black"),
axis.title = element_text(size = 12, color = "black"),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
plot.subtitle = element_text(size = 12, hjust = 0.5)
)

MI: Country
MI by country was having convergence issues. Attempt to run for each
country separately.
Vietnam appeared to be problematic and could not produce a
solution.
unique_countries <- unique(inv_df$COUNTRY)
for (country in unique_countries) {
group_data <- inv_df[inv_df$COUNTRY == country, ]
cat("Running CFA for country:", country, "\n")
tryCatch({
fit <- lavaan::cfa(
model = abuse.model,
data = group_data,
meanstructure = TRUE,
missing = "pairwise",
estimator = "MLR"
)
# print(summary(fit))
}, error = function(e) {
cat("Error for country:", country, "\n", e$message, "\n")
})
}
## Running CFA for country: Ethiopia
## Running CFA for country: Kenya
## Running CFA for country: Mozambique
## Running CFA for country: Namibia
## Running CFA for country: Tanzania
## Running CFA for country: Uganda
## Running CFA for country: Cambodia
## Running CFA for country: Indonesia
## Running CFA for country: Malaysia
## Running CFA for country: Philippines
## Running CFA for country: Thailand
## Running CFA for country: Vietnam
Check if all countries are positive definite. In fact, no countries
are positive definite, which is problematic.
for(c in unique_countries) {
country_data <- inv_df[inv_df$COUNTRY == country, ]
cov_matrix <- cov(country_data[, c("abuse_a", "abuse_b", "abuse_c", "abuse_d", "abuse_e", "abuse_f",
"abuse_g", "abuse_h", "abuse_i")], use = "pairwise.complete.obs")
print(paste("Country:", c, "is positive definite (T/F) =", is.positive.definite(cov_matrix)))
}
## [1] "Country: Ethiopia is positive definite (T/F) = FALSE"
## [1] "Country: Kenya is positive definite (T/F) = FALSE"
## [1] "Country: Mozambique is positive definite (T/F) = FALSE"
## [1] "Country: Namibia is positive definite (T/F) = FALSE"
## [1] "Country: Tanzania is positive definite (T/F) = FALSE"
## [1] "Country: Uganda is positive definite (T/F) = FALSE"
## [1] "Country: Cambodia is positive definite (T/F) = FALSE"
## [1] "Country: Indonesia is positive definite (T/F) = FALSE"
## [1] "Country: Malaysia is positive definite (T/F) = FALSE"
## [1] "Country: Philippines is positive definite (T/F) = FALSE"
## [1] "Country: Thailand is positive definite (T/F) = FALSE"
## [1] "Country: Vietnam is positive definite (T/F) = FALSE"
# thus apply a log transform
country_data <- inv_df[inv_df$COUNTRY == "Vietnam", ]
Will try to log transform the data. Given that we have established
(above), that there is extreme zero skew in the data (for all items), it
is reasonable to apply a log transform.
Pre:transform statistics
# Specify the columns to transform
columns_to_transform <- c("abuse_a", "abuse_b", "abuse_c", "abuse_d", "abuse_e", "abuse_f", "abuse_g", "abuse_h", "abuse_i")
# pre transform statistics
summary(inv_df[columns_to_transform])
## abuse_a abuse_b abuse_c abuse_d abuse_e
## Min. :1.00 Min. :1.000 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.:1.00 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:1.000
## Median :1.00 Median :1.000 Median :1.000 Median :1.000 Median :1.000
## Mean :1.25 Mean :1.266 Mean :1.149 Mean :1.134 Mean :1.115
## 3rd Qu.:1.00 3rd Qu.:1.000 3rd Qu.:1.000 3rd Qu.:1.000 3rd Qu.:1.000
## Max. :5.00 Max. :5.000 Max. :5.000 Max. :5.000 Max. :5.000
## abuse_f abuse_g abuse_h abuse_i
## Min. :1.000 Min. :1.000 Min. :1.000 Min. :1.000
## 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:1.000 1st Qu.:1.000
## Median :1.000 Median :1.000 Median :1.000 Median :1.000
## Mean :1.086 Mean :1.091 Mean :1.089 Mean :1.082
## 3rd Qu.:1.000 3rd Qu.:1.000 3rd Qu.:1.000 3rd Qu.:1.000
## Max. :5.000 Max. :5.000 Max. :5.000 Max. :5.000
# save pre-transform
inv_df_pre <- inv_df
# Apply log transformation using lapply
inv_df[columns_to_transform] <- lapply(inv_df[columns_to_transform], function(x) log(x + 1))
Post-transform statistics:
# post transform statistics
summary(inv_df[columns_to_transform])
## abuse_a abuse_b abuse_c abuse_d
## Min. :0.6931 Min. :0.6931 Min. :0.6931 Min. :0.6931
## 1st Qu.:0.6931 1st Qu.:0.6931 1st Qu.:0.6931 1st Qu.:0.6931
## Median :0.6931 Median :0.6931 Median :0.6931 Median :0.6931
## Mean :0.7816 Mean :0.7857 Mean :0.7457 Mean :0.7402
## 3rd Qu.:0.6931 3rd Qu.:0.6931 3rd Qu.:0.6931 3rd Qu.:0.6931
## Max. :1.7918 Max. :1.7918 Max. :1.7918 Max. :1.7918
## abuse_e abuse_f abuse_g abuse_h
## Min. :0.6931 Min. :0.6931 Min. :0.6931 Min. :0.6931
## 1st Qu.:0.6931 1st Qu.:0.6931 1st Qu.:0.6931 1st Qu.:0.6931
## Median :0.6931 Median :0.6931 Median :0.6931 Median :0.6931
## Mean :0.7330 Mean :0.7228 Mean :0.7241 Mean :0.7237
## 3rd Qu.:0.6931 3rd Qu.:0.6931 3rd Qu.:0.6931 3rd Qu.:0.6931
## Max. :1.7918 Max. :1.7918 Max. :1.7918 Max. :1.7918
## abuse_i
## Min. :0.6931
## 1st Qu.:0.6931
## Median :0.6931
## Mean :0.7212
## 3rd Qu.:0.6931
## Max. :1.7918
inv_df |>
tidyr::pivot_longer(cols=starts_with("abuse_"),
names_to="var",
values_to="val") |>
ggplot(aes(x=val)) +
geom_bar(fill = "skyblue", color = "black") +
facet_wrap(~var, scales="free_y") +
labs(
x="Likert Ordinal Scale Response (1-5)",
y="Frequency",
caption="Note. Ordinal scale responses range from 1 to 5:\n
(1) Never (2) Rarely (3) Sometimes (4) Always (5) Often"
) +
theme_minimal()

Note that residual invariance did not hold and was unable to be
computed.
inv_df$COUNTRY <- as.character(inv_df$COUNTRY)
# remove problematic countries
inv_df1 <- inv_df |>
dplyr::select(COUNTRY, contains('abuse_')) |>
dplyr::filter(!(COUNTRY %in% c('Vietnam')))
# specify model
# abuse.model <- "A =~ abuse_f + abuse_g + abuse_h + abuse_i
# B =~ abuse_a + abuse_b + abuse_c + abuse_d + abuse_e"
# i) Configural model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df1),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "COUNTRY",
estimator = "MLR") -> fit.tp_sex_configural.cfa
# ii) Metric model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df1),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "COUNTRY",
estimator = "MLR",
group.equal = "loadings") -> fit.tp_sex_weak.cfa
## iii) Scalar model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df1),
effect.coding = T,
meanstructure = T,
missing = "pairwise",
group = "COUNTRY",
estimator = "MLR",
group.equal = c("loadings", "intercepts")) -> fit.tp_sex_strong.cfa
# iv) Residual model
abuse.model %>% lavaan::cfa(
# data_cfa %>% remove_labels(),
labelled::remove_labels(inv_df1),
effect.coding = T,
missing = "pairwise",
meanstructure = T,
group = "COUNTRY",
estimator = "MLR",
group.equal = c("loadings", "intercepts")) -> fit.tp_sex_strict.cfa
# group.equal = c("loadings", "intercepts", "residuals")) -> fit.tp_sex_strict.cfa
Instead try bayesian methods?
fit_configural <- bsem(
model = abuse.model,
data = labelled::remove_labels(inv_df),
group = "COUNTRY",
meanstructure = TRUE,
missing = "pairwise"
)
Results
Intepretation of results:
Configural vs metric (weak) model: If the chi-square difference
test between the configural and metric model is non-significant (p >
0.05), it suggests that constraining the loadings to be equal across
groups does not significantly worsen the model fit. This supports metric
invariance.
Metric (Weak) vs. Scalar (Strong) model: If the chi-square
difference test between the metric and scalar model is non-significant
(p > 0.05), it indicates that constraining both the loadings and
intercepts to be equal across groups does not significantly worsen the
model fit. This supports scalar invariance.
Scalar (Strong) vs. Residual (Strict) Model: If the chi-square
difference test between the scalar and residual model is non-significant
(p > 0.05), it suggests that constraining the residual variances to
be equal across groups does not significantly worsen the model
fit
# Pairwise comparisons using anova function
anova_configural_metric <- anova(fit.tp_sex_configural.cfa, fit.tp_sex_weak.cfa)
anova_metric_scalar <- anova(fit.tp_sex_weak.cfa, fit.tp_sex_strong.cfa)
anova_scalar_residual <- anova(fit.tp_sex_strong.cfa, fit.tp_sex_strict.cfa)
# Print pairwise comparison results
print(anova_configural_metric)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff
## fit.tp_sex_configural.cfa 286 -123234 -121027 3496.3
## fit.tp_sex_weak.cfa 356 -122153 -120448 4717.3 145.44 70
## Pr(>Chisq)
## fit.tp_sex_configural.cfa
## fit.tp_sex_weak.cfa 3.228e-07 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
print(anova_metric_scalar)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_weak.cfa 356 -122153 -120448 4717.3
## fit.tp_sex_strong.cfa 426 -121790 -120587 5220.2 486.5 70 < 2.2e-16
##
## fit.tp_sex_weak.cfa
## fit.tp_sex_strong.cfa ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
print(anova_scalar_residual)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff Pr(>Chisq)
## fit.tp_sex_strong.cfa 426 -121790 -120587 5220.2
## fit.tp_sex_strict.cfa 426 -121790 -120587 5220.2 0 0
# Extract fit indices for each model
fit_indices_configural <- fitMeasures(fit.tp_sex_configural.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_metric <- fitMeasures(fit.tp_sex_weak.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_scalar <- fitMeasures(fit.tp_sex_strong.cfa, c("cfi", "tli", "rmsea", "srmr"))
fit_indices_residual <- fitMeasures(fit.tp_sex_strict.cfa, c("cfi", "tli", "rmsea", "srmr"))
# Calculate fit indices differences
cfi_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["cfi"] - fitmeasures(fit.tp_sex_strong.cfa)["cfi"]
tli_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["tli"] - fitmeasures(fit.tp_sex_strong.cfa)["tli"]
rmsea_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["rmsea"] - fitmeasures(fit.tp_sex_strong.cfa)["rmsea"]
srmr_diff_metric_scalar <- fitmeasures(fit.tp_sex_weak.cfa)["srmr"] - fitmeasures(fit.tp_sex_strong.cfa)["srmr"]
cfi_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["cfi"] - fitmeasures(fit.tp_sex_strict.cfa)["cfi"]
tli_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["tli"] - fitmeasures(fit.tp_sex_strict.cfa)["tli"]
rmsea_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["rmsea"] - fitmeasures(fit.tp_sex_strict.cfa)["rmsea"]
srmr_diff_scalar_residual <- fitmeasures(fit.tp_sex_strong.cfa)["srmr"] - fitmeasures(fit.tp_sex_strict.cfa)["srmr"]
# Create a data frame with the results
results_sex <- data.frame(
Comparison = c("Configural vs Metric", "Metric vs Scalar", "Scalar vs Residual"),
Chi_Square_Diff = c(anova_configural_metric$Chisq[2], anova_metric_scalar$Chisq[2], anova_scalar_residual$Chisq[2]),
DF_Diff = c(anova_configural_metric$Df[2], anova_metric_scalar$Df[2], anova_scalar_residual$Df[2]),
p_value = c(anova_configural_metric$`Pr(>Chisq)`[2], anova_metric_scalar$`Pr(>Chisq)`[2], anova_scalar_residual$`Pr(>Chisq)`[2]),
CFI = c(fit_indices_configural["cfi"], cfi_diff_metric_scalar, cfi_diff_scalar_residual),
TLI = c(fit_indices_configural["tli"], tli_diff_metric_scalar, tli_diff_scalar_residual),
RMSEA = c(fit_indices_configural["rmsea"], rmsea_diff_metric_scalar, rmsea_diff_scalar_residual),
SRMR = c(fit_indices_configural["srmr"], srmr_diff_metric_scalar, srmr_diff_scalar_residual)
)
# output model comparison indices
results_sex |>
kbl() |>
kable_styling(full_width = F)
|
Comparison
|
Chi_Square_Diff
|
DF_Diff
|
p_value
|
CFI
|
TLI
|
RMSEA
|
SRMR
|
|
Configural vs Metric
|
4717.337
|
356
|
3e-07
|
0.9281332
|
0.9004922
|
0.1136520
|
0.0499795
|
|
Metric vs Scalar
|
5220.178
|
426
|
0e+00
|
0.0096898
|
-0.0088386
|
0.0049339
|
-0.0035725
|
|
Scalar vs Residual
|
5220.178
|
426
|
NA
|
0.0000000
|
0.0000000
|
0.0000000
|
0.0000000
|
# Compare the models
anova_results_sex <- anova(fit.tp_sex_configural.cfa, fit.tp_sex_weak.cfa, fit.tp_sex_strong.cfa, fit.tp_sex_strict.cfa)
# Print the comparison results
print(anova_results_sex)
##
## Scaled Chi-Squared Difference Test (method = "satorra.bentler.2001")
##
## lavaan NOTE:
## The "Chisq" column contains standard test statistics, not the
## robust test that should be reported per model. A robust difference
## test is a function of two standard (not robust) statistics.
##
## Df AIC BIC Chisq Chisq diff Df diff
## fit.tp_sex_configural.cfa 286 -123234 -121027 3496.3
## fit.tp_sex_weak.cfa 356 -122153 -120448 4717.3 145.44 70
## fit.tp_sex_strong.cfa 426 -121790 -120587 5220.2 486.50 70
## fit.tp_sex_strict.cfa 426 -121790 -120587 5220.2 0.00 0
## Pr(>Chisq)
## fit.tp_sex_configural.cfa
## fit.tp_sex_weak.cfa 3.228e-07 ***
## fit.tp_sex_strong.cfa < 2.2e-16 ***
## fit.tp_sex_strict.cfa
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
anova_results_sex |>
kbl() |>
kable_styling(full_width = F)
|
|
Df
|
AIC
|
BIC
|
Chisq
|
Chisq diff
|
Df diff
|
Pr(>Chisq)
|
|
fit.tp_sex_configural.cfa
|
286
|
-123234.3
|
-121027.4
|
3496.259
|
NA
|
NA
|
NA
|
|
fit.tp_sex_weak.cfa
|
356
|
-122153.2
|
-120447.9
|
4717.337
|
145.4403
|
70
|
3e-07
|
|
fit.tp_sex_strong.cfa
|
426
|
-121790.4
|
-120586.6
|
5220.178
|
486.5017
|
70
|
0e+00
|
|
fit.tp_sex_strict.cfa
|
426
|
-121790.4
|
-120586.6
|
5220.178
|
0.0000
|
0
|
NA
|
# tabulate additional fit indices
fit_indices_sex <- data.frame(
Model = c("Configural", "Metric", "Scalar", "Residual"),
CFI = c(
fitMeasures(fit.tp_sex_configural.cfa, "cfi"),
fitMeasures(fit.tp_sex_weak.cfa, "cfi"),
fitMeasures(fit.tp_sex_strong.cfa, "cfi"),
fitMeasures(fit.tp_sex_strict.cfa, "cfi")
),
TLI = c(
fitMeasures(fit.tp_sex_configural.cfa, "tli"),
fitMeasures(fit.tp_sex_weak.cfa, "tli"),
fitMeasures(fit.tp_sex_strong.cfa, "tli"),
fitMeasures(fit.tp_sex_strict.cfa, "tli")
),
RMSEA = c(
fitMeasures(fit.tp_sex_configural.cfa, "rmsea"),
fitMeasures(fit.tp_sex_weak.cfa, "rmsea"),
fitMeasures(fit.tp_sex_strong.cfa, "rmsea"),
fitMeasures(fit.tp_sex_strict.cfa, "rmsea")
)
)
# output fit indices
fit_indices_sex |>
kbl() |>
kable_styling(full_width = F)
|
Model
|
CFI
|
TLI
|
RMSEA
|
|
Configural
|
0.9281332
|
0.9004922
|
0.113652
|
|
Metric
|
0.9023645
|
0.8913942
|
0.118734
|
|
Scalar
|
0.8926747
|
0.9002328
|
0.113800
|
|
Residual
|
0.8926747
|
0.9002328
|
0.113800
|
Latent means
plot
lavaan::parameterestimates(fit.tp_sex_strict.cfa) %>%
filter(grepl("abuse_.$", lhs) & op == "~1") %>%
ggplot(aes(x = lhs,
y = est,
group = as.factor(group),
color = as.factor(group),
ymin = ci.lower,
ymax = ci.upper)) +
geom_point(size = 3, alpha = 0.7) +
geom_errorbar(width = 0.3, position = position_dodge(width = 0.5), alpha = 0.7) +
geom_line(size = 1, position = position_dodge(width = 0.5), alpha = 0.7) +
# scale_color_brewer(palette = "Set1", labels = c("Female", "Male")) +
scale_color_lancet(labels=c("Female", "Male")) +
labs(x = "Subscale", y = "Latent mean", color = "Sex",
title = "Latent Means by Sex") +
# scale_x_discrete(labels = c("Authoritarianism",
# "Mediation",
# "Monitoring by Proxy",
# "Boundary Negotiation",
# "Technological Monitoring",
# "Co-Use")) +
theme_minimal() +
theme(
legend.position = "top",
legend.title = element_text(size = 12, face = "bold"),
legend.text = element_text(size = 10),
axis.text.x = element_text(angle = 45, hjust = 1, size = 10, color = "black"),
axis.text.y = element_text(size = 10, color = "black"),
axis.title = element_text(size = 12, color = "black"),
plot.title = element_text(size = 14, hjust = 0.5, face = "bold"),
plot.subtitle = element_text(size = 12, hjust = 0.5)
)