feat(collocationAnalysis): add cacheAs parameter for result caching
Add a `cacheAs` parameter that accepts a file path (with automatic
`.rds` extension if omitted). If the file exists, the cached result
is returned immediately without contacting the server. Otherwise the
analysis runs normally and the result is saved to the file.
Co-authored-by: Copilot <copilot@github.com>
Change-Id: I60f73a5a824e637d572a7b21b6ea80868a31b4d4
diff --git a/NEWS.md b/NEWS.md
index d264c4c..bfa6f18 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,5 +1,6 @@
# unpublished dev version 1.2.1.9000
+- added `cacheAs` parameter to `collocationAnalysis()` for transparent result caching: if the specified RDS file exists, the cached result is returned immediately; otherwise the analysis runs and the result is saved to the file (`.rds` extension is added automatically if omitted)
- fixed score threshold in recursive CA
- focus is now injected into webUIRequestUrls in collocationAnalysis results, when possible
- added support for comparing collocation analyses across multiple vcs (`max_delta_<score>`, `winner<score>`, `loser_score<score>` columns etc.)
diff --git a/R/collocationAnalysis.R b/R/collocationAnalysis.R
index 4344d1e..806eb7f 100644
--- a/R/collocationAnalysis.R
+++ b/R/collocationAnalysis.R
@@ -45,12 +45,13 @@
#' @param queryMissingScores if TRUE, attempt to retrieve corpus-based association scores for vc/collocate combinations that would otherwise be imputed, by re-querying the KorAP backend without applying the collocate frequency threshold
#' @param missingScoreQuantile lower quantile (evaluated per association measure) that anchors the adaptive floor used for imputing missing scores between virtual corpora; a robust spread is subtracted from this anchor so the imputed values stay below the weakest observed scores
#' @param vcLabel optional label override for the current virtual corpus (used internally when named VC collections are expanded)
+#' @param cacheAs path to an RDS file for caching the result. If the file already exists, the cached result is loaded and returned immediately without contacting the server. Otherwise the analysis is run normally and the result is saved to the file before returning. Defaults to \code{NULL} (no caching).
#' @param ... more arguments will be passed to [collocationScoreQuery()]
#' @inheritParams collocationScoreQuery,KorAPConnection-method
#' @return
#' A tibble where each row represents a candidate collocate for the requested node.
#' Columns include (depending on the selected association measures):
-#'
+#'
#' \itemize{
#' \item \code{node}, \code{collocate}, \code{vc}, \code{label}: identifiers for the query node, collocate, virtual corpus, and optional label.
#' \item Frequency and contingency information such as \code{frequency}, \code{O}, \code{O1}, \code{O2}, \code{E}, \code{leftContextSize}, \code{rightContextSize}, and \code{w}.
@@ -115,9 +116,19 @@
queryMissingScores = FALSE,
missingScoreQuantile = 0.05,
vcLabel = NA_character_,
+ cacheAs = NULL,
...) {
word <- frequency <- O <- NULL
+ if (!is.null(cacheAs) && !grepl("\\.rds$", cacheAs, ignore.case = TRUE)) {
+ cacheAs <- paste0(cacheAs, ".rds")
+ }
+
+ if (!is.null(cacheAs) && file.exists(cacheAs)) {
+ log_info(kco@verbose, sprintf("Loading collocation analysis from cache: %s\n", cacheAs))
+ return(readRDS(cacheAs))
+ }
+
if (!exactFrequencies && (!is.na(withinSpan) && !is.null(withinSpan) && nzchar(withinSpan))) {
stop(sprintf("Not empty withinSpan (='%s') requires exactFrequencies=TRUE", withinSpan), call. = FALSE)
}
@@ -360,6 +371,11 @@
}
}
+ if (!is.null(cacheAs)) {
+ log_info(kco@verbose, sprintf("Saving collocation analysis to cache: %s\n", cacheAs))
+ saveRDS(result, cacheAs)
+ }
+
result
}
)
@@ -752,7 +768,7 @@
if (!all(c(left_col, right_col) %in% names(comparison))) {
next
}
- filled <- fill_scores(comparison[[left_col]], comparison[[right_col]], col)
+ filled <- fill_scores(comparison[[left_col]], comparison[[right_col]], col)
comparison[[left_col]] <- filled$x
comparison[[right_col]] <- filled$y
comparison[[paste0("delta_", col)]] <- filled$x - filled$y
@@ -911,8 +927,8 @@
next
}
- rank_matrix <- as.matrix(rank_values)
- storage.mode(rank_matrix) <- "numeric"
+ rank_matrix <- as.matrix(rank_values)
+ storage.mode(rank_matrix) <- "numeric"
n_rows <- nrow(rank_matrix)
winner_labels <- rep(NA_character_, n_rows)
diff --git a/man/collocationAnalysis-KorAPConnection-method.Rd b/man/collocationAnalysis-KorAPConnection-method.Rd
index bc23235..095ffeb 100644
--- a/man/collocationAnalysis-KorAPConnection-method.Rd
+++ b/man/collocationAnalysis-KorAPConnection-method.Rd
@@ -30,6 +30,7 @@
queryMissingScores = FALSE,
missingScoreQuantile = 0.05,
vcLabel = NA_character_,
+ cacheAs = NULL,
...
)
}
@@ -68,9 +69,9 @@
\item{addExamples}{If TRUE, examples for instances of collocations will be added in a column \code{example}. This makes a difference in particular if \code{node} is given as a lemma query.}
-\item{thresholdScore}{association score function (see \code{\link{association-score-functions}}) to use for computing the threshold that is applied for recursive collocation analysis calls}
+\item{thresholdScore}{association score function (see \code{\link{association-score-functions}}) to use for computing the threshold that is applied for recursive collocation analysis calls (only applied when \code{maxRecurse > 0})}
-\item{threshold}{minimum value of \code{thresholdScore} function call to apply collocation analysis recursively}
+\item{threshold}{minimum value of \code{thresholdScore} function call to apply collocation analysis recursively (only applied when \code{maxRecurse > 0})}
\item{localStopwords}{vector of stopwords that will not be considered as collocates in the current function call, but that will not be passed to recursive calls}
@@ -82,6 +83,8 @@
\item{vcLabel}{optional label override for the current virtual corpus (used internally when named VC collections are expanded)}
+\item{cacheAs}{path to an RDS file for caching the result. If the file already exists, the cached result is loaded and returned immediately without contacting the server. Otherwise the analysis is run normally and the result is saved to the file before returning. Defaults to \code{NULL} (no caching).}
+
\item{...}{more arguments will be passed to \code{\link[=collocationScoreQuery]{collocationScoreQuery()}}}
}
\value{