Nothing
### R code from vignette source 'GPA2local.Stex'
###################################################
### code chunk number 1: GPA2local.Stex:19-21
###################################################
options(continue=" ")
pdf.options(pointsize = 8)
###################################################
### code chunk number 2: GPA2local.Stex:57-163
###################################################
library("GPArotation")
GPFallMinima <- function(A, method = "quartimin", orthogonal = FALSE,
randomStarts = 100, eps = 1e-5, maxit = 1000,
normalize = FALSE, methodArgs = NULL,
minimumInclusion = 2) {
# Runs multiple random starts and returns ALL distinct local minima,
# not just the global minimum. Non-converged starts are discarded.
# Minima found fewer than minimumInclusion times are excluded.
# Minima are sorted by frequency (most common first).
engine <- if (orthogonal) GPForth else GPFoblq
Qvalues <- numeric(randomStarts)
Qconverged <- logical(randomStarts)
all_res <- vector("list", randomStarts)
for (i in 1:randomStarts) {
res <- engine(A, Tmat = Random.Start(ncol(A)),
normalize = normalize,
eps = eps,
maxit = maxit,
method = method,
methodArgs = methodArgs)
Qvalues[i] <- res$Table[nrow(res$Table), 2]
Qconverged[i] <- res$convergence
all_res[[i]] <- res
}
# Discard non-converged starts
converged_idx <- which(Qconverged)
nConverged <- length(converged_idx)
if (nConverged == 0)
stop("No starts converged. Consider increasing maxit or relaxing eps.")
Qvalues_conv <- Qvalues[converged_idx]
all_res_conv <- all_res[converged_idx]
# Bin converged criterion values into equivalence classes
Q_round <- round(Qvalues_conv / eps) * eps
Q_unique <- unique(Q_round)
# Build one representative solution per unique minimum
minima <- vector("list", length(Q_unique))
for (j in seq_along(Q_unique)) {
idx <- which(Q_round == Q_unique[j])
count <- length(idx)
proportion <- count / nConverged
minima[[j]] <- list(
result = all_res_conv[[idx[1]]],
f = Q_unique[j],
count = count,
proportion = proportion
)
}
# Sort by count (most common first)
ord <- order(sapply(minima, `[[`, "count"), decreasing = TRUE)
minima <- minima[ord]
# Filter out minima found fewer than minimumInclusion times
keep <- sapply(minima, `[[`, "count") >= minimumInclusion
minima <- minima[keep]
if (length(minima) == 0)
stop("No minima found with count >= minimumInclusion (", minimumInclusion,
"). Consider reducing minimumInclusion or increasing randomStarts.")
# Summary data frame
f_values <- sapply(minima, `[[`, "f")
f_global <- min(f_values)
summary_df <- data.frame(
minimum = seq_along(minima),
f = round(f_values, 6),
deltaF = round(f_values - f_global, 6),
count = sapply(minima, `[[`, "count"),
proportion = round(sapply(minima, `[[`, "proportion"), 3),
isGlobal = f_values == f_global
)
result <- list(
minima = minima,
summary = summary_df,
Qvalues = Qvalues_conv,
nConverged = nConverged,
nStarts = randomStarts,
method = method,
orthogonal = orthogonal,
minimumInclusion = minimumInclusion
)
class(result) <- "GPFallMinima"
result
}
print.GPFallMinima <- function(x, ...) {
cat("Random start analysis:", x$nConverged, "of", x$nStarts,
"starts converged\n")
cat("Distinct minima found:", nrow(x$summary),
"(minimumInclusion =", x$minimumInclusion, ")\n\n")
print(x$summary, row.names = FALSE)
cat("\nGlobal minimum: f =", min(x$summary$f), "\n")
cat("Access full solutions via $minima[[i]]$result\n")
invisible(x)
}
###################################################
### code chunk number 3: GPA2local.Stex:212-230
###################################################
library("GPArotation")
data("CCAI", package = "GPArotation")
fa_unrotated <- factanal(factors = 3, covmat = CCAI_R,
n.obs = 461, rotation = "none")
A <- loadings(fa_unrotated)
# Oblimin: highly stable
res_oblimin <- oblimin(A, normalize = TRUE, randomStarts = 200)
cat("Oblimin random start diagnostics:\n")
res_oblimin$randStartChar
# Simplimax: complex landscape
set.seed(42)
res_ccai <- GPFallMinima(A, method = "simplimax",
randomStarts = 200,
normalize = TRUE,
minimumInclusion = 2)
res_ccai
###################################################
### code chunk number 4: GPA2local.Stex:254-256
###################################################
# Print the most common solution
print(res_ccai$minima[[1]]$result)
###################################################
### code chunk number 5: GPA2local.Stex:262-264
###################################################
# Print solution in row 3 of the summary
print(res_ccai$minima[[3]]$result, digits = 2)
###################################################
### code chunk number 6: GPA2local.Stex:269-271
###################################################
i <- 3
print(res_ccai$minima[[i]]$result, digits = 2)
###################################################
### code chunk number 7: GPA2local.Stex:278-281
###################################################
# Print the global minimum using the GPArotation S3 print method
global <- which(res_ccai$summary$isGlobal)
print(res_ccai$minima[[global]]$result, digits = 2)
###################################################
### code chunk number 8: GPA2local.Stex:284-286
###################################################
# Summary with structure matrix for oblique solution
summary(res_ccai$minima[[global]]$result, Structure = TRUE, digits = 2)
###################################################
### code chunk number 9: GPA2local.Stex:308-344
###################################################
plotSortedLoadings <- function(..., labels = NULL, col = NULL,
main = "Sorted Absolute Loadings",
ylab = "Absolute loading",
xlab = "Rank") {
solutions <- list(...)
for (i in seq_along(solutions)) {
if (!inherits(solutions[[i]], "GPArotation"))
stop("Argument ", i, " is not a GPArotation object.")
}
n <- length(solutions)
if (is.null(labels))
labels <- paste("Solution", seq_len(n))
if (is.null(col))
col <- palette.colors(n, palette = "Okabe-Ito")
sorted_loadings <- lapply(solutions, function(x)
sort(abs(as.vector(x$loadings)), decreasing = FALSE))
all_values <- unlist(sorted_loadings)
max_len <- max(sapply(sorted_loadings, length))
plot(NULL,
xlim = c(1, max_len),
ylim = c(0, max(all_values)),
main = main,
xlab = xlab,
ylab = ylab,
las = 1)
abline(h = seq(0, 1, by = 0.1), col = "grey90", lty = 1)
for (i in seq_len(n)) {
lines(seq_along(sorted_loadings[[i]]), sorted_loadings[[i]],
col = col[i], lwd = 2)
points(seq_along(sorted_loadings[[i]]), sorted_loadings[[i]],
col = col[i], pch = 19, cex = 0.6)
}
legend("topleft", legend = labels, col = col, lwd = 2, pch = 19,
bty = "n")
invisible(sorted_loadings)
}
###################################################
### code chunk number 10: GPA2local.Stex:349-354
###################################################
do.call(plotSortedLoadings,
c(lapply(res_ccai$minima, function(x) x$result),
list(labels = paste0("Min ", res_ccai$summary$minimum,
" (f=", round(res_ccai$summary$f, 4),
", n=", res_ccai$summary$count, ")"))))
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.