Skip to content

Commit ab95e73

Browse files
committed
draft
1 parent 527efce commit ab95e73

File tree

5 files changed

+106
-18
lines changed

5 files changed

+106
-18
lines changed

NAMESPACE

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ S3method(logLik,plm)
138138
S3method(logLik,svycoxph)
139139
S3method(mcdonalds_omega,data.frame)
140140
S3method(mcdonalds_omega,matrix)
141+
S3method(mcdonalds_omega,parameters_pca)
141142
S3method(model_performance,Arima)
142143
S3method(model_performance,BFBayesFactor)
143144
S3method(model_performance,DirichletRegModel)
@@ -293,6 +294,7 @@ S3method(print,icc_decomposed)
293294
S3method(print,item_difficulty)
294295
S3method(print,item_discrimination)
295296
S3method(print,looic)
297+
S3method(print,mcdonalds_omega)
296298
S3method(print,performance_accuracy)
297299
S3method(print,performance_cv)
298300
S3method(print,performance_hosmer)

R/cronbachs_alpha.R

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
#' @description Compute various measures of internal consistencies
55
#' for tests or item-scales of questionnaires.
66
#'
7-
#' @param x A matrix or a data frame.
7+
#' @param x A matrix or a data frame, or an object returned by
8+
#' `[parameters::principal_components()]`.
89
#' @param ... Currently not used.
910
#'
1011
#' @return The Cronbach's Alpha value for `x`.
@@ -50,14 +51,12 @@ cronbachs_alpha.data.frame <- function(x, verbose = TRUE, ...) {
5051
}
5152

5253

53-
5454
#' @export
5555
cronbachs_alpha.matrix <- function(x, verbose = TRUE, ...) {
5656
cronbachs_alpha(as.data.frame(x), verbose = verbose, ...)
5757
}
5858

5959

