Skip to content

Commit 3506b11

Browse files
Add support for EFA/PCA from {psych} package (#822)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 09a91fd commit 3506b11

File tree

11 files changed

+317
-12
lines changed

11 files changed

+317
-12
lines changed

DESCRIPTION

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: performance
33
Title: Assessment of Regression Models Performance
4-
Version: 0.14.0.2
4+
Version: 0.14.0.3
55
Authors@R:
66
c(person(given = "Daniel",
77
family = "Lüdecke",
@@ -71,7 +71,7 @@ License: GPL-3
7171
URL: https://easystats.github.io/performance/
7272
BugReports: https://github.com/easystats/performance/issues
7373
Depends:
74-
R (>= 3.6)
74+
R (>= 4.0)
7575
Imports:
7676
bayestestR (>= 0.16.0),
7777
insight (>= 1.3.0),
@@ -98,6 +98,7 @@ Suggests:
9898
dagitty,
9999
dbscan,
100100
DHARMa (>= 0.4.7),
101+
discovr,
101102
estimatr,
102103
fixest,
103104
flextable,
@@ -137,6 +138,7 @@ Suggests:
137138
patchwork,
138139
pscl,
139140
psych,
141+
psychTools,
140142
quantreg,
141143
qqplotr (>= 0.0.6),
142144
randomForest,
@@ -165,3 +167,4 @@ Config/Needs/website:
165167
r-lib/pkgdown,
166168
easystats/easystatstemplate
167169
Config/rcmdcheck/ignore-inconsequential-notes: true
170+
Remotes: easystats/insight, easystats/parameters

NAMESPACE

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,16 @@ S3method(check_multimodal,numeric)
6565
S3method(check_normality,BFBayesFactor)
6666
S3method(check_normality,afex_aov)
6767
S3method(check_normality,default)
68+
S3method(check_normality,fa)
6869
S3method(check_normality,glm)
6970
S3method(check_normality,glmmTMB)
7071
S3method(check_normality,htest)
7172
S3method(check_normality,lmerModLmerTest)
7273
S3method(check_normality,merMod)
7374
S3method(check_normality,numeric)
75+
S3method(check_normality,parameters_efa)
7476
S3method(check_normality,performance_simres)
77+
S3method(check_normality,principal)
7578
S3method(check_outliers,BFBayesFactor)
7679
S3method(check_outliers,DHARMa)
7780
S3method(check_outliers,character)
@@ -118,7 +121,10 @@ S3method(check_predictions,lme)
118121
S3method(check_predictions,stanreg)
119122
S3method(check_residuals,DHARMa)
120123
S3method(check_residuals,default)
124+
S3method(check_residuals,fa)
125+
S3method(check_residuals,parameters_efa)
121126
S3method(check_residuals,performance_simres)
127+
S3method(check_residuals,principal)
122128
S3method(check_singularity,MixMod)
123129
S3method(check_singularity,clmm)
124130
S3method(check_singularity,cpglmm)
@@ -173,6 +179,7 @@ S3method(model_performance,clm2)
173179
S3method(model_performance,coxph)
174180
S3method(model_performance,coxph_weightit)
175181
S3method(model_performance,default)
182+
S3method(model_performance,fa)
176183
S3method(model_performance,felm)
177184
S3method(model_performance,fixest)
178185
S3method(model_performance,fixest_multi)
@@ -205,10 +212,12 @@ S3method(model_performance,negbinirr)
205212
S3method(model_performance,negbinmfx)
206213
S3method(model_performance,nestedLogit)
207214
S3method(model_performance,ordinal_weightit)
215+
S3method(model_performance,parameters_efa)
208216
S3method(model_performance,plm)
209217
S3method(model_performance,poissonirr)
210218
S3method(model_performance,poissonmfx)
211219
S3method(model_performance,polr)
220+
S3method(model_performance,principal)
212221
S3method(model_performance,probitmfx)
213222
S3method(model_performance,rlmerMod)
214223
S3method(model_performance,rma)

NEWS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* Formatting of p-values in `test_likelihoodratio()` is now consistent with
66
formatted p-values from other functions.
77

8+
* Added following methods for `psych::fa()`, `psych::principal()` and
9+
`parameters::factor_analysis()`: `check_normality()`, `check_residuals()`, and `model_performance()`.
10+
811
# performance 0.14.0
912

1013
## Breaking Changes

R/check_normality.R

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,28 @@ check_normality.default <- function(x, ...) {
7575
p.val
7676
}
7777

78+
# PCA / FA ---------------
79+
80+
#' @export
81+
check_normality.parameters_efa <- function(x, ...) {
82+
# check for normality of residuals
83+
p.val <- .check_normality(insight::get_residuals(x), x)
84+
85+
attr(p.val, "data") <- x
86+
attr(p.val, "object_name") <- insight::safe_deparse_symbol(substitute(x))
87+
attr(p.val, "effects") <- "fixed"
88+
attr(p.val, "is_fa") <- TRUE
89+
class(p.val) <- unique(c("check_normality", "see_check_normality", class(p.val)))
90+
91+
p.val
92+
}
93+
94+
#' @export
95+
check_normality.fa <- check_normality.parameters_efa
96+
97+
#' @export
98+
check_normality.principal <- check_normality.parameters_efa
99+
78100
# glm ---------------
79101

80102
#' @export
@@ -180,6 +202,21 @@ print.check_normality <- function(x, ...) {
180202
}
181203
}
182204
}
205+
206+
# add FA / PCA information
207+
if (isTRUE(attributes(x)$is_fa)) {
208+
res <- insight::get_residuals(attributes(x)$data)
209+
lge_resid_tot <- sum(abs(res) > 0.05)
210+
lge_resid_pct <- lge_resid_tot / length(res)
211+
cat(paste0(
212+
"\nAbsolute residuals > 0.05 = ",
213+
lge_resid_tot,
214+
" (",
215+
insight::format_percent(lge_resid_pct),
216+
")\n"
217+
))
218+
}
219+
183220
invisible(x)
184221
}
185222

