4
4
# ' @description Compute various measures of internal consistencies
5
5
# ' for tests or item-scales of questionnaires.
6
6
# '
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
9
10
# '
10
11
# ' @return The McDonald's Omega value for `x`.
11
12
# '
@@ -33,11 +34,24 @@ mcdonalds_omega <- function(x, ...) {
33
34
34
35
# ' @export
35
36
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 )
40
39
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
41
55
model <- paste0(" f1 =~ NA*" , varnames [1 ], " + " )
42
56
formula_loadings <- paste(paste0(name_loadings , " *" , varnames ), collapse = " + " )
43
57
formula_factors <- " f1 ~~ 1*f1\n "
@@ -60,34 +74,102 @@ mcdonalds_omega.data.frame <- function(x, ci = 0.95, verbose = TRUE, ...) {
60
74
61
75
insight :: check_if_installed(" lavaan" )
62
76
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" )
64
79
out <- lavaan :: parameterEstimates(fit )
65
80
81
+ # extract omega and related standard error
66
82
estimate <- as.vector(out $ est [out $ label == " relia" ])
67
83
se <- as.vector(out $ se [out $ label == " relia" ])
68
84
85
+ # if user requested CI, return data frame with omega and CI
69
86
if (! is.null(ci ) && ! is.na(ci )) {
70
87
crit <- stats :: qnorm((1 + ci ) / 2 )
71
88
72
89
logest <- log(estimate / (1 - estimate ))
73
90
logse <- se / (estimate * (1 - estimate ))
74
91
loglower <- logest - crit * logse
75
92
logupper <- logest + crit * logse
93
+
76
94
if (logupper < loglower ) {
77
95
temp <- loglower
78
96
loglower <- logupper
79
97
loguppper <- temp
80
98
}
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" )
83
107
} else {
84
- ci_low <- NA
85
- ci_high <- NA
108
+ omega <- estimate
86
109
}
110
+
111
+ omega
87
112
}
88
113
89
114
90
115
# ' @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 )
93
175
}
0 commit comments