-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0c427b4
commit 716169a
Showing
10 changed files
with
433 additions
and
29 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,177 @@ | ||
#' Find Optimal Factor Number. | ||
#' | ||
#' Find optimal factor number using various solutions. | ||
#' | ||
#' @param df The dataframe | ||
#' @param rotate What rotation to use c("none", "varimax", "oblimin","promax") | ||
#' @param fm Factoring method – fm="pa" Principal Axis Factor Analysis, | ||
#' fm = "minres" minimum residual (OLS) factoring fm="mle" Maximum Likelihood FA, | ||
#' fm="pc" Principal Components" | ||
#' @param n_max How many factors to test. | ||
#' | ||
#' @return output | ||
#' | ||
#' @examples | ||
#' df <- dplyr::select_if(attitude, is.numeric) | ||
#' results <- psycho::n_factors(df) | ||
#' | ||
#' summary(results) | ||
#' plot(results) | ||
#' | ||
#' # See details on methods | ||
#' psycho::values(results)$methods | ||
#' | ||
#' @author \href{https://dominiquemakowski.github.io/}{Dominique Makowski} | ||
#' | ||
#' @importFrom qgraph cor_auto | ||
#' @importFrom psych VSS | ||
#' @importFrom MASS mvrnorm | ||
#' @importFrom MASS ginv | ||
#' @importFrom nFactors moreStats | ||
#' @importFrom nFactors nScree | ||
#' @importFrom stats cov | ||
#' @importFrom stats dnorm | ||
#' @importFrom stats qnorm | ||
#' @export | ||
n_factors <- function(df, rotate="varimax", fm="minres", n_max=8){ | ||
|
||
# Copy the parallel function from nFactors to correct the use of mvrnorm | ||
parallel <- function (subject = 100, var = 10, rep = 100, cent = 0.05, quantile = cent, | ||
model = "components", sd = diag(1, var), ...) | ||
{ | ||
r <- subject | ||
c <- var | ||
y <- matrix(c(1:r * c), nrow = r, ncol = c) | ||
ycor <- matrix(c(1:c * c), nrow = c, ncol = c) | ||
evpea <- NULL | ||
leg.txt <- "Pearson" | ||
for (k in c(1:rep)) { | ||
y <- MASS::mvrnorm(n = r, mu = rep(0, var), Sigma = sd, empirical = FALSE) | ||
corY <- cov(y, ...) | ||
if (model == "components") | ||
diag(corY) <- diag(sd) | ||
if (model == "factors") | ||
corY <- corY - MASS::ginv(diag(diag(MASS::ginv(corY)))) | ||
evpea <- rbind(evpea, eigen(corY)[[1]]) | ||
} | ||
SEcentile <- function(sd, n = 100, p = 0.95) { | ||
return(sd/sqrt(n) * sqrt(p * (1 - p))/dnorm(qnorm(p))) | ||
} | ||
sprob <- c(cent) | ||
mevpea <- sapply(as.data.frame(evpea), mean) | ||
sevpea <- sapply(as.data.frame(evpea), sd) | ||
qevpea <- nFactors::moreStats(evpea, quantile = quantile)[3, ] | ||
sqevpea <- sevpea | ||
sqevpea <- sapply(as.data.frame(sqevpea), SEcentile, n = rep, | ||
p = cent) | ||
result <- list(eigen = data.frame(mevpea, sevpea, qevpea, | ||
sqevpea), subject = r, variables = c, centile = cent) | ||
class(result) <- "parallel" | ||
return(result) | ||
} | ||
|
||
|
||
cor <- qgraph::cor_auto(df) | ||
|
||
ap <- parallel(subject=nrow(df), var=ncol(df)) | ||
nS <- nFactors::nScree(x=eigen(cor)$values, aparallel=ap$eigen$qevpea) | ||
|
||
# Eigeinvalues data | ||
eigenvalues <- nS$Analysis %>% | ||
dplyr::select_("Eigenvalues", | ||
"Exp.Variance"="Prop", | ||
"Cum.Variance"="Cumu") %>% | ||
mutate_("n.Factors"= ~1:nrow(nS$Analysis)) | ||
|
||
|
||
# Processing | ||
# ------------------- | ||
results <- data.frame(Method=c("Optimal Coordinates", "Acceleration Factor", "Parallel Analysis", "Eigenvalues (Kaiser Criterion)"), n_optimal=as.numeric(nS$Components[1,])) | ||
|
||
|
||
vss <- psych::VSS(cor, n=n_max, n.obs=nrow(df), rotate=rotate, fm=fm, plot=F) # fm can be "pa", "pc", "minres", "mle" | ||
stats <- vss$vss.stats | ||
stats$map <- vss$map | ||
stats$n_factors <- 1:nrow(stats) | ||
|
||
results2 <- data.frame(Method=c("Velicer MAP", | ||
"BIC", | ||
"Sample Size Adjusted BIC"), | ||
n_optimal=c(na.omit(stats[stats$map==min(stats$map, na.rm = T),])$n_factors, | ||
na.omit(stats[stats$BIC==min(stats$BIC, na.rm = T),])$n_factors, | ||
na.omit(stats[stats$SABIC==min(stats$SABIC, na.rm = T),])$n_factors)) | ||
results <- rbind(results, results2) | ||
|
||
|
||
cfits <- vss[grep("cfit", names(vss))] | ||
for (name in names(cfits)){ | ||
cfit <- cfits[[name]] | ||
|
||
cfit <- data.frame(cfit=cfit, n_factors=1:length(cfit)) | ||
|
||
result3 <- data.frame(Method=c(gsub("cfit.", "VSS Complexity ", name)), | ||
n_optimal=c(na.omit(cfit[cfit$cfit==max(cfit$cfit, na.rm = T),])$n_factors)) | ||
|
||
results <- rbind(results, result3) | ||
} | ||
|
||
|
||
eigenvalues <- results %>% | ||
group_by_("n_optimal") %>% | ||
summarise_("n_method"=~n()) %>% | ||
mutate_("n_optimal"=~factor(n_optimal, levels=1:nrow(eigenvalues))) %>% | ||
complete_("n_optimal", fill=list(n_method = 0)) %>% | ||
arrange_("n_optimal") %>% | ||
rename_("n.Factors"="n_optimal", | ||
"n.Methods"="n_method") %>% | ||
mutate_("n.Factors"=~as.integer(n.Factors)) %>% | ||
left_join(eigenvalues, by="n.Factors") | ||
|
||
|
||
# Values | ||
# ------------- | ||
values <- list(eigenvalues=eigenvalues, methods=results) | ||
|
||
# Summary | ||
# ------------- | ||
summary <- eigenvalues | ||
|
||
# Text | ||
# ------------- | ||
text <- "Not implemented yet :(" | ||
|
||
|
||
# Plot | ||
# ------------- | ||
plot_data <- eigenvalues | ||
plot_data$n.Methods.Ratio <- plot_data$n.Methods/sum(plot_data$n.Methods) | ||
plot_data$n.Methods.Ratio <- plot_data$n.Methods.Ratio*(1/max(plot_data$n.Methods.Ratio)) | ||
plot_data$area <- plot_data$n.Methods.Ratio/(max(plot_data$n.Methods.Ratio) / max(plot_data$Eigenvalues)) | ||
plot_data$var <- plot_data$Cum.Variance/(max(plot_data$Cum.Variance) / max(plot_data$Eigenvalues)) | ||
|
||
plot <- plot_data %>% | ||
ggplot(aes_string(x="n.Factors", y="Eigenvalues")) + | ||
geom_area(aes_string(y="area"), | ||
fill="#FFC107", | ||
alpha=0.5) + | ||
geom_line(colour="#E91E63", | ||
size=1) + | ||
geom_hline(yintercept = 1, linetype="dashed", colour="#607D8B") + | ||
geom_line(aes_string(y = "var"), | ||
colour="#2196F3", | ||
size=1) + | ||
scale_y_continuous(sec.axis = sec_axis(trans= ~.*(max(eigenvalues$Cum.Variance) / max(eigenvalues$Eigenvalues)), | ||
name = 'Cumulative Variance\n')) + | ||
ylab("Eigenvalues\n") + | ||
xlab("\nNumber of Factors") + | ||
theme_minimal() | ||
plot | ||
|
||
# Output | ||
# ------------- | ||
output <- list(text = text, plot = plot, summary = summary, values = values) | ||
|
||
class(output) <- c("psychobject", "list") | ||
return(output) | ||
|
||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
context("n_factors") | ||
|
||
test_that("Correct Value", { | ||
results <- attitude %>% | ||
select_if(is.numeric) %>% | ||
psycho::n_factors() | ||
|
||
testthat::expect_equal(nrow(summary(results)), 7) | ||
testthat::expect_equal(nrow(psycho::values(results)$methods), 9) | ||
testthat::expect_equal(length(plot(results)), 9) | ||
|
||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
Binary file not shown.
716169a
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
R/n_factors.R:39:1: style: lines should not be more than 80 characters.
R/n_factors.R:45:5: warning: local variable ‘ycor’ assigned but may not be used
R/n_factors.R:47:5: warning: local variable ‘leg.txt’ assigned but may not be used
R/n_factors.R:58:16: style: Put spaces around all infix operators.
R/n_factors.R:58:44: style: Put spaces around all infix operators.
R/n_factors.R:60:5: warning: local variable ‘sprob’ assigned but may not be used
R/n_factors.R:68:1: style: lines should not be more than 80 characters.
R/n_factors.R:76:25: style: Put spaces around all infix operators.
R/n_factors.R:76:39: style: Put spaces around all infix operators.
R/n_factors.R:77:27: style: Put spaces around all infix operators.
R/n_factors.R:77:56: style: Put spaces around all infix operators.
R/n_factors.R:82:33: style: Put spaces around all infix operators.
R/n_factors.R:83:33: style: Put spaces around all infix operators.
R/n_factors.R:84:24: style: Put spaces around all infix operators.
R/n_factors.R:89:1: style: lines should not be more than 80 characters.
R/n_factors.R:89:31: style: Put spaces around all infix operators.
R/n_factors.R:89:145: style: Put spaces around all infix operators.
R/n_factors.R:92:1: style: lines should not be more than 80 characters.
R/n_factors.R:92:27: style: Put spaces around all infix operators.
R/n_factors.R:92:40: style: Put spaces around all infix operators.
R/n_factors.R:92:57: style: Put spaces around all infix operators.
R/n_factors.R:92:68: style: Put spaces around all infix operators.
R/n_factors.R:92:77: style: Put spaces around all infix operators.
R/n_factors.R:97:32: style: Put spaces around all infix operators.
R/n_factors.R:100:1: style: lines should not be more than 80 characters.
R/n_factors.R:100:35: style: Put spaces around all infix operators.
R/n_factors.R:100:61: style: Put spaces around all infix operators.
R/n_factors.R:101:1: style: lines should not be more than 80 characters.
R/n_factors.R:101:61: style: Put spaces around all infix operators.
R/n_factors.R:102:1: style: lines should not be more than 80 characters.
R/n_factors.R:102:63: style: Put spaces around all infix operators.
R/n_factors.R:110:28: style: Put spaces around all infix operators.
R/n_factors.R:110:44: style: Put spaces around all infix operators.
R/n_factors.R:112:33: style: Put spaces around all infix operators.
R/n_factors.R:113:1: style: lines should not be more than 80 characters.
R/n_factors.R:113:36: style: Put spaces around all infix operators.
R/n_factors.R:113:61: style: Put spaces around all infix operators.
R/n_factors.R:121:26: style: Put spaces around all infix operators.
R/n_factors.R:122:24: style: Put spaces around all infix operators.
R/n_factors.R:122:50: style: Put spaces around all infix operators.
R/n_factors.R:123:32: style: Put spaces around all infix operators.
R/n_factors.R:125:24: style: Put spaces around all infix operators.
R/n_factors.R:126:23: style: Put spaces around all infix operators.
R/n_factors.R:127:24: style: Put spaces around all infix operators.
R/n_factors.R:128:30: style: Put spaces around all infix operators.
R/n_factors.R:133:29: style: Put spaces around all infix operators.
R/n_factors.R:133:50: style: Put spaces around all infix operators.
R/n_factors.R:147:51: style: Put spaces around all infix operators.
R/n_factors.R:148:1: style: lines should not be more than 80 characters.
R/n_factors.R:148:57: style: Put spaces around all infix operators.
R/n_factors.R:148:60: style: Put spaces around all infix operators.
R/n_factors.R:149:1: style: lines should not be more than 80 characters.
R/n_factors.R:149:46: style: Put spaces around all infix operators.
R/n_factors.R:150:1: style: lines should not be more than 80 characters.
R/n_factors.R:150:42: style: Put spaces around all infix operators.
R/n_factors.R:153:24: style: Put spaces around all infix operators.
R/n_factors.R:153:39: style: Put spaces around all infix operators.
R/n_factors.R:154:27: style: Put spaces around all infix operators.
R/n_factors.R:155:19: style: Put spaces around all infix operators.
R/n_factors.R:156:20: style: Put spaces around all infix operators.
R/n_factors.R:157:21: style: Put spaces around all infix operators.
R/n_factors.R:158:19: style: Put spaces around all infix operators.
R/n_factors.R:159:40: style: Put spaces around all infix operators.
R/n_factors.R:159:57: style: Put spaces around all infix operators.
R/n_factors.R:161:21: style: Put spaces around all infix operators.
R/n_factors.R:162:19: style: Put spaces around all infix operators.
R/n_factors.R:163:1: style: lines should not be more than 80 characters.
R/n_factors.R:163:49: style: Put spaces around all infix operators.
R/n_factors.R:163:53: style: Put spaces around all infix operators.
R/n_factors.R:164:51: style: Only use double-quotes.