-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
stitch.rspec function for row-wise merging #249
Open
thomased
wants to merge
12
commits into
master
Choose a base branch
from
stitch
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
d04ac52
First pass
thomased 2d237ec
Merge master
eb9a3cc
Fix linting
a4108c5
Implement method/generic
52e8073
Merge master
e53560c
Fix up generic/method
7ba3d0d
Add to pkgdown
7423e54
Merge master
22f1bc8
Add warnings/errors and tests
c16cb06
Update news
f395b89
Damn linting
66e240d
Update cran comments. Minor tweaks for cran
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,155 @@ | ||
#' Stitch together two rspec objects | ||
#' | ||
#' Stitch (row-wise merge) two `rspec` objects of differing wavelength ranges into | ||
#' a single `rspec` object. | ||
#' | ||
#' @param rspec1,rspec2 (required) `rspec` objects of differing wavelength ranges | ||
#' to stitch together by row. | ||
#' @param overlap_method the method for modifying reflectance values if regions | ||
#' of the spectra overlap in their wavelength range. Defaults to `mean`. | ||
#' @param interp logical argument specifying whether reflectance values should be | ||
#' interpolated between the two sets of spectra if their wavelength ranges | ||
#' do not overlap. Defaults to `TRUE`. | ||
#' | ||
#' @export | ||
#' | ||
#' @examples | ||
#' | ||
#' # Simulate a UV-VIS and NIR reflectance spectrum whose wavelength regions | ||
#' # slightly overlap then stitch them together, with the overlapping | ||
#' # regions being averaged. | ||
#' | ||
#' # Simulate specs | ||
#' reflect1 <- simulate_spec(wl_peak = 550, xlim = c(300, 700)) | ||
#' reflect2 <- simulate_spec(wl_inflect = 1100, xlim = c(650, 1200)) | ||
#' | ||
#' # Ensure the names of the spectra match | ||
#' names(reflect1) <- names(reflect2) <- c('wl', 'sample_1') | ||
#' | ||
#' # Stitch the spectra together by their wavelength column | ||
#' full_spec <- stitch(reflect1, reflect2) | ||
#' | ||
#' # Plot the resulting spectrum | ||
#' plot(full_spec) | ||
#' | ||
#' # Simulate another set of UV-VIS and NIR spectra. Note two additional complexities, | ||
#' # both of which are handled without issue. First, the wavelength ranges are | ||
#' # non-overlapping (with a 100 nm gap). We'll keep the default interp = TRUE argument | ||
#' # to allow the missing reflectance region to be interpolated. Second, the names of | ||
#' # the spectra match, but are in a different order in the two rspec objects. This isn't | ||
#' # an issue, as the function can match up the spectra by name irrespective of their | ||
#' # ordering | ||
#' | ||
#' # Simulate UV-VIS and NIR spectra | ||
#' reflect_vis <- merge(simulate_spec(wl_peak = 550, xlim = c(300, 700)), | ||
#' simulate_spec(wl_peak = 550, xlim = c(300, 700))) | ||
#' reflect_nir <- merge(simulate_spec(wl_inflect = 1000, xlim = c(800, 1250)), | ||
#' simulate_spec(wl_inflect = 1100, xlim = c(800, 1250))) | ||
#' | ||
#' # Ensure the names of the spectra exist in each, albeit in a different order | ||
#' names(reflect_vis) <- c('wl', 'sample_1', 'sample_2') | ||
#' names(reflect_nir) <- c('wl', 'sample_2', 'sample_1') | ||
#' | ||
#' # Stitch together by their wavelength column, with missing regions being | ||
#' # interpolated | ||
#' reflect_vis_nir <- stitch(reflect_vis, reflect_nir) | ||
#' | ||
#' # Plot the resulting spectrum | ||
#' plot(reflect_vis_nir) | ||
#' | ||
#' @author Thomas White \email{thomas.white026@@gmail.com} | ||
#' @author Hugo Gruson \email{hugo.gruson+R@@normalesup.org} | ||
#' | ||
#' @seealso [as.rspec()], [merge.rspec()] | ||
|
||
stitch <- function(rspec1, rspec2, overlap_method, interp) { | ||
UseMethod("stitch") | ||
} | ||
|
||
#' @rdname stitch | ||
#' | ||
#' @export | ||
stitch.rspec <- function(rspec1, rspec2, | ||
overlap_method = c("mean", "minimum", "maximum"), | ||
interp = TRUE) { | ||
|
||
# Class check | ||
if (!inherits(rspec1, "rspec") || !inherits(rspec2, "rspec")) { | ||
stop("Both inputs must be of class 'rspec'") | ||
} | ||
|
||
# Validate overlap_method | ||
overlap_method <- match.arg(overlap_method) | ||
|
||
# Check that at least one spectrum has a matching name in both objects | ||
common_cols <- intersect(names(rspec1), names(rspec2)) | ||
if (length(common_cols) <= 1) { | ||
stop("At least one spectrum in both rspec objects must have a matching name") | ||
} | ||
|
||
# Warn if only subset is present across both rspec objects | ||
if (length(common_cols) != ncol(rspec1) || length(common_cols) != ncol(rspec2)) { | ||
warning("Not all spectra are present in both objects. Stitching only the common samples.") | ||
} | ||
|
||
# Identify unique spectra in both objects | ||
unique_rspec1 <- setdiff(names(rspec1), common_cols) | ||
unique_rspec2 <- setdiff(names(rspec2), common_cols) | ||
|
||
# Create NA-filled columns in each rspec for unique spectra in the other | ||
rspec1[, unique_rspec2] <- NA | ||
rspec2[, unique_rspec1] <- NA | ||
|
||
# Reorder columns of rspec2 to match rspec1 | ||
rspec2 <- rspec2[, names(rspec1)] | ||
|
||
# Merge by wl | ||
res <- rbind(rspec1, rspec2) | ||
|
||
# Handle overlapping wl values | ||
if (anyDuplicated(res$wl) > 0) { | ||
overlap_wl <- unique(res$wl[duplicated(res$wl)]) | ||
|
||
for (wl in overlap_wl) { | ||
idx <- which(res$wl == wl) | ||
|
||
# Replace with a switch statement | ||
switch(overlap_method, | ||
mean = res[idx[1], -1] <- colMeans(as.matrix(res[idx, -1]), na.rm = TRUE), | ||
minimum = res[idx[1], -1] <- apply(as.matrix(res[idx, -1]), 2, min, na.rm = TRUE), | ||
maximum = res[idx[1], -1] <- apply(as.matrix(res[idx, -1]), 2, max, na.rm = TRUE) | ||
) | ||
|
||
# Remove extra rows | ||
if (length(idx) > 1) { | ||
res <- res[-idx[-1], ] | ||
} | ||
} | ||
} | ||
|
||
# Interpolate missing values | ||
if (interp) { | ||
full_wl_range <- min(res$wl):max(res$wl) | ||
missing_wl <- setdiff(full_wl_range, res$wl) | ||
|
||
if (length(missing_wl) > 0) { | ||
new_rows <- data.frame(wl = missing_wl, matrix(rep(NA, length(names(res)) - 1), ncol = length(names(res)) - 1)) | ||
names(new_rows)[-1] <- names(res)[-1] | ||
|
||
# Interpolate only common spectra | ||
for (col in common_cols[-1]) { | ||
new_values <- approx(res$wl, res[, col], xout = missing_wl)$y | ||
new_rows[, col] <- new_values | ||
} | ||
res <- rbind(res, new_rows) | ||
} | ||
} | ||
|
||
# Sort stitched spec by wl | ||
res <- res[order(res$wl), ] | ||
|
||
# Classes | ||
class(res) <- c("rspec", "data.frame") | ||
|
||
res | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
To leave room for future extensions or other methods.
We may want to re-use this generic for other objects in the future and it's probably safer to offer more flexibility in the argument names. This is the same reason why we use
x
as the first argument insubset.rspec()
,plot.rspec()
, etc.If we don't expect ever re-using this generic for other objects, we could also not leverage S3 and propose a
stitch_rspec()
orstitchrspec()
function directly. The same way we do it withexplorespec()
,aggspec()
, etc.