R/check_residuals.R

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,19 @@ check_residuals.default <- function(x, alternative = "two.sided",
6666
}
6767
}
6868

69+
70+
#' @export
71+
check_residuals.fa <- function(x, ...) {
72+
check_normality(x, ...)
73+
}
74+
75+
#' @export
76+
check_residuals.principal <- check_residuals.fa
77+
78+
#' @export
79+
check_residuals.parameters_efa <- check_residuals.fa
80+
81+
6982
#' @export
7083
check_residuals.performance_simres <- function(x, alternative = "two.sided",
7184
distribution = "punif", ...) {

R/model_performance.psych.R

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#' Performance of FA / PCA models
2+
#'
3+
#' Compute indices of model performance for models from the **psych** package,
4+
#' and for `parameters::factor_analysis()`.
5+
#'
6+
#' @param model A model object of class `fa` (e.g., from `psych::fa()`),
7+
#' `principal` (e.g., from `psych::principal()`), or from
8+
#' `parameters::factor_analysis()`.
9+
#' @param metrics Can be `"all"` or a character vector of metrics to be computed
10+
#' (some of `"Chi2"`, `"Chi2_df"`, `"p_Chi2"`, `"RMSA"`, `"RMSA_corrected"`,
11+
#' `"TLI"`, `"RMSEA"`, `"RMSEA_CI_low"`, `"RMSEA_CI_high"`, and `"BIC"`.
12+
#' @param verbose Toggle off warnings.
13+
#' @param ... Arguments passed to or from other methods.
14+
#'
15+
#' @return A data frame (with one row) and one column per "index" (see
16+
#' `metrics`).
17+
#'
18+
#' @examplesIf all(insight::check_if_installed(c("psych", "GPArotation", "psychTools"), quietly = TRUE))
19+
#' out <- psych::fa(psychTools::bfi[, 1:25], 5)
20+
#' model_performance(out)
21+
#' @export
22+
model_performance.fa <- function(model, metrics = "all", verbose = TRUE, ...) {
23+
out <- data.frame(
24+
Chi2 = ifelse(is.null(model$STATISTIC), NA_real_, model$STATISTIC),
25+
Chi2_df = ifelse(is.null(model$dof), NA_real_, model$dof),
26+
p_Chi2 = ifelse(is.null(model$PVAL), NA_real_, model$PVAL),
27+
RMSA = ifelse(is.null(model$rms), NA_real_, model$rms),
28+
RMSA_corrected = ifelse(is.null(model$crms), NA_real_, model$crms),
29+
TLI = ifelse(is.null(model$TLI), NA_real_, model$TLI),
30+
RMSEA = ifelse(is.null(model$RMSEA), NA_real_, model$RMSEA[1]),
31+
RMSEA_CI = ifelse(is.null(model$RMSEA), NA_real_, 0.9),
32+
RMSEA_CI_low = ifelse(is.null(model$RMSEA), NA_real_, model$RMSEA[2]),
33+
RMSEA_CI_high = ifelse(is.null(model$RMSEA), NA_real_, model$RMSEA[3]),
34+
BIC = ifelse(is.null(model$BIC), NA_real_, model$BIC)
35+
)
36+
37+
if (all(metrics == "all")) {
38+
metrics <- names(out)
39+
}
40+
41+
# clean up
42+
out <- out[, metrics]
43+
out <- datawizard::remove_empty_columns(out)
44+
45+
class(out) <- c("performance_fa", "performance_model", class(out))
46+
out
47+
}
48+
49+
#' @export
50+
model_performance.principal <- model_performance.fa
51+
52+
#' @export
53+
model_performance.parameters_efa <- function(model, metrics = "all", verbose = TRUE, ...) {
54+
model_performance(attributes(model)$model, metrics = metrics, verbose = verbose, ...)
55+
}

R/test_likelihoodratio.R

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ print.test_likelihoodratio <- function(x, digits = 2, ...) {
7373

7474

7575
#' @export
76-
format.test_likelihoodratio <- function(x, digits = 2, p_digits = 3, ...) {
76+
format.test_likelihoodratio <- function(x, digits = 2, p_digits = 3, format = "text", ...) {
7777
# Footer
7878
if ("LogLik" %in% names(x)) {
7979
best <- which.max(x$LogLik)
@@ -91,21 +91,27 @@ format.test_likelihoodratio <- function(x, digits = 2, p_digits = 3, ...) {
9191
estimator_string <- sprintf(" (%s-estimator)", toupper(attributes(x)$estimator))
9292
}
9393

94+
if (format == "text") {
95+
caption <- c(paste0("# Likelihood-Ratio-Test (LRT) for Model Comparison", estimator_string), "blue")
96+
} else {
97+
caption <- paste0("Likelihood-Ratio-Test (LRT) for Model Comparison", estimator_string)
98+
}
99+
94100
attr(x, "table_footer") <- footer
95-
attr(x, "table_caption") <- c(paste0("# Likelihood-Ratio-Test (LRT) for Model Comparison", estimator_string), "blue")
101+
attr(x, "table_caption") <- caption
96102
x
97103
}
98104

99105

100106
#' @export
101107
print_md.test_likelihoodratio <- function(x, digits = 2, ...) {
102-
insight::export_table(format(x, digits = digits, ...), format = "markdown", ...)
108+
insight::export_table(format(x, digits = digits, format = "markdown", ...), format = "markdown", ...)
103109
}
104110

105111

106112
#' @export
107113
print_html.test_likelihoodratio <- function(x, digits = 2, ...) {
108-
insight::export_table(format(x, digits = digits, ...), format = "html", ...)
114+
insight::export_table(format(x, digits = digits, format = "html", ...), format = "html", ...)
109115
}
110116

111117

man/model_performance.fa.Rd

Lines changed: 35 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)