From 325e021c96bf7db1fa1892da081bd67bc9ffe9dd Mon Sep 17 00:00:00 2001 From: Zacharias Steinmetz Date: Wed, 16 Aug 2023 11:31:19 +0200 Subject: [PATCH] Improve `conform_spec()` - renamed `new_wavenumbers` argument to `range` - updated docs - added tests --- R/adj_range.R | 6 ++-- R/conform_spec.R | 31 +++++++++------- R/data_norm.R | 4 ++- R/interactive_plots.R | 2 +- R/manage_spec.R | 2 +- R/match_spec.R | 2 +- R/process_spec.R | 2 +- man/adj_range.Rd | 6 ++-- man/conform_spec.Rd | 20 +++++++---- man/data_norm.Rd | 4 ++- man/interactive_plots.Rd | 2 +- man/match_spec.Rd | 2 +- tests/testthat/test-conform_spec.R | 58 ++++++++++++++++-------------- tests/testthat/test-match_spec.R | 2 +- tests/testthat/test-workflows.R | 4 +-- 15 files changed, 86 insertions(+), 61 deletions(-) diff --git a/R/adj_range.R b/R/adj_range.R index 922134f3..56331d2c 100644 --- a/R/adj_range.R +++ b/R/adj_range.R @@ -37,8 +37,10 @@ #' Win Cowger, Zacharias Steinmetz #' #' @seealso -#' \code{\link[base]{min}()} and \code{\link[base]{round}()}; -#' \code{\link{adj_intens}()} for log transformation functions +#' \code{\link{conform_spec}()} for conforming wavenumbers to be matched with +#' a reference library; +#' \code{\link{adj_intens}()} for log transformation functions; +#' \code{\link[base]{min}()} and \code{\link[base]{round}()} #' #' @importFrom data.table as.data.table .SD #' @export diff --git a/R/conform_spec.R b/R/conform_spec.R index ef43b2e0..9ffc7d57 100644 --- a/R/conform_spec.R +++ b/R/conform_spec.R @@ -1,17 +1,19 @@ #' @rdname conform_spec -#' #' @title Conform spectra to a standard wavenumber series #' #' @description -#' Spectra can be conformed to a standard suite of wavenumbers to be compared with a reference library or to be merged to other spectra. +#' Spectra can be conformed to a standard suite of wavenumbers to be compared +#' with a reference library or to be merged to other spectra. #' #' @param x a list object of class \code{OpenSpecy}. -#' @param new_wavenumbers a vector of new wavenumber values, can be just supplied as a min and max value. +#' @param range a vector of new wavenumber values, can be just supplied as a +#' min and max value. #' @param res spectral resolution adjusted to. +#' @param \ldots further arguments passed to \code{\link[stats]{approx}()} #' #' @return #' \code{adj_intens()} returns a data frame containing two columns -#' named \code{"wavenumber"} and \code{"intensity"}. +#' named \code{"wavenumber"} and \code{"intensity"} #' #' @examples #' data("raman_hdpe") @@ -21,34 +23,37 @@ #' Win Cowger, Zacharias Steinmetz #' #' @seealso -#' \code{\link{subtr_bg}()} for spectral background correction. +#' \code{\link{restrict_range}()} and \code{\link{flatten_range}()} for +#' adjusting wavenumber ranges; +#' \code{\link{subtr_bg}()} for spectral background correction #' #' @importFrom data.table .SD #' @export -conform_spec <- function(x, new_wavenumbers, res = 5) { +conform_spec <- function(x, ...) { UseMethod("conform_spec") } #' @rdname conform_spec #' #' @export -conform_spec.default <- function(x, new_wavenumbers, res = 5) { +conform_spec.default <- function(x, ...) { stop("object 'x' needs to be of class 'OpenSpecy'", call. = F) } #' @rdname conform_spec #' #' @export -conform_spec.OpenSpecy <- function(x, new_wavenumbers, res = 5) { - if(is.null(new_wavenumbers)){ - new_wavenumbers <- x$wavenumber - } +conform_spec.OpenSpecy <- function(x, range = NULL, res = 5, ...) { + if(is.null(range)) range <- x$wavenumber + range <- c(max(min(range), min(x$wavenumber)), + min(max(range), max(x$wavenumber)) + ) - wn <- conform_res(x = new_wavenumbers[new_wavenumbers <= max(x$wavenumber) & new_wavenumbers >= min(x$wavenumber)], res = res) + wn <- conform_res(range, res = res) spec <- x$spectra[, lapply(.SD, .conform_intens, x = x$wavenumber, - xout = wn)] + xout = wn, ...)] x$wavenumber <- wn x$spectra <- spec diff --git a/R/data_norm.R b/R/data_norm.R index d937ae87..224ec58e 100644 --- a/R/data_norm.R +++ b/R/data_norm.R @@ -49,7 +49,9 @@ #' #' @seealso #' \code{\link[base]{min}()} and \code{\link[base]{round}()}; -#' \code{\link{adj_intens}()} for log transformation functions +#' \code{\link{adj_intens}()} for log transformation functions; +#' \code{\link{conform_spec}()} for conforming wavenumbers of an +#' \code{OpenSpecy} object to be matched with a reference library #' #' @export adj_res <- function(x, res = 1, fun = round) { diff --git a/R/interactive_plots.R b/R/interactive_plots.R index 86fb152c..9b7c0132 100644 --- a/R/interactive_plots.R +++ b/R/interactive_plots.R @@ -23,7 +23,7 @@ #' plotly_spec(raman_hdpe) #' #' correlation <- cor_spec( -#' conform_spec(raman_hdpe, new_wavenumbers = raman_hdpe$wavenumber, res = 5), +#' conform_spec(raman_hdpe, range = raman_hdpe$wavenumber, res = 5), #' conform_spec(tiny_map, tiny_map$wavenumbers, res = 5) #' ) #' heatmap_spec(tiny_map, z = tiny_map$metadata$y) diff --git a/R/manage_spec.R b/R/manage_spec.R index 78aaa9ca..73291a3f 100644 --- a/R/manage_spec.R +++ b/R/manage_spec.R @@ -53,7 +53,7 @@ c_spec <- function(objects, wavenumber_transform = NULL, res = 5){ } objects <- lapply(objects, function(x) { conform_spec(x, - new_wavenumbers = new_wavenumbers, + range = new_wavenumbers, res = res)}) } diff --git a/R/match_spec.R b/R/match_spec.R index e9b34758..3fc3866b 100644 --- a/R/match_spec.R +++ b/R/match_spec.R @@ -27,7 +27,7 @@ #' @examples #' data("test_lib") #' unknown <- read_any(read_extdata("ftir_ldpe_soil.asp")) |> -#' conform_spec(new_wavenumbers = test_lib$wavenumber, +#' conform_spec(range = test_lib$wavenumber, #' res = spec_res(test_lib)) |> #' process_spec() #' matches <- cor_spec(unknown, test_lib) diff --git a/R/process_spec.R b/R/process_spec.R index b3678c29..de31d282 100644 --- a/R/process_spec.R +++ b/R/process_spec.R @@ -121,7 +121,7 @@ process_spec.OpenSpecy <- function(x, if(adj_intensity_decision) x <- adj_intens(x, type = type, make_rel = F) if(conform_decision) - x <- conform_spec(x, new_wavenumbers = new_wavenumbers, res = res) + x <- conform_spec(x, range = new_wavenumbers, res = res) if(range_decision) x <- restrict_range(x, min_range = min_range, max_range = max_range, make_rel = F) diff --git a/man/adj_range.Rd b/man/adj_range.Rd index 031d377a..54abd995 100644 --- a/man/adj_range.Rd +++ b/man/adj_range.Rd @@ -60,8 +60,10 @@ plot(flattened_intensities) } \seealso{ -\code{\link[base]{min}()} and \code{\link[base]{round}()}; -\code{\link{adj_intens}()} for log transformation functions +\code{\link{conform_spec}()} for conforming wavenumbers to be matched with +a reference library; +\code{\link{adj_intens}()} for log transformation functions; +\code{\link[base]{min}()} and \code{\link[base]{round}()} } \author{ Win Cowger, Zacharias Steinmetz diff --git a/man/conform_spec.Rd b/man/conform_spec.Rd index de740aae..a2f5caea 100644 --- a/man/conform_spec.Rd +++ b/man/conform_spec.Rd @@ -6,25 +6,29 @@ \alias{conform_spec.OpenSpecy} \title{Conform spectra to a standard wavenumber series} \usage{ -conform_spec(x, new_wavenumbers, res = 5) +conform_spec(x, ...) -\method{conform_spec}{default}(x, new_wavenumbers, res = 5) +\method{conform_spec}{default}(x, ...) -\method{conform_spec}{OpenSpecy}(x, new_wavenumbers, res = 5) +\method{conform_spec}{OpenSpecy}(x, range = NULL, res = 5, ...) } \arguments{ \item{x}{a list object of class \code{OpenSpecy}.} -\item{new_wavenumbers}{a vector of new wavenumber values, can be just supplied as a min and max value.} +\item{range}{a vector of new wavenumber values, can be just supplied as a +min and max value.} \item{res}{spectral resolution adjusted to.} + +\item{\ldots}{further arguments passed to \code{\link[stats]{approx}()}} } \value{ \code{adj_intens()} returns a data frame containing two columns -named \code{"wavenumber"} and \code{"intensity"}. +named \code{"wavenumber"} and \code{"intensity"} } \description{ -Spectra can be conformed to a standard suite of wavenumbers to be compared with a reference library or to be merged to other spectra. +Spectra can be conformed to a standard suite of wavenumbers to be compared +with a reference library or to be merged to other spectra. } \examples{ data("raman_hdpe") @@ -32,7 +36,9 @@ conform_spec(raman_hdpe, c(1000, 2000)) } \seealso{ -\code{\link{subtr_bg}()} for spectral background correction. +\code{\link{restrict_range}()} and \code{\link{flatten_range}()} for +adjusting wavenumber ranges; +\code{\link{subtr_bg}()} for spectral background correction } \author{ Win Cowger, Zacharias Steinmetz diff --git a/man/data_norm.Rd b/man/data_norm.Rd index fb1ac88a..b3d9c184 100644 --- a/man/data_norm.Rd +++ b/man/data_norm.Rd @@ -71,7 +71,9 @@ make_rel(c(-1000, -1, 0, 1, 10)) } \seealso{ \code{\link[base]{min}()} and \code{\link[base]{round}()}; -\code{\link{adj_intens}()} for log transformation functions +\code{\link{adj_intens}()} for log transformation functions; +\code{\link{conform_spec}()} for conforming wavenumbers of an +\code{OpenSpecy} object to be matched with a reference library } \author{ Win Cowger, Zacharias Steinmetz diff --git a/man/interactive_plots.Rd b/man/interactive_plots.Rd index 32a4195f..aaa77d39 100644 --- a/man/interactive_plots.Rd +++ b/man/interactive_plots.Rd @@ -78,7 +78,7 @@ tiny_map <- read_zip(read_extdata("CA_tiny_map.zip")) plotly_spec(raman_hdpe) correlation <- cor_spec( - conform_spec(raman_hdpe, new_wavenumbers = raman_hdpe$wavenumber, res = 5), + conform_spec(raman_hdpe, range = raman_hdpe$wavenumber, res = 5), conform_spec(tiny_map, tiny_map$wavenumbers, res = 5) ) heatmap_spec(tiny_map, z = tiny_map$metadata$y) diff --git a/man/match_spec.Rd b/man/match_spec.Rd index d10747d2..0c5312c9 100644 --- a/man/match_spec.Rd +++ b/man/match_spec.Rd @@ -79,7 +79,7 @@ This function correlates two \code{OpenSpecy} objects, typically one with knowns \examples{ data("test_lib") unknown <- read_any(read_extdata("ftir_ldpe_soil.asp")) |> - conform_spec(new_wavenumbers = test_lib$wavenumber, + conform_spec(range = test_lib$wavenumber, res = spec_res(test_lib)) |> process_spec() matches <- cor_spec(unknown, test_lib) diff --git a/tests/testthat/test-conform_spec.R b/tests/testthat/test-conform_spec.R index eb7e3ce2..7719911c 100644 --- a/tests/testthat/test-conform_spec.R +++ b/tests/testthat/test-conform_spec.R @@ -1,36 +1,42 @@ -spectrum <- read_extdata("raman_hdpe.csv") |> read_any() - -test_that("check that conforming spectra doesn't produce errors", { - expect_true(is_OpenSpecy(conform_spec(spectrum, c(min(spectrum$wavenumber + 10), max(spectrum$wavenumber))))) - expect_equal(conform_spec(spectrum, c(1000, 2000))$wavenumber, seq(1000, 2000, by = 5)) - expect_error(is_OpenSpecy(conform_spec(spectrum, c(min(spectrum$wavenumber + 10))))) - expect_error(is_OpenSpecy(conform_spec(spectrum))) - expect_error(is_OpenSpecy(conform_spec(spectrum, c(min(spectrum$wavenumber + 10), max(spectrum$wavenumber)), res = 0))) -}) +library(data.table) + +data("raman_hdpe") -test_that("conform_spec.default throws an error for non-OpenSpecy objects", { +test_that("conform_spec() throws an error for non-OpenSpecy objects", { # Create a non-OpenSpecy object - non_open_specy_object <- data.frame(wavenumber = c(1000, 1100, 1200), - intensity = c(0.1, 0.2, 0.3)) + non_OpenSpecy <- data.frame(wavenumber = c(1000, 1100, 1200), + intensity = c(0.1, 0.2, 0.3)) + + conform_spec(non_OpenSpecy, c(1000, 2000)) |> + expect_error() +}) - # Test if the function throws an error for non-OpenSpecy object - expect_error(conform_spec(non_open_specy_object, c(1000, 2000))) +test_that("conform_spec() handles errors correctly", { + conform_spec(raman_hdpe, range = c(min(raman_hdpe$wavenumber + 10), + max(raman_hdpe$wavenumber))) |> + expect_silent() + conform_spec(raman_hdpe, range = c(0,5000)) |> + expect_silent() + expect_equal(conform_spec(raman_hdpe, c(1000, 2000))$wavenumber, + seq(1000, 2000, by = 5)) + conform_spec(raman_hdpe, c(min(raman_hdpe$wavenumber + 10))) |> + expect_error() + conform_spec(raman_hdpe, c(min(raman_hdpe$wavenumber + 10), + max(raman_hdpe$wavenumber)), res = 0) |> + expect_error() }) -test_that("conform_spec.OpenSpecy conforms wavenumbers correctly", { - # Create a sample OpenSpecy object - wavenumber <- seq(1000, 2000, 5) - intensity <- rnorm(length(wavenumber)) - open_specy_object <- as_OpenSpecy(wavenumber, data.table(intensity = intensity)) +test_that("conform_spec() conforms wavenumbers correctly", { + sam <- as_OpenSpecy(wn <- seq(1000, 2000, 5), + data.table(intensity = rnorm(length(wn)))) - # Conform the wavenumbers to a new range new_wavenumbers <- c(1100, 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900) - conform_result <- conform_spec(open_specy_object, new_wavenumbers) + conf_spec <- conform_spec(sam, new_wavenumbers) - # Test if the wavenumber and intensity vectors have the same length after conforming - expect_equal(length(conform_result$wavenumber), length(conform_result$spectra[[1]])) + expect_equal(length(conf_spec$wavenumber), length(conf_spec$spectra[[1]])) + expect_equal(range(conf_spec$wavenumber), range(new_wavenumbers)) - # Test if the wavenumber range matches the new_wavenumbers vector - expect_equal(min(conform_result$wavenumber), min(new_wavenumbers)) - expect_equal(max(conform_result$wavenumber), max(new_wavenumbers)) + conform_spec(raman_hdpe)$spectra$intensity[c(63, 143, 283, 325, 402)] |> + round(2) |> + expect_equal(c(78.84, 65.00, 105.73, 109.41, 116.00)) }) diff --git a/tests/testthat/test-match_spec.R b/tests/testthat/test-match_spec.R index 8ea589ac..f3304274 100644 --- a/tests/testthat/test-match_spec.R +++ b/tests/testthat/test-match_spec.R @@ -1,7 +1,7 @@ # Create test data for cor_spec function data("test_lib") unknown <- read_any(read_extdata("ftir_ldpe_soil.asp")) |> - conform_spec(new_wavenumbers = test_lib$wavenumber, res = spec_res(test_lib)) |> + conform_spec(range= test_lib$wavenumber, res = spec_res(test_lib)) |> process_spec() # Create a subset of test_lib for filtering diff --git a/tests/testthat/test-workflows.R b/tests/testthat/test-workflows.R index 4a35ea75..2dea3b16 100644 --- a/tests/testthat/test-workflows.R +++ b/tests/testthat/test-workflows.R @@ -16,7 +16,7 @@ test_that("Raman batch analysis with test library", { expect_silent(lib <- load_lib(type = "test", path = tmp)) expect_silent(filter_spec(lib, lib$metadata$SpectrumType == "Raman")) - expect_silent(batch2 <- conform_spec(batch, new_wavenumbers = lib$wavenumber, + expect_silent(batch2 <- conform_spec(batch, lib$wavenumber, res = spec_res(lib$wavenumber))) expect_silent(plotly_spec(batch2)) @@ -48,7 +48,7 @@ test_that("Raman batch analysis with complete library", { expect_silent(lib <- load_lib(type = "nobaseline", path = tmp)) expect_silent(filter_spec(lib, lib$metadata$SpectrumType == "Raman")) - expect_silent(batch2 <- conform_spec(batch, new_wavenumbers = lib$wavenumber, + expect_silent(batch2 <- conform_spec(batch, range = lib$wavenumber, res = spec_res(lib$wavenumber))) expect_silent(plotly_spec(batch2))