60-
6160
#' @export
6261
cronbachs_alpha.parameters_pca <- function(x, verbose = TRUE, ...) {
6362
# fetch data used for the PCA

R/mcdonalds_omega.r

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@
44
#' @description Compute various measures of internal consistencies
55
#' for tests or item-scales of questionnaires.
66
#'
7-
#' @param x A matrix or a data frame.
8-
#' @param ... Currently not used.
7+
#' @param ci Confidence interval for the reliability estimate. If `NULL`,
8+
#' no confidence interval is computed.
9+
#' @inheritParams cronbachs_alpha
910
#'
1011
#' @return The McDonald's Omega value for `x`.
1112
#'
@@ -33,11 +34,24 @@ mcdonalds_omega <- function(x, ...) {
3334

3435
#' @export
3536
mcdonalds_omega.data.frame <- function(x, ci = 0.95, verbose = TRUE, ...) {
36-
varnames <- colnames(x)
37-
n_params <- length(varnames)
38-
name_loadings <- paste0("a", 1:n_params)
39-
name_error <- paste0("b", 1:n_params)
37+
# remove missings
38+
.data <- stats::na.omit(x)
4039

40+
# we need at least two columns for Cronach's Alpha
41+
if (is.null(ncol(.data)) || ncol(.data) < 2) {
42+
if (verbose) {
43+
insight::format_warning("Too few columns in `x` to compute McDonald's Omega.")
44+
}
45+
return(NULL)
46+
}
47+
48+
# prepare names and formulas for lavaan
49+
varnames <- colnames(.data)
50+
name_loadings <- paste0("a", seq_len(ncol(.data)))
51+
name_error <- paste0("b", seq_len(ncol(.data)))
52+
53+
# we need this specific formulation for lavaan to get the omega reliability estimate
54+
# see code in MBESS
4155
model <- paste0("f1 =~ NA*", varnames[1], " + ")
4256
formula_loadings <- paste(paste0(name_loadings, "*", varnames), collapse = " + ")
4357
formula_factors <- "f1 ~~ 1*f1\n"
@@ -60,34 +74,102 @@ mcdonalds_omega.data.frame <- function(x, ci = 0.95, verbose = TRUE, ...) {
6074

6175
insight::check_if_installed("lavaan")
6276

63-
fit <- lavaan::cfa(model, data = x, missing = "ml", estimator = "mlr", se = "default")
77+
# fit CFA to get reliability estimate
78+
fit <- lavaan::cfa(model, data = .data, missing = "ml", estimator = "mlr", se = "default")
6479
out <- lavaan::parameterEstimates(fit)
6580

81+
# extract omega and related standard error
6682
estimate <- as.vector(out$est[out$label == "relia"])
6783
se <- as.vector(out$se[out$label == "relia"])
6884

85+
# if user requested CI, return data frame with omega and CI
6986
if (!is.null(ci) && !is.na(ci)) {
7087
crit <- stats::qnorm((1 + ci) / 2)
7188

7289
logest <- log(estimate / (1 - estimate))
7390
logse <- se / (estimate * (1 - estimate))
7491
loglower <- logest - crit * logse
7592
logupper <- logest + crit * logse
93+
7694
if (logupper < loglower) {
7795
temp <- loglower
7896
loglower <- logupper
7997
loguppper <- temp
8098
}
81-
ci_low <- 1 / (1 + exp(-loglower))
82-
ci_high <- 1 / (1 + exp(-logupper))
99+
100+
omega <- data.frame(
101+
Omega = estimate,
102+
CI_low = 1 / (1 + exp(-loglower)),
103+
CI_high = 1 / (1 + exp(-logupper)),
104+
stringsAsFactors = FALSE
105+
)
106+
class(omega) <- c("mcdonalds_omega", "data.frame")
83107
} else {
84-
ci_low <- NA
85-
ci_high <- NA
108+
omega <- estimate
86109
}
110+
111+
omega
87112
}
88113

89114

90115
#' @export
91-
mcdonalds_omega.matrix <- function(x, verbose = TRUE, ...) {
92-
mcdonalds_omega(as.data.frame(x), verbose = verbose, ...)
116+
mcdonalds_omega.matrix <- function(x, ci = 0.95, verbose = TRUE, ...) {
117+
mcdonalds_omega(as.data.frame(x), ci = ci, verbose = verbose, ...)
118+
}
119+
120+
121+
#' @export
122+
mcdonalds_omega.parameters_pca <- function(x, verbose = TRUE, ...) {
123+
# fetch data used for the PCA
124+
pca_data <- attributes(x)$dataset
125+
126+
# if NULL, can we get from environment?
127+
if (is.null(pca_data)) {
128+
pca_data <- attr(x, "data")
129+
if (is.null(pca_data)) {
130+
if (verbose) {
131+
insight::format_warning("Could not find data frame that was used for the PCA.")
132+
}
133+
return(NULL)
134+
}
135+
pca_data <- get(pca_data, envir = parent.frame())
136+
}
137+
138+
# get assignment of columns to extracted components, based on the max loading
139+
factor_assignment <- attributes(x)$closest_component
140+
141+
# sort and get unique IDs so we only get data from relevant columns
142+
unique_factors <- sort(unique(factor_assignment))
143+
144+
# apply cronbach's alpha for each component,
145+
# only for variables with max loading
146+
omegas <- sapply(unique_factors, function(i) {
147+
mcdonalds_omega(
148+
pca_data[, as.vector(x$Variable[factor_assignment == i]), drop = FALSE],
149+
ci = NULL,
150+
verbose = verbose,
151+
...
152+
)
153+
})
154+
155+
names(omegas) <- paste0("PC", unique_factors)
156+
unlist(omegas)
157+
}
158+
159+
160+
# methods ---------------------------------------------------------------------
161+
162+
#' @export
163+
print.mcdonalds_omega <- function(x, digits = 3, ...) {
164+
# print regular R2
165+
out <- sprintf(
166+
"Omega: %.*f %s",
167+
digits,
168+
x$omega,
169+
insight::format_ci(ci_low, ci_high, digits = digits, ci = NULL)
170+
)
171+
172+
cat(out)
173+
cat("\n")
174+
invisible(x)
93175
}

man/cronbachs_alpha.Rd

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/mcdonalds_omega.Rd

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)