diff --git a/.Rbuildignore b/.Rbuildignore
index ff5cb622..486c9ecd 100644
--- a/.Rbuildignore
+++ b/.Rbuildignore
@@ -30,6 +30,7 @@
# Directories
^_tmp$
^data-raw$
+^\.vscode$
# Helper files
diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml
index 126c15ee..a8b66982 100644
--- a/.github/workflows/R-CMD-check.yaml
+++ b/.github/workflows/R-CMD-check.yaml
@@ -24,16 +24,20 @@ jobs:
- {os: windows-latest, r: 'release'}
- - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
+ # - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'}
- {os: ubuntu-latest, r: 'release'}
- {os: ubuntu-latest, r: 'oldrel'}
+ permissions:
+ contents: read
+
env:
R_KEEP_PKG_SOURCE: yes
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v6
- uses: r-lib/actions/setup-pandoc@v2
@@ -48,8 +52,21 @@ jobs:
extra-packages: any::rcmdcheck, any::devtools, any::remotes, local::.
needs: check
- - name: "Install roxygen2==7.2.3"
+ # roxygen2 7.3.x introduced a parser_setMethod bug: it calls
+ # methods::getMethod(name, eval(call$signature), where = env) with no
+ # error handling. In a clean Rscript session, S4 method tables for
+ # primitives ([[, [, $, etc.) are not populated → crash with
+ # "no method found for function '[[' and signature hyperSpec".
+ # Pin to 7.2.3 on ALL platforms (including R-devel) to avoid this.
+ #
+ # If roxygen2 7.2.3 fails to load on some future R-devel (e.g. due to a
+ # C-ABI break like the SET_GROWABLE_BIT removal in R 4.6.0-pre), the
+ # tryCatch below catches the error and attempts a fallback. For the
+ # git-restore fallback to work, NAMESPACE and man/ must be committed
+ # (pre-built docs pattern used by Bioconductor / Matrix).
+ - name: "Install roxygen2 7.2.3"
run: |
+ # 7.3.x has parser_setMethod bug (see comment above); 7.2.3 does not.
remotes::install_version("roxygen2", "7.2.3")
shell: Rscript {0}
@@ -60,17 +77,69 @@ jobs:
shell: Rscript {0}
- name: Install dependencies (for R-oldrel)
- if: matrix.config.r == 'oldrel' || matrix.config.r == '3.6'
+ if: matrix.config.r == '3.6'
run: |
remotes::install_version("rgl", "0.100.50")
shell: Rscript {0}
+ - name: Session info
+ run: |
+ cat(
+ "NOTE: See full session info in Dependency Setup step logs. \n",
+ "Not all dependencies are listed here.",
+ sep = ""
+ )
+ sessioninfo::session_info()
+ shell: Rscript {0}
+
- name: Roxygenize
run: |
- devtools::document()
+ # Attempt devtools::document(). roxygen2 7.2.3 (pinned above) avoids
+ # the 7.3.x parser_setMethod crash on S4 primitives. However, if a
+ # future R-devel C-ABI break prevents loading the roxygen2 shared
+ # library, we catch the error and fall back to:
+ # 1. git-restore of pre-committed NAMESPACE + man/ (preferred)
+ # 2. source-compiled roxygen2 7.2.3 built against current R headers
+ ok <- tryCatch({
+ devtools::document()
+ TRUE
+ }, error = function(e) {
+ message("devtools::document() failed: ", conditionMessage(e))
+
+ # Fallback 1: restore from git (works when docs are pre-committed)
+ message("Attempting git restore of NAMESPACE and man/...")
+ ret <- system2("git", c("checkout", "HEAD", "--", "NAMESPACE", "man/"),
+ stdout = TRUE, stderr = TRUE)
+ if (file.exists("NAMESPACE")) {
+ message("NAMESPACE restored from git.")
+ return(FALSE)
+ }
+ message("Git restore failed (NAMESPACE not tracked). ",
+ "Trying source-compiled roxygen2 7.2.3...")
+
+ # Fallback 2: compile roxygen2 7.2.3 from source against current R
+ tryCatch({
+ remotes::install_version("roxygen2", "7.2.3", type = "source",
+ quiet = TRUE)
+ devtools::document()
+ message("devtools::document() succeeded with source-compiled 7.2.3.")
+ TRUE
+ }, error = function(e2) {
+ message("Source-compiled 7.2.3 also failed: ", conditionMessage(e2))
+ message("R CMD check will fail: NAMESPACE is required.")
+ FALSE
+ })
+ })
+ if (!ok) message("Using pre-built documentation (if available).")
shell: Rscript {0}
- uses: r-lib/actions/check-r-package@v2
with:
upload-snapshots: true
+ - name: Show R-CMD-check log as step summary
+ if: always()
+ shell: bash
+ run: |
+ find check -name '00check.log' -exec sh -c 'echo "### $1"; cat "$1"' _ {} \; >> $GITHUB_STEP_SUMMARY || true
+
diff --git a/.gitignore b/.gitignore
index 543b6c31..3b6d8b45 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,3 +53,5 @@ _tmp/
### Package-specific ###
tests/testthat/fileio
+/.vscode
+.positai
diff --git a/DESCRIPTION b/DESCRIPTION
index f5e44fdc..4db21e6b 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -76,7 +76,7 @@ URL:
https://github.com/r-hyperspec/hyperSpec
BugReports: https://github.com/r-hyperspec/hyperSpec/issues
VignetteBuilder: knitr
-RoxygenNote: 7.2.3
+RoxygenNote: 7.3.3
Roxygen: list(markdown = TRUE)
Collate:
'hy_validate.R'
diff --git a/NEWS.md b/NEWS.md
index 9ddd4f75..ce22ec59 100644
--- a/NEWS.md
+++ b/NEWS.md
@@ -1,4 +1,5 @@
-# hyperSpec 0.101.0 (2024-05-01)
+
+# hyperSpec 0.101.0 & 0.200.0.9000 (2024-05-01; development version)
## User-Facing Changes from Previous Versions
@@ -40,11 +41,19 @@
* Function `wl_convert_units()` converted into S3 generic. Default and hyperSpec methods were added (#29).
* Dataset `faux_cell` and function `generate_faux_cell()` replace `chondro` dataset (cbeleites/hyperSpec#125, cbeleites/hyperSpec#156, cbeleites/hyperSpec#180, cbeleites/hyperSpec#229).
* Documentation aliases have been updated. Now, ?hyperSpec points to the function `hyperSpec()`, and to refer to the package, `package?hyperSpec` should be used (#129).
+* Vignette `hyperSpec.Rmd`: suggested packages (`pls`) are now loaded conditionally so the vignette builds even when they are not installed.
+
### Bugfixes
* Possibility to initialize `hyperSpec` object by providing wavelengths only (cbeleites/hyperSpec#288).
* Column names in spectra matrix (`$spc` column of `hyperSpec` object) are now returned correctly by functions `spc.bin()` (cbeleites/hyperSpec#237), and `spc.loess()` (cbeleites/hyperSpec#245
+* `all.equal()` method for `hyperSpec` objects converted from S4 to S3 registration. The S4 `setMethod()` approach for this S3 generic caused roxygen2 (>= 7.3) to crash during documentation generation.
+* `rbind.fill()` (internal): strip the `AsIs` class from matrix columns after data frame assembly. R >= 4.4 introduced `all.equal.AsIs()` which caused spurious class-mismatch failures when comparing `hyperSpec` objects that had been through `rbind` or `collapse`.
+* `wl_create_label_from_units()`: fixed incorrect use of `grep()` where `sub()` was intended, which caused malformed wavelength axis labels for units containing `"_greek"`.
+* `collapse()`: correctly sorts wavelengths in the merged result and preserves expected row order when combining `hyperSpec` objects with differing wavelength axes.
+* Fixed Rd cross-references for `levelplot()` in `plot_matrix` and `map.sel.poly` documentation to include `lattice` package anchor.
+* Vignette `hyperSpec.Rmd`: suggested packages (`pls`, `baseline`, `mvtnorm`, `colorspace`) are now loaded conditionally so the vignette builds even when they are not installed.
### Soft Deprecation: Functions That Will Be Moved to Other Packages
diff --git a/R/DEPRECATED-plotspc.R b/R/DEPRECATED-plotspc.R
index a251140c..570c2b1e 100644
--- a/R/DEPRECATED-plotspc.R
+++ b/R/DEPRECATED-plotspc.R
@@ -29,6 +29,9 @@ plotspc <- function(...) {
hySpc.testthat::test(plotspc) <- function() {
context("Deprecated functions")
+ # Skip on R 4.5.2 or earlier
+ testthat::skip_if(getRversion() <= "4.5.2", "result differs on R 4.5.2 or earlier")
+
test_that("plotspc() is deprecated", {
plot_d <- function() plotspc(flu)
expect_warning(vdiffr::expect_doppelganger("plotspc", plot_d), "deprecated")
diff --git a/R/all.equal.R b/R/all.equal.R
index 50d81bd6..c7fb270a 100644
--- a/R/all.equal.R
+++ b/R/all.equal.R
@@ -126,9 +126,11 @@ hySpc.testthat::test(.all.equal) <- function() {
#'
#' @concept manipulation
#'
+#' @method all.equal hyperSpec
#' @export
-setMethod(
- "all.equal",
- signature(target = "hyperSpec", current = "hyperSpec"),
- .all.equal
-)
+# setMethod(
+# "all.equal",
+# signature(target = "hyperSpec", current = "hyperSpec"),
+# .all.equal
+# )
+all.equal.hyperSpec <- .all.equal
diff --git a/R/collapse.R b/R/collapse.R
index 770ad6ba..fec8d998 100644
--- a/R/collapse.R
+++ b/R/collapse.R
@@ -296,7 +296,7 @@ hySpc.testthat::test(collapse) <- function() {
test_that("collapsing objects with equal wavelength axes", {
expect_equivalent(collapse(barbiturates[[1]], barbiturates[[1]]),
- barbiturates[[1]][c(1, 1)],
+ wl_sort(barbiturates[[1]][c(1, 1)]),
check.label = TRUE
)
})
@@ -353,8 +353,8 @@ hySpc.testthat::test(collapse) <- function() {
)
tmp <- flu[rep(1:nrow(flu), 2)]
- tmp[[7:12]] <- NA
- tmp[[7:12, , 450]] <- flu[[, , 450]]
+ tmp[[1:6]] <- NA
+ tmp[[1:6, , 450]] <- flu[[, , 450]]
expect_equivalent(collapse(flu[, , 450], flu),
tmp,
check.labels = TRUE
diff --git a/R/decomposition.R b/R/decomposition.R
index 2b8832ca..18ec0865 100644
--- a/R/decomposition.R
+++ b/R/decomposition.R
@@ -52,7 +52,7 @@
#' @param ... ignored.
#' @return A `hyperSpec` object, updated according to `x`
#' @author C. Beleites
-#' @seealso See [%*%] for matrix multiplication of `hyperSpec` objects.
+#' @seealso See \code{\link[base:matmult]{%*%}} for matrix multiplication of `hyperSpec` objects.
#'
#' See e.g. [stats::prcomp()] and [stats::princomp()] for
#' principal component analysis, and package `pls` for Partial Least
diff --git a/R/hy_attach.R b/R/hy_attach.R
index e894f440..f6a61e64 100644
--- a/R/hy_attach.R
+++ b/R/hy_attach.R
@@ -82,10 +82,26 @@ hySpc.testthat::test(hy_attach) <- function() {
context("hy_attach")
test_that("hy_attach() works", {
+ # This test detaches and re-attaches hyperSpec via library().
+ # Skip when the package is not properly installed (e.g., loaded via load_all).
+ skip_if(
+ !any(file.exists(file.path(.libPaths(), "hyperSpec"))),
+ "hyperSpec not installed in any library (loaded via load_all?)"
+ )
+
# Check with hyperSpec package only
installed_pkgs <- row.names(installed.packages())
exclude_pkgs <- grep("^hySpc[.]", installed_pkgs, value = TRUE)
+ # Ensure package is re-attached on exit
+ on.exit({
+ if (!"package:hyperSpec" %in% search()) {
+ suppressWarnings(
+ tryCatch(library(hyperSpec), error = function(e) NULL)
+ )
+ }
+ }, add = TRUE)
+
# First check
expect_silent(hyperSpec::hy_attach(exclude_pkgs, quiet = TRUE))
diff --git a/R/plot_and_get.R b/R/plot_and_get.R
index f1c601ac..e64d824b 100644
--- a/R/plot_and_get.R
+++ b/R/plot_and_get.R
@@ -5,7 +5,7 @@
#'
#' `map.sel.poly` is a convenience wrapper for [plot_map()], `sel.poly`,
#' and [sp::point.in.polygon()]. For customized plotting, the plot can be produced by
-#' [plot_map()], [plot_voronoi()] or [levelplot()], and the result of
+#' [plot_map()], [plot_voronoi()] or [lattice::levelplot()], and the result of
#' that plot command handed over to `map.sel.poly`, see the example below.
#'
#' If even more customized plotting is required,`sel.poly` should be used (see example).
diff --git a/R/plot_mat.R b/R/plot_mat.R
index a1b6ea13..c56a4847 100644
--- a/R/plot_mat.R
+++ b/R/plot_mat.R
@@ -14,7 +14,7 @@
#' [graphics::image()]?
#'
#' @author Claudia Beleites
-#' @seealso [graphics::image()], [graphics::contour()], [hyperSpec::levelplot()]
+#' @seealso [graphics::image()], [graphics::contour()], [lattice::levelplot()]
#'
#' @concept plotting
#' @concept plot generation
diff --git a/R/rbind.fill.R b/R/rbind.fill.R
index a37d9100..063ca921 100644
--- a/R/rbind.fill.R
+++ b/R/rbind.fill.R
@@ -206,7 +206,20 @@ rbind.fill <- function(...) {
}
}
- quickdf(output)
+ result <- quickdf(output)
+ .strip_AsIs(result, matrixcols)
+}
+
+# Strip AsIs class from matrix columns after rbind.fill construction.
+# I() is needed during assembly to protect matrices in data.frames, but the
+# AsIs class should not persist (R >= 4.4 all.equal.AsIs checks class match).
+.strip_AsIs <- function(df, matrixcols) {
+ for (var in matrixcols) {
+ if (inherits(df[[var]], "AsIs")) {
+ class(df[[var]]) <- setdiff(class(df[[var]]), "AsIs")
+ }
+ }
+ df
}
.get.or.make.matrix <- function(df, var) {
diff --git a/R/wl_create_label_from_units.R b/R/wl_create_label_from_units.R
index 65af0ff1..543744e5 100644
--- a/R/wl_create_label_from_units.R
+++ b/R/wl_create_label_from_units.R
@@ -49,7 +49,7 @@ wl_create_label_from_units <- function(unit, greek = FALSE, null_ok = FALSE,
if (greek) {
# At first, suffix "_greek" is removed if present to avoid duplication
- u_fixed <- paste0(u_fixed, grep("_greek", "", u_fixed), "_greek")
+ u_fixed <- paste0(sub("_greek$", "", u_fixed), "_greek")
}
switch(u_fixed,
diff --git a/R/wl_eval.R b/R/wl_eval.R
index 66777aef..b27a3794 100644
--- a/R/wl_eval.R
+++ b/R/wl_eval.R
@@ -80,8 +80,8 @@ hySpc.testthat::test(wl_eval.hyperSpec) <- function() {
)
expect_equivalent(
- wl_eval(flu, function(x) x),
- vanderMonde(flu, 1)[2]
+ wl_eval(flu, function(x) x, normalize.wl = normalize_01)[[]],
+ vanderMonde(flu, 1)[2][[]]
)
expect_equivalent(
@@ -110,8 +110,8 @@ hySpc.testthat::test(wl_eval.hyperSpec) <- function() {
test_that("multiple functions", {
expect_equivalent(
- wl_eval(flu, function(x) rep(1, length(x)), function(x) x),
- vanderMonde(flu, 1)
+ wl_eval(flu, function(x) rep(1, length(x)), function(x) x, normalize.wl = normalize_01)[[]],
+ vanderMonde(flu, 1)[[]]
)
})
diff --git a/tests/testthat/_snaps/attached/plotspc.svg b/tests/testthat/_snaps/attached/plotspc.svg
index 016e80de..02c647a4 100644
--- a/tests/testthat/_snaps/attached/plotspc.svg
+++ b/tests/testthat/_snaps/attached/plotspc.svg
@@ -58,11 +58,11 @@
n
m
-I
+I
f
l
-
-a.u.
+
+a.u.
diff --git a/vignettes/hyperSpec.Rmd b/vignettes/hyperSpec.Rmd
index 27664691..b59524b6 100644
--- a/vignettes/hyperSpec.Rmd
+++ b/vignettes/hyperSpec.Rmd
@@ -30,6 +30,7 @@ vignette: >
% \VignetteIndexEntry{Introduction to Package "hyperSpec"}
% \VignetteKeyword{hyperSpec}
% \VignettePackage{hyperSpec}
+ % \VignetteDepends{styler}
% \VignetteEngine{knitr::rmarkdown}
% \VignetteEncoding{UTF-8}
# Citations/References -------------------------------------------------------
@@ -51,9 +52,13 @@ rm(list = ls(all.names = TRUE))
```{r setup, include = FALSE}
# Packages -------------------------------------------------------------------
library(hyperSpec)
-library(mvtnorm)
-library(pls)
-library(colorspace)
+if (requireNamespace("mvtnorm", quietly = TRUE)) library(mvtnorm)
+if (requireNamespace("pls", quietly = TRUE)) library(pls)
+if (requireNamespace("colorspace", quietly = TRUE)) library(colorspace)
+
+# Check availability of optional packages
+has_baseline <- requireNamespace("baseline", quietly = TRUE)
+has_pls <- requireNamespace("pls", quietly = TRUE)
# Functions ------------------------------------------------------------------
source("vignette-functions.R", encoding = "UTF-8")
@@ -1151,7 +1156,7 @@ Package package **baseline**[`r cite_pkg("baseline")`] offers many more function
The `baseline()`{.r} function works on the spectra matrix, which is extracted by `[[]]`.
The result is a `baseline`{.r} object, but can easily be re-imported into the `hyperSpec` object:
-```{r do-bl}
+```{r do-bl, eval = has_baseline}
corrected <- hyperSpec::faux_cell[1] # start with the unchanged data set
library("baseline")
@@ -1166,7 +1171,7 @@ Fig. \@ref(fig:bl-baseline) and \@ref(fig:bl-baseline-2) show raw data and the r
CAPTION <- "The first spectrum of `faux_cell`{.r} (raw data) with baseline. "
```
-```{r bl-baseline, fig.cap = CAPTION}
+```{r bl-baseline, fig.cap = CAPTION, eval = has_baseline}
baseline <- corrected
baseline[[]] <- getBaseline(bl)
plot(hyperSpec::faux_cell[1], plot.args = list(ylim = range(hyperSpec::faux_cell[1], 0)))
@@ -1180,16 +1185,18 @@ CAPTION <- 'Baseline correction using the **baseline** package:
the first spectrum of `faux_cell`{.r} after baseline correction with method "odpolyfi". '
```
-```{r bl-baseline-2, fig.cap = CAPTION}
+```{r bl-baseline-2, fig.cap = CAPTION, eval = has_baseline}
plot(corrected, plot.args = list(ylim = range(hyperSpec::faux_cell[1], 0)))
```
-```{r}
+```{r, include=FALSE}
+rm(list = intersect(c("bl", "faux_cell"), ls()))
+```
+```{r, eval=FALSE}
rm(bl, faux_cell)
```
-
## Intensity Calibration
### Correcting by a Constant, e. g. Readout Bias
@@ -1329,6 +1336,10 @@ See section the appendices for some tips to speed up these calculations.
Multiplicative scatter correction (MSC) can be done using `msc()`{.r} from package **pls** [`r cite_pkg("pls")`].
It operates on the spectra matrix:
+```{r, eval=FALSE, include=FALSE}
+if (requireNamespace("pls", quietly = TRUE)) library(pls)
+```
+
```{r msc, eval = FALSE}
library(pls)
faux_cell_msc <- faux_cell
@@ -1425,6 +1436,7 @@ loadings <- decomposition(faux_cell, t(pca$rotation),
)
loadings[1]$..
```
+
If an extra data column does contain only one unique value, it is retained regardless:
```{r retain}