From de30b7883763b708d4afc4424fe4e846001972b4 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Mon, 23 Feb 2026 15:20:15 +0100 Subject: [PATCH 01/27] first draft of a mechanism to consume the unit name in the conversion --- R/archchem_basic.R | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 9641eb1..bd56f97 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -169,6 +169,8 @@ as_archchem <- function( purrr::discard(is.null) # turn into tibble-derived object df <- tibble::new_tibble(df, nrow = nrow(df), class = "archchem") + # remove unit names from columns if they got a unit + df <- remove_unit_substrings(df) # post-reading validation if (validate) { validation_output <- validate(df, quiet = FALSE) @@ -182,6 +184,21 @@ as_archchem <- function( return(df) } +# helper function to rename column names +remove_unit_substrings <- function(x, ...) { + dplyr::rename_with( + x, + remove_unit_substring, + tidyselect::where(function(y) { + class(y) == "units" + }) + ) +} + +remove_unit_substring <- function(colname) { + sub("_.*$", "", colname, perl = TRUE) +} + #' @param path path to the file that should be read #' @param delim A character string with the separator for tabular data. Must be #' provided for all file types except `.xlsx` or `.xls`. Default to `,`. Use From 1675a6b25058384a55ac5434fe8ab60517988a04 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Mon, 23 Feb 2026 16:05:54 +0100 Subject: [PATCH 02/27] look at this @archaeothommy - it seems we can very easily define our own units in relation to existing units --- R/archchem_colname_parser.R | 3 ++- R/zzz.R | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index b5e3ff4..8f82414 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -93,7 +93,8 @@ colnames_to_constructors <- function( # handle special cases unit_from_col <- dplyr::case_match( unit_from_col, - c("at%", "wt%") ~ "%", + c("at%", "atP") ~ "atP", + c("wt%", "wtP") ~ "wtP", c("cps") ~ "count/s", .default = unit_from_col ) diff --git a/R/zzz.R b/R/zzz.R index 209e50d..47157d2 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -8,3 +8,24 @@ globalVariables(".") #' @importFrom rlang .data := #' NULL + +# registers the dimensionless units at% (atom percent) and wt% (weight percent) +# when the package is loaded +.onLoad <- function(libname, pkgname) { + try( + units::install_unit( + symbol = "atP", # at% is not allowed https://github.com/r-quantities/units/issues/289 + def = "percent", + name = "atom percent" + ), + silent = TRUE + ) + try( + units::install_unit( + symbol = "wtP", + def = "percent", + name = "weight percent" + ), + silent = TRUE + ) +} From fd8e933b553406a1e74e938469b2aa2e4e71f491 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Mon, 23 Feb 2026 16:19:58 +0100 Subject: [PATCH 03/27] another way to express "weight percent" --- R/archchem_colname_parser.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 8f82414..fcf5bd9 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -94,7 +94,7 @@ colnames_to_constructors <- function( unit_from_col <- dplyr::case_match( unit_from_col, c("at%", "atP") ~ "atP", - c("wt%", "wtP") ~ "wtP", + c("wt%", "wtP", "w/w%") ~ "wtP", c("cps") ~ "count/s", .default = unit_from_col ) From 9e993708d629da649bc75eebf64f82896a71b868 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 04:47:22 +0100 Subject: [PATCH 04/27] assigning the unit percent to percent errors --- R/archchem_basic.R | 2 +- R/archchem_colname_parser.R | 32 +++++++++++++++++++++++++------- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index bd56f97..81dd773 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -190,7 +190,7 @@ remove_unit_substrings <- function(x, ...) { x, remove_unit_substring, tidyselect::where(function(y) { - class(y) == "units" + class(y) == "units" && !is_archchem_class(y, "archchem_error") }) ) } diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index fcf5bd9..4442d4b 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -41,7 +41,17 @@ colnames_to_constructors <- function( break } # error columns - if (is_err(colname)) { + if (is_err_percent(colname)) { # check for percent err must be first + return( + function(x) { + x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) + x <- as_numeric_info(x, colname) + x <- units::set_units(x, value = "%", mode = "standard") + x <- add_archchem_class(x, c("archchem_error")) + } + ) + } + if (is_err_abs(colname)) { return( function(x) { x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) @@ -163,8 +173,11 @@ apply_bdl_strategy <- function(x, colname, bdl, bdl_strategy) { #### regex validators #### -is_err <- function(colname) { - grepl(err(), colname, perl = TRUE) +is_err_percent <- function(colname) { + grepl(err_percent(), colname, perl = TRUE) +} +is_err_abs <- function(colname) { + grepl(err_abs(), colname, perl = TRUE) } is_isotope_ratio <- function(colname) { grepl(isotope_ratio(), colname, perl = TRUE) @@ -226,17 +239,22 @@ isotope_delta_epsilon <- function() { } # error states -err <- function() { +err_percent <- function() { + paste0(c( + err_2sd_percent(), err_sd_percent() + ), collapse = "|") +} +err_2sd_percent <- function() "\\_err2SD%" +err_sd_percent <- function() "\\_errSD%" + +err_abs <- function() { paste0(c( err_2sd(), err_sd(), - err_2sd_percent(), err_sd_percent(), err_2se(), err_se() ), collapse = "|") } err_2sd <- function() "\\_err2SD" err_sd <- function() "\\_errSD" -err_2sd_percent <- function() "\\_err2SD%" -err_sd_percent <- function() "\\_errSD%" err_2se <- function() "\\_err2SE" err_se <- function() "\\_errSE" From ba2e709bc4187817c1b2c6f97c9cae9ca14b79cc Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 04:48:32 +0100 Subject: [PATCH 05/27] defining at% and wt% as a derivative of % does not work, unfortunately, as this overwrites % itself; we have to create our own unit for it --- R/zzz.R | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 47157d2..3dd18df 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -12,20 +12,24 @@ NULL # registers the dimensionless units at% (atom percent) and wt% (weight percent) # when the package is loaded .onLoad <- function(libname, pkgname) { - try( - units::install_unit( - symbol = "atP", # at% is not allowed https://github.com/r-quantities/units/issues/289 - def = "percent", - name = "atom percent" - ), - silent = TRUE + safe_install <- function(...) { + tryCatch( + units::install_unit(...), + error = function(e) NULL + ) + } + # dummy base units (purely semantic) + safe_install("atomic_basis") + safe_install("mass_basis") + # percent-like units + safe_install( + symbol = "atP", + def = "0.01 atomic_basis", + name = "atom percent" ) - try( - units::install_unit( - symbol = "wtP", - def = "percent", - name = "weight percent" - ), - silent = TRUE + safe_install( + symbol = "wtP", + def = "0.01 mass_basis", + name = "weight percent" ) } From 0b61cd93c272297ee718d0972c25edc803c7bc01 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 06:11:56 +0100 Subject: [PATCH 06/27] update of basic archchem golden test --- tests/testthat/_snaps/archchem_basic.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/testthat/_snaps/archchem_basic.md b/tests/testthat/_snaps/archchem_basic.md index 3b8c409..c6113ff 100644 --- a/tests/testthat/_snaps/archchem_basic.md +++ b/tests/testthat/_snaps/archchem_basic.md @@ -3,10 +3,8 @@ Code as.data.frame(test_input) Output - ID 206Pb/204Pb Al2O3_cps SiO2+Al2O3_ppt 204Pb_ppm other other2 K2O_wt% - 1 troet 0.5 3 [count/s] 20 [ppt] 7 [ppm] troet 27 23 [%] - d18O SiO2/FeO Mn_at% Zn_ppm SiO2/(FeO+MnO) Sb/As Sn_µg/g - 1 -5.32 0.5 2.45 [%] 240 [ppm] 0.32 5.69 56 [µg/g] - K2O+MgO+Na2O_wt% - 1 54 [%] + ID 206Pb/204Pb Al2O3 SiO2+Al2O3 204Pb other other2 K2O d18O + 1 troet 0.5 3 [count/s] 20 [ppt] 7 [ppm] troet 27 23 [wtP] -5.32 + SiO2/FeO Mn Zn SiO2/(FeO+MnO) Sb/As Sn K2O+MgO+Na2O + 1 0.5 2.45 [atP] 240 [ppm] 0.32 5.69 56 [µg/g] 54 [wtP] From ab5e0c1bfd7a4661acf5059e340cbfb0e5f49dbe Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 07:52:26 +0100 Subject: [PATCH 07/27] working through the archchem example code and addressing various issues that emerged with the introduction of at% and wt% --- DESCRIPTION | 1 + R/archchem_basic.R | 59 +++++++++++++++++----------------- R/archchem_unit_manipulation.R | 2 +- man/archchem.Rd | 15 ++++----- 4 files changed, 39 insertions(+), 38 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index a696d37..97bf00d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -53,6 +53,7 @@ Imports: tibble, tidyselect, units, + vctrs, robCompositions, ggplot2, ks, diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 81dd773..595c4c2 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -93,11 +93,11 @@ #' conc <- get_concentration_columns(arch) # see also other get_..._columns functions #' #' # unit-aware arithmetics on archchem columns thanks to the units package -#' conc$Sb_ppm + conc$Ag_ppb # works -#' \dontrun{conc$Sb_ppm + conc$`Sn_µg/ml`} # fails with: cannot convert µg/ml into ppm +#' conc$Sb + conc$Ag # works +#' \dontrun{conc$Sb + conc$Sn} # fails with: cannot convert µg/ml into ppm #' #' # converting units -#' conc$Sb_ppb <- units::set_units(arch$Sb_ppm, "ppb") %>% +#' conc$Sb <- units::set_units(arch$Sb, "ppb") %>% #' magrittr::set_attr("archchem_class", "archchem_concentration") #' #' # removing all units from archchem tables @@ -106,14 +106,13 @@ #' # applying tidyverse data manipulation on archchem tables #' arch %>% #' dplyr::group_by(Site) %>% -#' dplyr::summarise(mean_Na2O = mean(`Na2O_wt%`)) +#' dplyr::summarise(mean_Na2O = mean(Na2O)) #' conc_subset <- conc %>% -#' dplyr::select(-`Sn_µg/ml`, -`Sb_ppm`) %>% -#' dplyr::filter(`Na2O_wt%` > units::set_units(1, "%")) +#' dplyr::select(-Sn, -Sb) %>% +#' dplyr::filter(Na2O > units::set_units(4, "wtP")) #' #' # unify all concentration units -#' unify_concentration_unit(conc_subset, "ppm") -#' # note that the column names are inaccurate now +#' unify_concentration_unit(conc_subset, "ppb") #' #' @export as_archchem <- function( @@ -142,17 +141,17 @@ as_archchem <- function( } context <- append(context, id_column) # add ID column to context for the following operation - df <- df %>% + df1 <- df %>% dplyr::mutate(ID = .data[[id_column]]) %>% dplyr::relocate("ID", .before = 1) # handle ID duplicates - if (length(unique(df$ID)) != nrow(df)) { + if (length(unique(df1$ID)) != nrow(df1)) { warning( "Detected multiple data rows with the same ID. They will be renamed ", "consecutively using the following convention: _1, _2, ... _n" ) } - df <- df %>% + df2 <- df1 %>% dplyr::group_by(.data[["ID"]]) %>% dplyr::mutate( ID = dplyr::case_when( @@ -163,17 +162,18 @@ as_archchem <- function( dplyr::ungroup() # determine and apply column types constructors <- colnames_to_constructors( - df, context, bdl, bdl_strategy, guess_context_type, na, drop_columns + df2, context, bdl, bdl_strategy, guess_context_type, na, drop_columns ) - df <- purrr::map2(df, constructors, function(col, f) f(col)) %>% + col_list <- purrr::map2(df2, constructors, function(col, f) f(col)) %>% purrr::discard(is.null) - # turn into tibble-derived object - df <- tibble::new_tibble(df, nrow = nrow(df), class = "archchem") + df3 <- as.data.frame(col_list, check.names = FALSE) # remove unit names from columns if they got a unit - df <- remove_unit_substrings(df) + df4 <- remove_unit_substrings(df3) + # turn into tibble-derived object + df5 <- tibble::new_tibble(df4, nrow = nrow(df4), class = "archchem") # post-reading validation if (validate) { - validation_output <- validate(df, quiet = FALSE) + validation_output <- validate(df5, quiet = FALSE) if (nrow(validation_output) > 0) { warning( "See the full list of validation output with: ", @@ -181,7 +181,7 @@ as_archchem <- function( ) } } - return(df) + return(df5) } # helper function to rename column names @@ -376,20 +376,20 @@ print.archchem <- function(x, ...) { # carry over archchem_class column attribute preserve_archchem_attrs <- function(modified, original) { - purrr::map2(modified, original, function(new_col, old_col) { - arch_attr <- attr(old_col, "archchem_class") - if (!is.null(arch_attr)) attr(new_col, "archchem_class") <- arch_attr - new_col - }) %>% - magrittr::set_names(names(modified)) %>% - tibble::new_tibble(nrow = nrow(modified), class = class(original)) + for (nm in intersect(names(modified), names(original))) { + arch_attr <- attr(original[[nm]], "archchem_class") + if (!is.null(arch_attr)) { + attr(modified[[nm]], "archchem_class") <- arch_attr + } + } + tibble::new_tibble(modified, class = class(original)) } # row-slice method #' @exportS3Method dplyr::dplyr_row_slice dplyr_row_slice.archchem <- function(data, i, ...) { - sliced <- purrr::map(data, function(x) x[i]) - sliced_tbl <- tibble::new_tibble(sliced, nrow = length(i), class = class(data)) + sliced <- purrr::map(data, vctrs::vec_slice, i = i) + sliced_tbl <- tibble::new_tibble(sliced, class = class(data)) preserve_archchem_attrs(sliced_tbl, data) } @@ -397,12 +397,13 @@ dplyr_row_slice.archchem <- function(data, i, ...) { #' @exportS3Method dplyr::dplyr_col_modify dplyr_col_modify.archchem <- function(data, cols) { modified_list <- utils::modifyList(as.list(data), cols) - modified_tbl <- tibble::new_tibble(modified_list, nrow = nrow(data), class = class(data)) + modified_tbl <- tibble::new_tibble(modified_list, class = class(data)) preserve_archchem_attrs(modified_tbl, data) } + # final reconstruction #' @exportS3Method dplyr::dplyr_reconstruct dplyr_reconstruct.archchem <- function(data, template) { - tibble::new_tibble(data, nrow = nrow(data), class = class(template)) + preserve_archchem_attrs(data, template) } diff --git a/R/archchem_unit_manipulation.R b/R/archchem_unit_manipulation.R index ffecdd6..1871e88 100644 --- a/R/archchem_unit_manipulation.R +++ b/R/archchem_unit_manipulation.R @@ -30,7 +30,7 @@ unify_concentration_unit <- function(x, unit, ...) { dplyr::across( tidyselect::where(function(y) { class(y) == "units" && - is_archchem_class(y, "archchem_concentration") + units::ud_are_convertible(units::deparse_unit(y), "%") }), function(z) units::set_units(z, unit, mode = "standard") ) diff --git a/man/archchem.Rd b/man/archchem.Rd index 255382a..88304ae 100644 --- a/man/archchem.Rd +++ b/man/archchem.Rd @@ -189,11 +189,11 @@ validate(arch) conc <- get_concentration_columns(arch) # see also other get_..._columns functions # unit-aware arithmetics on archchem columns thanks to the units package -conc$Sb_ppm + conc$Ag_ppb # works -\dontrun{conc$Sb_ppm + conc$`Sn_µg/ml`} # fails with: cannot convert µg/ml into ppm +conc$Sb + conc$Ag # works +\dontrun{conc$Sb + conc$Sn} # fails with: cannot convert µg/ml into ppm # converting units -conc$Sb_ppb <- units::set_units(arch$Sb_ppm, "ppb") \%>\% +conc$Sb <- units::set_units(arch$Sb, "ppb") \%>\% magrittr::set_attr("archchem_class", "archchem_concentration") # removing all units from archchem tables @@ -202,13 +202,12 @@ remove_units(arch) # applying tidyverse data manipulation on archchem tables arch \%>\% dplyr::group_by(Site) \%>\% - dplyr::summarise(mean_Na2O = mean(`Na2O_wt\%`)) + dplyr::summarise(mean_Na2O = mean(Na2O)) conc_subset <- conc \%>\% - dplyr::select(-`Sn_µg/ml`, -`Sb_ppm`) \%>\% - dplyr::filter(`Na2O_wt\%` > units::set_units(1, "\%")) + dplyr::select(-Sn, -Sb) \%>\% + dplyr::filter(Na2O > units::set_units(4, "wtP")) # unify all concentration units -unify_concentration_unit(conc_subset, "ppm") -# note that the column names are inaccurate now +unify_concentration_unit(conc_subset, "ppb") } From 00d99b9ca8b08654e4f38c8c50bec8668d8f9a9b Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 08:14:39 +0100 Subject: [PATCH 08/27] satisfied lintr --- R/zzz.R | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 3dd18df..aed5a1c 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,8 +1,18 @@ # defining global variables -# ugly solution to avoid magrittr NOTE -# see http://stackoverflow.com/questions/9439256/ -# how-can-i-handle-r-cmd-check-no-visible-binding-for-global-variable-notes-when -globalVariables(".") +utils::globalVariables( + c( + # ugly solution to avoid magrittr NOTE + # see http://stackoverflow.com/questions/9439256/ + # how-can-i-handle-r-cmd-check-no-visible-binding-for-global-variable-notes-when + ".", + # register data objects to make lintr aware of them + "isotopes_data", + "elements_data", + "oxides_data", + "special_oxide_states", + "conversion_oxides" + ) +) #' @importFrom magrittr "%>%" #' @importFrom rlang .data := From 6b41372420b6eba2abbdef4437247b76b169ee36 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 08:16:49 +0100 Subject: [PATCH 09/27] added missing test dependency --- tests/testthat/setup.R | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index 6e5e215..10a6379 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -3,6 +3,7 @@ # Additional packages to load for testing or setting up the test environment library(vdiffr) +library(ggplot2) # reference data sets From 3e7ab7f10c264d3ec23a10a8083ee0812ae91acc Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 11:26:05 +0100 Subject: [PATCH 10/27] split colnames_to_constructors into two steps: 1. creating a table with information on each column, 2. creating the respective constructor functions --- R/archchem_colname_parser.R | 184 ++++++++++++++++++++++++------------ 1 file changed, 122 insertions(+), 62 deletions(-) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 4442d4b..bb9b93b 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -12,7 +12,8 @@ colnames_to_constructors <- function( guess_context_type, na, drop_columns ) { - purrr::imap( + # determine column properties + column_table <- purrr::imap_dfr( colnames(x), function(colname, idx) { # use while for hacky switch statement @@ -20,102 +21,126 @@ colnames_to_constructors <- function( # ID column if (colname == "ID") { return( - function(x) { - x <- add_archchem_class(x, "archchem_id") - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = FALSE, + type = "as input", + unit = "none", + class = list("archchem_id"), + ) ) break } # contextual columns if (idx %in% context || colname %in% context) { return( - function(x) { - if (guess_context_type && is.character(x)) { - x <- readr::parse_guess(x, na = na) - } - x <- add_archchem_class(x, "archchem_context") - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = FALSE, + type = "guess", + unit = "none", + class = list("archchem_context") + ) ) break } # error columns if (is_err_percent(colname)) { # check for percent err must be first return( - function(x) { - x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) - x <- as_numeric_info(x, colname) - x <- units::set_units(x, value = "%", mode = "standard") - x <- add_archchem_class(x, c("archchem_error")) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = TRUE, + type = "numeric", + unit = "%", + class = list("archchem_error"), + ) ) + break } if (is_err_abs(colname)) { return( - function(x) { - x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) - x <- as_numeric_info(x, colname) - x <- add_archchem_class(x, c("archchem_error")) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = TRUE, + type = "numeric", + unit = "from main field", + class = list("archchem_error") + ) ) + break } # ratios if (is_isotope_ratio(colname)) { return( - function(x) { - x <- as_numeric_info(x, colname) - x <- add_archchem_class(x, c("archchem_isotope", "archchem_ratio")) - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = FALSE, + type = "numeric", + unit = "none", + class = list(c("archchem_isotope", "archchem_ratio")), + ) ) break } if (is_isotope_delta_epsilon(colname)) { - # delta_epsilon <- extract_delta_epsilon_string(colname) return( - function(x) { - # if (delta_epsilon == "d") { - # x / 10 # per mille -> percent - # } else if (delta_epsilon == "e") { - # x / 100 # parts per 10000 -> percent - # } - x <- as_numeric_info(x, colname) - x <- add_archchem_class(x, c("archchem_isotope", "archchem_ratio")) - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = FALSE, + type = "numeric", + unit = "none", + class = list(c("archchem_isotope", "archchem_ratio")) + ) ) break } if (is_elemental_ratio(colname)) { return( - function(x) { - x <- as_numeric_info(x, colname) - x <- add_archchem_class(x, c("archchem_element", "archchem_ratio")) - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = FALSE, + type = "numeric", + unit = "none", + class = list(c("archchem_element", "archchem_ratio")) + ) ) break } # concentrations if (is_concentration(colname)) { - unit_from_col <- extract_unit_string(colname) + unit_from_name <- extract_unit_string(colname) # handle special cases - unit_from_col <- dplyr::case_match( - unit_from_col, + unit_from_name <- dplyr::case_match( + unit_from_name, c("at%", "atP") ~ "atP", c("wt%", "wtP", "w/w%") ~ "wtP", c("cps") ~ "count/s", - .default = unit_from_col + .default = unit_from_name ) return( - function(x) { - x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) - x <- as_numeric_info(x, colname) - x <- units::set_units(x, value = unit_from_col, mode = "standard") - x <- add_archchem_class(x, c("archchem_concentration")) - return(x) - } + tibble::tibble( + drop = FALSE, + idx, + colname, + bdl = TRUE, + type = "numeric", + unit = unit_from_name, + class = list("archchem_concentration"), + ) ) break } @@ -127,15 +152,50 @@ colnames_to_constructors <- function( "Either analytical columns do not conform to ASTR conventions or ", "contextual columns are not specified as such." ) - if (drop_columns) { - warning(m) - return(function(x) { + warning(m) + return( + tibble::tibble( + drop = TRUE, + idx, + colname + ) + ) + } + } + ) + # build constructors + purrr::pmap( + column_table, function(drop, idx, colname, bdl, type, unit, class) { + # delete fields that should be dropped (NULL-constructor) + if (drop) { + return( + function(x) { NULL - }) - } else { - stop(m) - } + } + ) } + # assemble normal constructor function based on column properties + return( + function(x) { + # bdl + if (bdl) { + x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) + } + # type + if (type == "numeric") { + x <- as_numeric_info(x, colname) + } else if (type == "guess" && guess_context_type && is.character(x)) { + x <- readr::parse_guess(x, na = na) + } + # unit + if (unit != "none" && unit != "from main field") { + x <- units::set_units(x, value = unit, mode = "standard") + } + # class + x <- add_archchem_class(x, class) + return(x) + } + ) } ) } From a62b65cbc3a6faaf7b2a6b637ea32eee5f29de68 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 12:26:32 +0100 Subject: [PATCH 11/27] split colnames_to_constructors into two functions --- R/archchem_basic.R | 5 ++--- R/archchem_colname_parser.R | 43 ++++++++++++++++++++----------------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 595c4c2..3530719 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -161,9 +161,8 @@ as_archchem <- function( ) %>% dplyr::ungroup() # determine and apply column types - constructors <- colnames_to_constructors( - df2, context, bdl, bdl_strategy, guess_context_type, na, drop_columns - ) + column_table <- parse_colnames(df2, context) + constructors <- build_constructors(column_table, bdl, bdl_strategy, guess_context_type, na, drop_columns) col_list <- purrr::map2(df2, constructors, function(col, f) f(col)) %>% purrr::discard(is.null) df3 <- as.data.frame(col_list, check.names = FALSE) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index bb9b93b..8bf5006 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -5,15 +5,10 @@ # SI-unit column types are defined with the units package # https://cran.r-project.org/web/packages/units/index.html # (so the udunits library) -colnames_to_constructors <- function( - x, - context, - bdl, bdl_strategy, - guess_context_type, na, - drop_columns -) { - # determine column properties - column_table <- purrr::imap_dfr( + +# 1. evaluate column names +parse_colnames <- function(x, context) { + purrr::imap_dfr( colnames(x), function(colname, idx) { # use while for hacky switch statement @@ -25,7 +20,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = FALSE, + consider_bdl = FALSE, type = "as input", unit = "none", class = list("archchem_id"), @@ -40,7 +35,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = FALSE, + consider_bdl = FALSE, type = "guess", unit = "none", class = list("archchem_context") @@ -55,7 +50,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = TRUE, + consider_bdl = TRUE, type = "numeric", unit = "%", class = list("archchem_error"), @@ -69,7 +64,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = TRUE, + consider_bdl = TRUE, type = "numeric", unit = "from main field", class = list("archchem_error") @@ -84,7 +79,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = FALSE, + consider_bdl = FALSE, type = "numeric", unit = "none", class = list(c("archchem_isotope", "archchem_ratio")), @@ -98,7 +93,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = FALSE, + consider_bdl = FALSE, type = "numeric", unit = "none", class = list(c("archchem_isotope", "archchem_ratio")) @@ -112,7 +107,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = FALSE, + consider_bdl = FALSE, type = "numeric", unit = "none", class = list(c("archchem_element", "archchem_ratio")) @@ -136,7 +131,7 @@ colnames_to_constructors <- function( drop = FALSE, idx, colname, - bdl = TRUE, + consider_bdl = TRUE, type = "numeric", unit = unit_from_name, class = list("archchem_concentration"), @@ -163,9 +158,17 @@ colnames_to_constructors <- function( } } ) - # build constructors +} + +# 2. build constructor functions +build_constructors <- function( + column_table, + bdl, bdl_strategy, + guess_context_type, na, + drop_columns +) { purrr::pmap( - column_table, function(drop, idx, colname, bdl, type, unit, class) { + column_table, function(drop, idx, colname, consider_bdl, type, unit, class) { # delete fields that should be dropped (NULL-constructor) if (drop) { return( @@ -178,7 +181,7 @@ colnames_to_constructors <- function( return( function(x) { # bdl - if (bdl) { + if (consider_bdl) { x <- apply_bdl_strategy(x, colname, bdl, bdl_strategy) } # type From 32a900add4e0437774ce8fe7b99a88b6efe57be4 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 13:19:59 +0100 Subject: [PATCH 12/27] introduced a mechanism to assign errors columns the units of their source column --- R/archchem_basic.R | 4 ++-- R/archchem_colname_parser.R | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 3530719..7b23900 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -187,14 +187,14 @@ as_archchem <- function( remove_unit_substrings <- function(x, ...) { dplyr::rename_with( x, - remove_unit_substring, + remove_suffix, tidyselect::where(function(y) { class(y) == "units" && !is_archchem_class(y, "archchem_error") }) ) } -remove_unit_substring <- function(colname) { +remove_suffix <- function(colname) { sub("_.*$", "", colname, perl = TRUE) } diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 8bf5006..6d729d0 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -8,7 +8,7 @@ # 1. evaluate column names parse_colnames <- function(x, context) { - purrr::imap_dfr( + column_table <- purrr::imap_dfr( colnames(x), function(colname, idx) { # use while for hacky switch statement @@ -158,6 +158,23 @@ parse_colnames <- function(x, context) { } } ) + # get units for columns that depend on a main column, + # so overwrite unit "from main field" with the unit of + # said main field + is_dependent <- column_table$unit == "from main field" + dependent_cols <- column_table[is_dependent, ] + dependent_bases <- remove_suffix(dependent_cols$colname) + independent_cols <- column_table[!is_dependent, ] + independent_bases <- remove_suffix(independent_cols$colname) + for (i in seq_along(dependent_bases)) { + j <- which(dependent_bases[i] == independent_bases) + if (length(j) != 1) { + stop("Column ", dependent_cols$colname[i], " can not be uniquely matched to a non-error column.") + } else { + column_table[is_dependent, ]$unit[i] <- independent_cols$unit[j] + } + } + return(column_table) } # 2. build constructor functions From e84db6e1bbbd90475256c890b82079dd0f71e75d Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 13:54:30 +0100 Subject: [PATCH 13/27] added an optional feature to remove_units that recovers the unit names in the column names --- R/archchem_unit_manipulation.R | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/R/archchem_unit_manipulation.R b/R/archchem_unit_manipulation.R index 1871e88..d81034f 100644 --- a/R/archchem_unit_manipulation.R +++ b/R/archchem_unit_manipulation.R @@ -4,7 +4,36 @@ remove_units <- function(x, ...) { UseMethod("remove_units") } #' @export -remove_units.archchem <- function(x, ...) { +remove_units.archchem <- function(x, recover_unit_names = FALSE, ...) { + # rename columns: recover units in column names + if (recover_unit_names) { + x <- dplyr::rename_with( + x, + function(column_names) { + unit_names <- purrr::map_chr( + column_names, + function(column_name) { + # render individual units + unit <- units(x[[column_name]]) + rendered_unit <- as.character(unit, neg_power = FALSE, prod_sep = "*") + # handle special cases + dplyr::case_match( + rendered_unit, + "atP" ~ "at%", + "wtP" ~ "wt%", + "count/s" ~ "cps", + .default = rendered_unit + ) + } + ) + paste0(column_names, "_", unit_names) + }, + tidyselect::where(function(y) { + class(y) == "units" && !is_archchem_class(y, "archchem_error") + }) + ) + } + # drop units dplyr::mutate( x, dplyr::across( From 2cbbc6866fe55d089cca779ff473a4a23ad787a1 Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Tue, 24 Feb 2026 14:23:40 +0100 Subject: [PATCH 14/27] brought back the established drop_columns behaviour --- R/archchem_basic.R | 4 ++-- R/archchem_colname_parser.R | 22 +++++++++++++--------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 7b23900..4d584c8 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -161,8 +161,8 @@ as_archchem <- function( ) %>% dplyr::ungroup() # determine and apply column types - column_table <- parse_colnames(df2, context) - constructors <- build_constructors(column_table, bdl, bdl_strategy, guess_context_type, na, drop_columns) + column_table <- parse_colnames(df2, context, drop_columns) + constructors <- build_constructors(column_table, bdl, bdl_strategy, guess_context_type, na) col_list <- purrr::map2(df2, constructors, function(col, f) f(col)) %>% purrr::discard(is.null) df3 <- as.data.frame(col_list, check.names = FALSE) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 6d729d0..720b0ce 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -7,7 +7,7 @@ # (so the udunits library) # 1. evaluate column names -parse_colnames <- function(x, context) { +parse_colnames <- function(x, context, drop_columns) { column_table <- purrr::imap_dfr( colnames(x), function(colname, idx) { @@ -148,13 +148,18 @@ parse_colnames <- function(x, context) { "contextual columns are not specified as such." ) warning(m) - return( - tibble::tibble( - drop = TRUE, - idx, - colname + if (drop_columns) { + return( + tibble::tibble( + drop = TRUE, + idx, + colname, + unit = "none" + ) ) - ) + } else { + stop(m) + } } } ) @@ -181,8 +186,7 @@ parse_colnames <- function(x, context) { build_constructors <- function( column_table, bdl, bdl_strategy, - guess_context_type, na, - drop_columns + guess_context_type, na ) { purrr::pmap( column_table, function(drop, idx, colname, consider_bdl, type, unit, class) { From 6f9aa67cec0e9ebdc0d5bd93fa0907efcd0003f4 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 1 Mar 2026 13:30:34 -0500 Subject: [PATCH 15/27] fix typos [skip ci] --- R/archchem_colname_parser.R | 4 ++-- data-raw/.DS_Store | Bin 6148 -> 0 bytes 2 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 data-raw/.DS_Store diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 720b0ce..2f4e35e 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -174,7 +174,7 @@ parse_colnames <- function(x, context, drop_columns) { for (i in seq_along(dependent_bases)) { j <- which(dependent_bases[i] == independent_bases) if (length(j) != 1) { - stop("Column ", dependent_cols$colname[i], " can not be uniquely matched to a non-error column.") + stop("Column ", dependent_cols$colname[i], " cannot be uniquely matched to a non-error column.") } else { column_table[is_dependent, ]$unit[i] <- independent_cols$unit[j] } @@ -313,7 +313,7 @@ isotope_ratio <- function() { paste0("(", isotopes_list(), ")/(", isotopes_list(), ")") } -# define regex pattern for delta and espilon notation: +# define regex pattern for delta and epsilon notation: # letter d OR e followed by any isotope isotope_delta_epsilon <- function() { paste0( diff --git a/data-raw/.DS_Store b/data-raw/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Sat, 14 Mar 2026 20:29:05 -0400 Subject: [PATCH 16/27] replace functions deprecated in dplyr 1.2.0 --- R/archchem_basic.R | 10 +++++----- R/archchem_colname_parser.R | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 4d584c8..8825c4a 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -154,11 +154,11 @@ as_archchem <- function( df2 <- df1 %>% dplyr::group_by(.data[["ID"]]) %>% dplyr::mutate( - ID = dplyr::case_when( - dplyr::n() > 1 ~ paste0(.data[["ID"]], "_", as.character(dplyr::row_number())), - .default = .data[["ID"]] - ) - ) %>% + ID = if (dplyr::n() > 1) { + paste0(.data[["ID"]], "_", as.character(dplyr::row_number())) + } else { + .data[["ID"]] + }) %>% dplyr::ungroup() # determine and apply column types column_table <- parse_colnames(df2, context, drop_columns) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 2f4e35e..4a488b5 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -119,12 +119,12 @@ parse_colnames <- function(x, context, drop_columns) { if (is_concentration(colname)) { unit_from_name <- extract_unit_string(colname) # handle special cases - unit_from_name <- dplyr::case_match( + unit_from_name <- dplyr::recode_values( unit_from_name, c("at%", "atP") ~ "atP", c("wt%", "wtP", "w/w%") ~ "wtP", c("cps") ~ "count/s", - .default = unit_from_name + default = unit_from_name ) return( tibble::tibble( From 20509ecd76027d82683c4c8ae1af827fd0d8b347 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sat, 14 Mar 2026 20:29:42 -0400 Subject: [PATCH 17/27] add tests for class archchem --- data/archchem_example_input.rda | Bin 4306 -> 4301 bytes tests/testthat/_snaps/archchem_basic.md | 171 ++++++++++++++++++ tests/testthat/test_archchem_basic.R | 41 +++++ .../test_archchem_column_selectionc.R | 32 ++++ 4 files changed, 244 insertions(+) create mode 100644 tests/testthat/test_archchem_column_selectionc.R diff --git a/data/archchem_example_input.rda b/data/archchem_example_input.rda index 5a16b8e863cf125f17a270fe526e454da7fd379c..e2d92a198905b725b02c109031c3ceee63cadf22 100644 GIT binary patch literal 4301 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 [count/s] [ppt] [ppm] [wtP] + 1 troet 0.5 3 20 7 troet 27 23 -5.32 + # i 7 more variables: `SiO2/FeO` , Mn [atP], Zn [ppm], + # `SiO2/(FeO+MnO)` , `Sb/As` , Sn [µg/g], `K2O+MgO+Na2O` [wtP] + diff --git a/tests/testthat/test_archchem_basic.R b/tests/testthat/test_archchem_basic.R index d4a8491..d472f60 100644 --- a/tests/testthat/test_archchem_basic.R +++ b/tests/testthat/test_archchem_basic.R @@ -6,9 +6,50 @@ test_input <- read_archchem( context = c("other", "other2") ) +test_input2 <- suppressWarnings( + as_archchem( + archchem_example_input, + id_column = "Sample", + context = c("Lab no.", "Site" ,"latitude" ,"longitude", "Type", "method_comp") + ) +) + +test_input3 <- readr::read_csv(system.file("extdata", "input_format.csv", package = "ASTR")) +test_input3[2,] <- test_input3[1,] +test_input3 <- suppressWarnings( + as_archchem(test_input3, id_column = "other2", context = "other") + ) + test_that("reading of a basic example table works as expected", { expect_snapshot({ # turn to data.frame to render the entire table as.data.frame(test_input) }) + expect_snapshot({ + # turn to data.frame to render the entire table + as.data.frame(test_input2) + }) + expect_snapshot({ + print(test_input) + }) + # checks that automatic renaming of ID values works + expect_all_equal(test_input3$ID[2], "27_2") +}) + +# parsing throws expected errors + +test_input3 <- readr::read_csv(system.file("extdata", "input_format.csv", package = "ASTR")) +test_input3[2,] <- test_input3[1,] + +test_that("archem functions result in expected errors and warnings", { + expect_error( + suppressWarnings(as_archchem(test_input3, id_column = "other")), + "Column name .* could not be parsed") + expect_warning( + as_archchem(test_input3, id_column = "other", context = "other2"), + "Detected multiple data rows with the same ID") + expect_error( + validate(2), + "x is not an object of class archchem" + ) }) diff --git a/tests/testthat/test_archchem_column_selectionc.R b/tests/testthat/test_archchem_column_selectionc.R new file mode 100644 index 0000000..f21cd6f --- /dev/null +++ b/tests/testthat/test_archchem_column_selectionc.R @@ -0,0 +1,32 @@ +# test columns selection function not checked elsewhere + +test_input <- suppressWarnings( + as_archchem( + archchem_example_input, + id_column = "Sample", + context = c("Lab no.", "Site" ,"latitude" ,"longitude", "Type", "method_comp") + ) +) + +test_that("column selection based on archchem column types", { + expect_all_true( + colnames(get_isotope_columns(test_input)) == + c("ID", "143Nd/144Nd", "d65Cu", "206Pb/204Pb", "207Pb/204Pb", "208Pb/204Pb", + "207Pb/206Pb", "208Pb/206Pb") + ) + expect_all_true( + colnames(get_element_columns(test_input)) == + c("ID", "FeOtot/SiO2", "(Na2O+K2O)/SiO2") + ) + expect_all_true( + colnames(get_ratio_columns(test_input)) == + c("ID", "143Nd/144Nd", "d65Cu", "FeOtot/SiO2", "(Na2O+K2O)/SiO2", "206Pb/204Pb", "207Pb/204Pb", + "208Pb/204Pb", "207Pb/206Pb", "208Pb/206Pb") + ) + expect_all_true( + colnames(get_concentration_columns(test_input)) == + c("ID", "Na2O", "BaO", "Pb", "MgO", "Al2O3", "SiO2", "P2O5", "S", "CaO", "TiO2", + "MnO", "FeOtot", "ZnO", "K2O", "Cu", "As", "LOI", "Ag", "Sn", "Sb", "Te", "Bi", + "U", "V", "Cr", "Co", "Ni", "Sr", "Se") + ) +}) From 3b7d2ce0d16ee55679933715837516c3b8aa51e7 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sat, 14 Mar 2026 20:36:09 -0400 Subject: [PATCH 18/27] fix linting --- R/archchem_basic.R | 5 +++-- tests/testthat/test_archchem_basic.R | 14 +++++++------ .../test_archchem_column_selectionc.R | 20 ++++++++++++------- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/R/archchem_basic.R b/R/archchem_basic.R index 8825c4a..22d4582 100644 --- a/R/archchem_basic.R +++ b/R/archchem_basic.R @@ -156,9 +156,10 @@ as_archchem <- function( dplyr::mutate( ID = if (dplyr::n() > 1) { paste0(.data[["ID"]], "_", as.character(dplyr::row_number())) - } else { + } else { .data[["ID"]] - }) %>% + } + ) %>% dplyr::ungroup() # determine and apply column types column_table <- parse_colnames(df2, context, drop_columns) diff --git a/tests/testthat/test_archchem_basic.R b/tests/testthat/test_archchem_basic.R index d472f60..5d2f6a0 100644 --- a/tests/testthat/test_archchem_basic.R +++ b/tests/testthat/test_archchem_basic.R @@ -10,15 +10,15 @@ test_input2 <- suppressWarnings( as_archchem( archchem_example_input, id_column = "Sample", - context = c("Lab no.", "Site" ,"latitude" ,"longitude", "Type", "method_comp") + context = c("Lab no.", "Site", "latitude", "longitude", "Type", "method_comp") ) ) test_input3 <- readr::read_csv(system.file("extdata", "input_format.csv", package = "ASTR")) -test_input3[2,] <- test_input3[1,] +test_input3[2, ] <- test_input3[1, ] test_input3 <- suppressWarnings( as_archchem(test_input3, id_column = "other2", context = "other") - ) +) test_that("reading of a basic example table works as expected", { expect_snapshot({ @@ -39,15 +39,17 @@ test_that("reading of a basic example table works as expected", { # parsing throws expected errors test_input3 <- readr::read_csv(system.file("extdata", "input_format.csv", package = "ASTR")) -test_input3[2,] <- test_input3[1,] +test_input3[2, ] <- test_input3[1, ] test_that("archem functions result in expected errors and warnings", { expect_error( suppressWarnings(as_archchem(test_input3, id_column = "other")), - "Column name .* could not be parsed") + "Column name .* could not be parsed" + ) expect_warning( as_archchem(test_input3, id_column = "other", context = "other2"), - "Detected multiple data rows with the same ID") + "Detected multiple data rows with the same ID" + ) expect_error( validate(2), "x is not an object of class archchem" diff --git a/tests/testthat/test_archchem_column_selectionc.R b/tests/testthat/test_archchem_column_selectionc.R index f21cd6f..ba9cf7c 100644 --- a/tests/testthat/test_archchem_column_selectionc.R +++ b/tests/testthat/test_archchem_column_selectionc.R @@ -4,15 +4,17 @@ test_input <- suppressWarnings( as_archchem( archchem_example_input, id_column = "Sample", - context = c("Lab no.", "Site" ,"latitude" ,"longitude", "Type", "method_comp") + context = c("Lab no.", "Site", "latitude", "longitude", "Type", "method_comp") ) ) test_that("column selection based on archchem column types", { expect_all_true( colnames(get_isotope_columns(test_input)) == - c("ID", "143Nd/144Nd", "d65Cu", "206Pb/204Pb", "207Pb/204Pb", "208Pb/204Pb", - "207Pb/206Pb", "208Pb/206Pb") + c( + "ID", "143Nd/144Nd", "d65Cu", "206Pb/204Pb", "207Pb/204Pb", "208Pb/204Pb", + "207Pb/206Pb", "208Pb/206Pb" + ) ) expect_all_true( colnames(get_element_columns(test_input)) == @@ -20,13 +22,17 @@ test_that("column selection based on archchem column types", { ) expect_all_true( colnames(get_ratio_columns(test_input)) == - c("ID", "143Nd/144Nd", "d65Cu", "FeOtot/SiO2", "(Na2O+K2O)/SiO2", "206Pb/204Pb", "207Pb/204Pb", - "208Pb/204Pb", "207Pb/206Pb", "208Pb/206Pb") + c( + "ID", "143Nd/144Nd", "d65Cu", "FeOtot/SiO2", "(Na2O+K2O)/SiO2", "206Pb/204Pb", "207Pb/204Pb", + "208Pb/204Pb", "207Pb/206Pb", "208Pb/206Pb" + ) ) expect_all_true( colnames(get_concentration_columns(test_input)) == - c("ID", "Na2O", "BaO", "Pb", "MgO", "Al2O3", "SiO2", "P2O5", "S", "CaO", "TiO2", + c( + "ID", "Na2O", "BaO", "Pb", "MgO", "Al2O3", "SiO2", "P2O5", "S", "CaO", "TiO2", "MnO", "FeOtot", "ZnO", "K2O", "Cu", "As", "LOI", "Ag", "Sn", "Sb", "Te", "Bi", - "U", "V", "Cr", "Co", "Ni", "Sr", "Se") + "U", "V", "Cr", "Co", "Ni", "Sr", "Se" + ) ) }) From b1b55ceb53b5cb85ee5bc511de257699d8367a9b Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 15 Mar 2026 11:50:45 -0400 Subject: [PATCH 19/27] use reference file consistently from one directory, add test for excel format --- data-raw/data_prep.R | 8 -------- data-raw/test_data_input_good.csv | 15 --------------- data/archchem_example_input.rda | Bin 4301 -> 0 bytes inst/extdata/input_format.xlsx | Bin 6068 -> 7126 bytes tests/testthat/setup.R | 1 + tests/testthat/test_archchem_basic.R | 11 +++++++++-- 6 files changed, 10 insertions(+), 25 deletions(-) delete mode 100644 data-raw/test_data_input_good.csv delete mode 100644 data/archchem_example_input.rda diff --git a/data-raw/data_prep.R b/data-raw/data_prep.R index 3d1e287..6b36035 100644 --- a/data-raw/data_prep.R +++ b/data-raw/data_prep.R @@ -72,11 +72,3 @@ usethis::use_data( # ) # # usethis::use_data(isotopes, overwrite = T) - -#### archem data input table #### - -archchem_example_input <- readr::read_csv("data-raw/test_data_input_good.csv") - -usethis::use_data(archchem_example_input, - overwrite = TRUE -) diff --git a/data-raw/test_data_input_good.csv b/data-raw/test_data_input_good.csv deleted file mode 100644 index 5322c4e..0000000 --- a/data-raw/test_data_input_good.csv +++ /dev/null @@ -1,15 +0,0 @@ -Sample,Lab no.,Site,latitude,longitude,Type,method_comp,143Nd/144Nd,d65Cu,d65Cu_err2SD,Na2O_wt%,BaO_wt%,Pb_wt%,MgO_wt%,Al2O3_wt%,SiO2_wt%,SiO2_errSD%,P2O5_wt%,S_at%,CaO_wt%,TiO2_wt%,MnO_wt%,FeOtot_wt%,FeOtot_err2SD,ZnO_%,K2O_wt%,Cu_wt%,As_wt%,LOI_wt%,Ag_ppb,Sn_µg/ml,Sb_ppm,Te_ppm,Bi_ppm,U_ppm,V_ppm,Cr_ppm,Co_ppm,Ni_ppm,Sr_ppm,Se_ppm,FeOtot/SiO2,(Na2O+K2O)/SiO2,206Pb/204Pb,206Pb/204Pb_err2SD,207Pb/204Pb,207Pb/204Pb_err2SD,208Pb/204Pb,208Pb/204Pb_err2SD,207Pb/206Pb,207Pb/206Pb_err2SD,208Pb/206Pb,208Pb/206Pb_err2SD -TR-001,3421/19,Bochum,51.48165,7.21648,1,ICP-MS,0.513014,1.24,0.124,3.1,0.07,6.34,0.77,3.92,31.63,4.3,0.15,2.57,2.11,0.52,0.2,43.83,4.12,5.64,1.34,0.11,0.02,1.89,500,85,370,2,18,3,60,160,60,60,130,<5,1.386,0.14037,18.61147,0.01082,15.65405,0.00915,38.7563,0.0227,0.8411,0.00006,2.08239,0.00017 -TR-002_1,3423/19,Oviedo,43.36029,-5.84476,2,ICP-MS,0.512994,0.88,0.06,2.2,0.07,3.52,0.55,3.46,29.06,2.88,-,NA,2.34,0.49,0.54,51.02,3.89,4.07,1.26,0.22,0.01,-,200,85,210,2,20,2,60,140,70,100,120,<5,1.756,0.11906,18.61617,0.01629,15.65682,0.01341,38.76404,0.0345,0.84103,0.00013,2.08229,0.00033 -TR-002_2,3435/19,Oviedo,43.36029,-5.84476,2,ICP-MS,0.512996,0.85,0.05,5,0.054,3.6,0.33,5.87,30.5,5.71,0.11,3.68,2,0.55,0.1,30.59,2.94,6.87,1.54,0.74,bdl,-,210,98,256,<1.5,47,-,63,99,55,87,121,15,1.003,0.21443,18.61615,0.01698,15.6574,0.01441,38.76411,0.03452,0.84107,0.00014,2.08228,0.00033 -TR-003,3422/19,佛山,23.02677,113.13148,3,ICP-MS,0.513008,2.76,0.276,6.9,0.04,5.07,0.76,4.33,25.73,2.22,0.23,2.76,3.91,0.27,0.7,46.09,3.5,5.87,1.66,0.13,0.01,NA,350,45,400,2,bdl,2,45,30,30,20,250,<5,1.791,0.33269,18.61272,0.00926,15.65677,0.00747,38.76097,0.0182,0.84119,0.0001,2.08251,0.00025 -TR-004,3429/19,Băiuț,43.234895,23.124983,4,ICP-MS,,-8.21,-0.821,3,0.03,9.15,0.52,4.17,43.5,4.65,0.4,0.57,1.79,0.24,1,31.91,2.87,2.26,2.15,0.14,0.03,3.57,400,14,410,2,6,3,55,75,n.a.,10,160,<5,0.734,0.11839,18.63562,0.01189,15.64765,0.00989,38.73583,0.02561,0.83966,0.00009,2.07858,0.00023 -TR-005,3430/19,Şuior,41.445566,24.935661,5,ICP-MS,,0.06,0.004,4.1,0.07,12.61,0.58,3.58,33.83,3.67,0.29,0.61,2.06,0.18,0.49,36.57,5.486,1.66,1.21,0.09,0.11,5.66,250,181,453,6,9,3,54,41,8,47,258,-,1.081,0.15696,18.63952,0.01802,15.65502,0.01513,38.75729,0.04157,0.83989,0.00012,2.07923,0.00032 -TR-006.1,3431/19,Blagodat,41.453133,25.505011,5,ICP-MS,,1.48,0.222,3.4,0.05,12.68,0.64,5.6,33.81,3,0.43,0.77,,NA,1.52,35.52,2.66,2.46,2.26,0.21,0.23,0.12,120,566,701,8,9,bdl,75,106,22,19,122,3,1.051,0.16741,18.64465,0.01468,15.65836,0.0121,38.76615,0.02716,0.83984,0.00009,2.07928,0.00023 -TR-006.2,3432/19,Pezinok,45.92125,15.919219,1,ICP-MS,,-0.42,-0.042,1.8,0.04,5.31,0.59,4.47,26.39,2.87,0.62,3.04,4.28,NA,0.4,41.2,3.89,11.65,1.86,0.43,0.02,0.89,430,203,231,15,3,b.d.l.,84,91,47,34,280,3,1.561,0.13869,18.83274,0.01608,15.82161,0.0135,39.20161,0.03356,0.84852,0.00007,2.10238,0.00017 -TR-006.3,3433/19,Free State Geduld Mines,50.35621322,7.651313679,4,ICP-MS,,0.3,0.02,5.1,0.09,2.36,0.53,3.73,21.18,3.44,0.24,3.93,4.08,0.15,0.46,48.26,2.268,6.8,2.01,0.59,0.08,4.75,300,5594,107,6,5,5,32,63,17,37,310,<5,2.279,0.33569,18.83312,0.01875,15.82042,0.01619,39.20067,0.04173,0.84846,0.00007,2.10235,0.00018 -smn348,3424/19,Aggenys,50.99159628,8.02757263,3,ICP-MS,0.512977,0.24,0.024,4.1,0.34,47.49,0.7,6.27,31.73,6.12,0.28,0.21,7.14,NA,0.24,5.26,0.305,0.03,1.34,0.07,0.06,n.a.,2420,32,3310,9,150,NA,61,45,-,4,287,5,0.166,0.17145,18.80371,0.03129,15.81646,0.026,39.15557,0.06458,0.84955,0.00007,2.10313,0.00026 -smn349,3425/19,Chiprovtsi,50.6890264,6.406379006,3,ICP-MS,0.513004,0.54,0.01,3.4,0.04,2.53,0.46,2.91,16.91,3.73,0.22,3.57,2.5,0.14,0.21,56.36,6.763,4.89,1.34,0.15,0.01,4.22,140,23,95,5,1,3,37,36,69,39,155,<5,3.333,0.28031,18.8076,0.0155,15.82128,0.01309,39.17608,0.03091,0.84963,0.00012,2.10388,0.00022 -smn350,3426/19,Krusov Dol; Krushev Dol,40.695753,24.591976,5,ICP-MS,0.512991,2.04,0.8,4.5,0.07,1.56,0.58,3.6,28.49,4.89,0.19,1.95,1.39,0.17,0.13,47.64,4.235,6.56,0.98,0.16,0.02,6.01,1610,45,78,5,4,3,40,33,43,39,165,<5,1.672,0.19235,18.81356,0.01557,15.82045,0.01305,39.17244,0.03263,0.84931,0.00007,2.10297,0.00022 -smn351,3427/19,Masua,37.726179,24.012951,1,ICP-MS,,-0.24,-0.0504,3.2,0.04,3.64,0.88,5.24,38.04,10.12,0.3,1.35,2.1,0.34,1.32,43.21,2.161,1.52,1.48,0.27,0.05,3.22,100,15,610,2,1,2,80,190,20,50,100,<5,1.136,0.12303,18.81501,0.01535,15.80824,0.0135,39.13346,0.03109,0.84859,0.00007,2.10076,0.00018 -8896,3428/19,Σπαρτη,37.07446,22.43009,1,ICP-MS,,-0.42,-0.042,3.7,0.06,7.51,0.84,3.97,31.67,4.3,0.18,0.83,3.51,0.24,0.34,45.9,2.387,0.85,1.32,0.09,0.14,n.a.,100,146,390,<1.5,<0.3,1,40,100,10,65,120,<5,1.449,0.15851,18.82037,0.01483,15.81119,0.01252,39.13715,0.03182,0.84851,0.00007,2.1003,0.00024 diff --git a/data/archchem_example_input.rda b/data/archchem_example_input.rda deleted file mode 100644 index e2d92a198905b725b02c109031c3ceee63cadf22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4301 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 literal 6068 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 Date: Sun, 15 Mar 2026 18:04:55 -0400 Subject: [PATCH 20/27] clean-up example data --- R/data.R | 10 ---------- man/archchem_example_input.Rd | 16 ---------------- 2 files changed, 26 deletions(-) delete mode 100644 man/archchem_example_input.Rd diff --git a/R/data.R b/R/data.R index ba7d33a..fe773c3 100644 --- a/R/data.R +++ b/R/data.R @@ -57,16 +57,6 @@ #' @name conversion_oxides "conversion_oxides" -#' Example input data for ASTR -#' -#' @docType data -#' @keywords datasets -#' @name archchem_example_input -#' @usage data(archchem_example_input) -#' @format A data frame with 15 observations and 53 variables. -#' -"archchem_example_input" - #' ArgentinaDatabase #' #' Lead isotope data from ore deposits in Argentina prepared for TerraLID database. diff --git a/man/archchem_example_input.Rd b/man/archchem_example_input.Rd deleted file mode 100644 index b86abb1..0000000 --- a/man/archchem_example_input.Rd +++ /dev/null @@ -1,16 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/data.R -\docType{data} -\name{archchem_example_input} -\alias{archchem_example_input} -\title{Example input data for ASTR} -\format{ -A data frame with 15 observations and 53 variables. -} -\usage{ -data(archchem_example_input) -} -\description{ -Example input data for ASTR -} -\keyword{datasets} From 3e033d7e6bb2cf0ec28b5fffaee72a4a41917b02 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 15 Mar 2026 18:16:30 -0400 Subject: [PATCH 21/27] re-organise package dependencies in test --- tests/testthat.R | 3 - tests/testthat/_snaps/archchem_basic.md | 134 +++++++++--------- tests/testthat/setup.R | 5 +- .../test_archchem_column_selectionc.R | 4 +- 4 files changed, 74 insertions(+), 72 deletions(-) diff --git a/tests/testthat.R b/tests/testthat.R index a26f0d9..6be89e9 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -8,8 +8,5 @@ library(testthat) library(ASTR) -library(tibble) -library(ggplot2) -library(vdiffr) test_check("ASTR") diff --git a/tests/testthat/_snaps/archchem_basic.md b/tests/testthat/_snaps/archchem_basic.md index 7b9bc44..de8d737 100644 --- a/tests/testthat/_snaps/archchem_basic.md +++ b/tests/testthat/_snaps/archchem_basic.md @@ -3,10 +3,12 @@ Code as.data.frame(test_input) Output - ID 206Pb/204Pb Al2O3 SiO2+Al2O3 204Pb other other2 K2O d18O - 1 troet 0.5 3 [count/s] 20 [ppt] 7 [ppm] troet 27 23 [wtP] -5.32 - SiO2/FeO Mn Zn SiO2/(FeO+MnO) Sb/As Sn K2O+MgO+Na2O - 1 0.5 2.45 [atP] 240 [ppm] 0.32 5.69 56 [µg/g] 54 [wtP] + ID 206Pb/204Pb Al2O3 SiO2+Al2O3 204Pb other other2 K2O + 1 troet 0.5 3 [count/s] 20 [ng/kg] 7 [mg/kg] troet 27 23 [wtP] + d18O SiO2/FeO Mn Zn SiO2/(FeO+MnO) Sb/As Sn + 1 -5.32 0.5 2.45 [atP] 240 [mg/kg] 0.32 5.69 56 [µg/g] + K2O+MgO+Na2O + 1 54 [wtP] --- @@ -73,66 +75,66 @@ 12 1.39 [wtP] 0.17 [wtP] 0.13 [wtP] 47.64 [wtP] 4.235 [wtP] 6.56 [%] 13 2.10 [wtP] 0.34 [wtP] 1.32 [wtP] 43.21 [wtP] 2.161 [wtP] 1.52 [%] 14 3.51 [wtP] 0.24 [wtP] 0.34 [wtP] 45.90 [wtP] 2.387 [wtP] 0.85 [%] - K2O Cu As LOI Ag Sn - 1 1.34 [wtP] 0.11 [wtP] 0.02 [wtP] 1.89 [wtP] 500 [ppb] 85 [µg/ml] - 2 1.26 [wtP] 0.22 [wtP] 0.01 [wtP] NA [wtP] 200 [ppb] 85 [µg/ml] - 3 1.54 [wtP] 0.74 [wtP] NA [wtP] NA [wtP] 210 [ppb] 98 [µg/ml] - 4 1.66 [wtP] 0.13 [wtP] 0.01 [wtP] NA [wtP] 350 [ppb] 45 [µg/ml] - 5 2.15 [wtP] 0.14 [wtP] 0.03 [wtP] 3.57 [wtP] 400 [ppb] 14 [µg/ml] - 6 1.21 [wtP] 0.09 [wtP] 0.11 [wtP] 5.66 [wtP] 250 [ppb] 181 [µg/ml] - 7 2.26 [wtP] 0.21 [wtP] 0.23 [wtP] 0.12 [wtP] 120 [ppb] 566 [µg/ml] - 8 1.86 [wtP] 0.43 [wtP] 0.02 [wtP] 0.89 [wtP] 430 [ppb] 203 [µg/ml] - 9 2.01 [wtP] 0.59 [wtP] 0.08 [wtP] 4.75 [wtP] 300 [ppb] 5594 [µg/ml] - 10 1.34 [wtP] 0.07 [wtP] 0.06 [wtP] NA [wtP] 2420 [ppb] 32 [µg/ml] - 11 1.34 [wtP] 0.15 [wtP] 0.01 [wtP] 4.22 [wtP] 140 [ppb] 23 [µg/ml] - 12 0.98 [wtP] 0.16 [wtP] 0.02 [wtP] 6.01 [wtP] 1610 [ppb] 45 [µg/ml] - 13 1.48 [wtP] 0.27 [wtP] 0.05 [wtP] 3.22 [wtP] 100 [ppb] 15 [µg/ml] - 14 1.32 [wtP] 0.09 [wtP] 0.14 [wtP] NA [wtP] 100 [ppb] 146 [µg/ml] - Sb Te Bi U V Cr Co Ni - 1 370 [ppm] 2 [ppm] 18 [ppm] 3 [ppm] 60 [ppm] 160 [ppm] 60 [ppm] 60 [ppm] - 2 210 [ppm] 2 [ppm] 20 [ppm] 2 [ppm] 60 [ppm] 140 [ppm] 70 [ppm] 100 [ppm] - 3 256 [ppm] NA [ppm] 47 [ppm] NA [ppm] 63 [ppm] 99 [ppm] 55 [ppm] 87 [ppm] - 4 400 [ppm] 2 [ppm] NA [ppm] 2 [ppm] 45 [ppm] 30 [ppm] 30 [ppm] 20 [ppm] - 5 410 [ppm] 2 [ppm] 6 [ppm] 3 [ppm] 55 [ppm] 75 [ppm] NA [ppm] 10 [ppm] - 6 453 [ppm] 6 [ppm] 9 [ppm] 3 [ppm] 54 [ppm] 41 [ppm] 8 [ppm] 47 [ppm] - 7 701 [ppm] 8 [ppm] 9 [ppm] NA [ppm] 75 [ppm] 106 [ppm] 22 [ppm] 19 [ppm] - 8 231 [ppm] 15 [ppm] 3 [ppm] NA [ppm] 84 [ppm] 91 [ppm] 47 [ppm] 34 [ppm] - 9 107 [ppm] 6 [ppm] 5 [ppm] 5 [ppm] 32 [ppm] 63 [ppm] 17 [ppm] 37 [ppm] - 10 3310 [ppm] 9 [ppm] 150 [ppm] NA [ppm] 61 [ppm] 45 [ppm] NA [ppm] 4 [ppm] - 11 95 [ppm] 5 [ppm] 1 [ppm] 3 [ppm] 37 [ppm] 36 [ppm] 69 [ppm] 39 [ppm] - 12 78 [ppm] 5 [ppm] 4 [ppm] 3 [ppm] 40 [ppm] 33 [ppm] 43 [ppm] 39 [ppm] - 13 610 [ppm] 2 [ppm] 1 [ppm] 2 [ppm] 80 [ppm] 190 [ppm] 20 [ppm] 50 [ppm] - 14 390 [ppm] NA [ppm] NA [ppm] 1 [ppm] 40 [ppm] 100 [ppm] 10 [ppm] 65 [ppm] - Sr Se FeOtot/SiO2 (Na2O+K2O)/SiO2 206Pb/204Pb - 1 130 [ppm] NA [ppm] 1.386 0.14037 18.61147 - 2 120 [ppm] NA [ppm] 1.756 0.11906 18.61617 - 3 121 [ppm] 15 [ppm] 1.003 0.21443 18.61615 - 4 250 [ppm] NA [ppm] 1.791 0.33269 18.61272 - 5 160 [ppm] NA [ppm] 0.734 0.11839 18.63562 - 6 258 [ppm] NA [ppm] 1.081 0.15696 18.63952 - 7 122 [ppm] 3 [ppm] 1.051 0.16741 18.64465 - 8 280 [ppm] 3 [ppm] 1.561 0.13869 18.83274 - 9 310 [ppm] NA [ppm] 2.279 0.33569 18.83312 - 10 287 [ppm] 5 [ppm] 0.166 0.17145 18.80371 - 11 155 [ppm] NA [ppm] 3.333 0.28031 18.80760 - 12 165 [ppm] NA [ppm] 1.672 0.19235 18.81356 - 13 100 [ppm] NA [ppm] 1.136 0.12303 18.81501 - 14 120 [ppm] NA [ppm] 1.449 0.15851 18.82037 - 206Pb/204Pb_err2SD 207Pb/204Pb 207Pb/204Pb_err2SD 208Pb/204Pb - 1 0.01082 15.65405 0.00915 38.75630 - 2 0.01629 15.65682 0.01341 38.76404 - 3 0.01698 15.65740 0.01441 38.76411 - 4 0.00926 15.65677 0.00747 38.76097 - 5 0.01189 15.64765 0.00989 38.73583 - 6 0.01802 15.65502 0.01513 38.75729 - 7 0.01468 15.65836 0.01210 38.76615 - 8 0.01608 15.82161 0.01350 39.20161 - 9 0.01875 15.82042 0.01619 39.20067 - 10 0.03129 15.81646 0.02600 39.15557 - 11 0.01550 15.82128 0.01309 39.17608 - 12 0.01557 15.82045 0.01305 39.17244 - 13 0.01535 15.80824 0.01350 39.13346 - 14 0.01483 15.81119 0.01252 39.13715 + K2O Cu As LOI Ag Sn + 1 1.34 [wtP] 0.11 [wtP] 0.02 [wtP] 1.89 [wtP] 500 [ug/kg] 85 [µg/ml] + 2 1.26 [wtP] 0.22 [wtP] 0.01 [wtP] NA [wtP] 200 [ug/kg] 85 [µg/ml] + 3 1.54 [wtP] 0.74 [wtP] NA [wtP] NA [wtP] 210 [ug/kg] 98 [µg/ml] + 4 1.66 [wtP] 0.13 [wtP] 0.01 [wtP] NA [wtP] 350 [ug/kg] 45 [µg/ml] + 5 2.15 [wtP] 0.14 [wtP] 0.03 [wtP] 3.57 [wtP] 400 [ug/kg] 14 [µg/ml] + 6 1.21 [wtP] 0.09 [wtP] 0.11 [wtP] 5.66 [wtP] 250 [ug/kg] 181 [µg/ml] + 7 2.26 [wtP] 0.21 [wtP] 0.23 [wtP] 0.12 [wtP] 120 [ug/kg] 566 [µg/ml] + 8 1.86 [wtP] 0.43 [wtP] 0.02 [wtP] 0.89 [wtP] 430 [ug/kg] 203 [µg/ml] + 9 2.01 [wtP] 0.59 [wtP] 0.08 [wtP] 4.75 [wtP] 300 [ug/kg] 5594 [µg/ml] + 10 1.34 [wtP] 0.07 [wtP] 0.06 [wtP] NA [wtP] 2420 [ug/kg] 32 [µg/ml] + 11 1.34 [wtP] 0.15 [wtP] 0.01 [wtP] 4.22 [wtP] 140 [ug/kg] 23 [µg/ml] + 12 0.98 [wtP] 0.16 [wtP] 0.02 [wtP] 6.01 [wtP] 1610 [ug/kg] 45 [µg/ml] + 13 1.48 [wtP] 0.27 [wtP] 0.05 [wtP] 3.22 [wtP] 100 [ug/kg] 15 [µg/ml] + 14 1.32 [wtP] 0.09 [wtP] 0.14 [wtP] NA [wtP] 100 [ug/kg] 146 [µg/ml] + Sb Te Bi U V Cr + 1 370 [mg/kg] 2 [mg/kg] 18 [mg/kg] 3 [mg/kg] 60 [mg/kg] 160 [mg/kg] + 2 210 [mg/kg] 2 [mg/kg] 20 [mg/kg] 2 [mg/kg] 60 [mg/kg] 140 [mg/kg] + 3 256 [mg/kg] NA [mg/kg] 47 [mg/kg] NA [mg/kg] 63 [mg/kg] 99 [mg/kg] + 4 400 [mg/kg] 2 [mg/kg] NA [mg/kg] 2 [mg/kg] 45 [mg/kg] 30 [mg/kg] + 5 410 [mg/kg] 2 [mg/kg] 6 [mg/kg] 3 [mg/kg] 55 [mg/kg] 75 [mg/kg] + 6 453 [mg/kg] 6 [mg/kg] 9 [mg/kg] 3 [mg/kg] 54 [mg/kg] 41 [mg/kg] + 7 701 [mg/kg] 8 [mg/kg] 9 [mg/kg] NA [mg/kg] 75 [mg/kg] 106 [mg/kg] + 8 231 [mg/kg] 15 [mg/kg] 3 [mg/kg] NA [mg/kg] 84 [mg/kg] 91 [mg/kg] + 9 107 [mg/kg] 6 [mg/kg] 5 [mg/kg] 5 [mg/kg] 32 [mg/kg] 63 [mg/kg] + 10 3310 [mg/kg] 9 [mg/kg] 150 [mg/kg] NA [mg/kg] 61 [mg/kg] 45 [mg/kg] + 11 95 [mg/kg] 5 [mg/kg] 1 [mg/kg] 3 [mg/kg] 37 [mg/kg] 36 [mg/kg] + 12 78 [mg/kg] 5 [mg/kg] 4 [mg/kg] 3 [mg/kg] 40 [mg/kg] 33 [mg/kg] + 13 610 [mg/kg] 2 [mg/kg] 1 [mg/kg] 2 [mg/kg] 80 [mg/kg] 190 [mg/kg] + 14 390 [mg/kg] NA [mg/kg] NA [mg/kg] 1 [mg/kg] 40 [mg/kg] 100 [mg/kg] + Co Ni Sr Se FeOtot/SiO2 (Na2O+K2O)/SiO2 + 1 60 [mg/kg] 60 [mg/kg] 130 [mg/kg] NA [mg/kg] 1.386 0.14037 + 2 70 [mg/kg] 100 [mg/kg] 120 [mg/kg] NA [mg/kg] 1.756 0.11906 + 3 55 [mg/kg] 87 [mg/kg] 121 [mg/kg] 15 [mg/kg] 1.003 0.21443 + 4 30 [mg/kg] 20 [mg/kg] 250 [mg/kg] NA [mg/kg] 1.791 0.33269 + 5 NA [mg/kg] 10 [mg/kg] 160 [mg/kg] NA [mg/kg] 0.734 0.11839 + 6 8 [mg/kg] 47 [mg/kg] 258 [mg/kg] NA [mg/kg] 1.081 0.15696 + 7 22 [mg/kg] 19 [mg/kg] 122 [mg/kg] 3 [mg/kg] 1.051 0.16741 + 8 47 [mg/kg] 34 [mg/kg] 280 [mg/kg] 3 [mg/kg] 1.561 0.13869 + 9 17 [mg/kg] 37 [mg/kg] 310 [mg/kg] NA [mg/kg] 2.279 0.33569 + 10 NA [mg/kg] 4 [mg/kg] 287 [mg/kg] 5 [mg/kg] 0.166 0.17145 + 11 69 [mg/kg] 39 [mg/kg] 155 [mg/kg] NA [mg/kg] 3.333 0.28031 + 12 43 [mg/kg] 39 [mg/kg] 165 [mg/kg] NA [mg/kg] 1.672 0.19235 + 13 20 [mg/kg] 50 [mg/kg] 100 [mg/kg] NA [mg/kg] 1.136 0.12303 + 14 10 [mg/kg] 65 [mg/kg] 120 [mg/kg] NA [mg/kg] 1.449 0.15851 + 206Pb/204Pb 206Pb/204Pb_err2SD 207Pb/204Pb 207Pb/204Pb_err2SD 208Pb/204Pb + 1 18.61147 0.01082 15.65405 0.00915 38.75630 + 2 18.61617 0.01629 15.65682 0.01341 38.76404 + 3 18.61615 0.01698 15.65740 0.01441 38.76411 + 4 18.61272 0.00926 15.65677 0.00747 38.76097 + 5 18.63562 0.01189 15.64765 0.00989 38.73583 + 6 18.63952 0.01802 15.65502 0.01513 38.75729 + 7 18.64465 0.01468 15.65836 0.01210 38.76615 + 8 18.83274 0.01608 15.82161 0.01350 39.20161 + 9 18.83312 0.01875 15.82042 0.01619 39.20067 + 10 18.80371 0.03129 15.81646 0.02600 39.15557 + 11 18.80760 0.01550 15.82128 0.01309 39.17608 + 12 18.81356 0.01557 15.82045 0.01305 39.17244 + 13 18.81501 0.01535 15.80824 0.01350 39.13346 + 14 18.82037 0.01483 15.81119 0.01252 39.13715 208Pb/204Pb_err2SD 207Pb/206Pb 207Pb/206Pb_err2SD 208Pb/206Pb 1 0.02270 0.84110 0.00006 2.08239 2 0.03450 0.84103 0.00013 2.08229 @@ -174,8 +176,8 @@ Contextual columns: other, other2 # A data frame: 1 x 16 ID `206Pb/204Pb` Al2O3 `SiO2+Al2O3` `204Pb` other other2 K2O d18O - [count/s] [ppt] [ppm] [wtP] + [count/s] [ng/kg] [mg/kg] [wtP] 1 troet 0.5 3 20 7 troet 27 23 -5.32 - # i 7 more variables: `SiO2/FeO` , Mn [atP], Zn [ppm], + # i 7 more variables: `SiO2/FeO` , Mn [atP], Zn [mg/kg], # `SiO2/(FeO+MnO)` , `Sb/As` , Sn [µg/g], `K2O+MgO+Na2O` [wtP] diff --git a/tests/testthat/setup.R b/tests/testthat/setup.R index d039fdd..ea93e54 100644 --- a/tests/testthat/setup.R +++ b/tests/testthat/setup.R @@ -2,8 +2,11 @@ # Additional packages to load for testing or setting up the test environment -library(vdiffr) +library(testthat) +library(ASTR) +library(tibble) library(ggplot2) +library(vdiffr) library(readxl) # reference data sets diff --git a/tests/testthat/test_archchem_column_selectionc.R b/tests/testthat/test_archchem_column_selectionc.R index ba9cf7c..6349d8f 100644 --- a/tests/testthat/test_archchem_column_selectionc.R +++ b/tests/testthat/test_archchem_column_selectionc.R @@ -1,8 +1,8 @@ # test columns selection function not checked elsewhere test_input <- suppressWarnings( - as_archchem( - archchem_example_input, + read_archchem( + system.file("extdata", "test_data_input_good.csv", package = "ASTR"), id_column = "Sample", context = c("Lab no.", "Site", "latitude", "longitude", "Type", "method_comp") ) From 2417e03aa728f3f3d7b4370c9e30ee122fdc8e9b Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 15 Mar 2026 18:16:49 -0400 Subject: [PATCH 22/27] add handling of relative units --- R/archchem_colname_parser.R | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/R/archchem_colname_parser.R b/R/archchem_colname_parser.R index 4a488b5..9c3f723 100644 --- a/R/archchem_colname_parser.R +++ b/R/archchem_colname_parser.R @@ -118,11 +118,32 @@ parse_colnames <- function(x, context, drop_columns) { # concentrations if (is_concentration(colname)) { unit_from_name <- extract_unit_string(colname) - # handle special cases + # handle relative units and special cases unit_from_name <- dplyr::recode_values( unit_from_name, c("at%", "atP") ~ "atP", - c("wt%", "wtP", "w/w%") ~ "wtP", + c("wt%", "wtP", + "w/w%", "m/m%", "%w/w", "%m/m", + "(w/w)%", "(m/m)%", "%(w/w)", "%(m/m)", + "pph", "pph(m/m)", "pph(w/w)" + ) ~ "wtP", + c("%w/v", "%m/v", "%(w/v)", "%(m/v)", + "w/v%", "m/v%", "(w/v)%", "(m/v)%", + "pph(m/v)" + ) ~ "0.01 g/L", + c("%v/v", "v/v%", "%(v/v)", "(v/v)%", "pph(v/v)") ~ "0.01 L/L", + c("ppm(w/w)", "ppm(m/m)", "ppm") ~ "mg/kg", + c("ppm(m/v)") ~ "mg/L", + c("ppm(v/v)") ~ "ml/L", + c("ppb(m/m)", "ppb(w/w)", "ppb") ~ "ug/kg", + c("ppb(m/v)", "ppb(w/v)") ~ "ug/L", + c("ppb(v/v)") ~ "ul/L", + c("ppt(m/m)", "ppt(w/w)", "ppt") ~ "ng/kg", + c("ppt(m/v)", "ppt(w/v)") ~ "ng/L", + c("ppt(v/v)") ~ "nl/L", + c("ppq(m/m)", "ppq(w/w)", "ppq") ~ "pg/kg", + c("ppq(m/v)", "ppq(w/v)") ~ "pg/L", + c("ppq(v/v)") ~ "pl/L", c("cps") ~ "count/s", default = unit_from_name ) From 4ba9d22bdea27f8c9acb8ba3be36f0a4ee23c569 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 15 Mar 2026 18:17:08 -0400 Subject: [PATCH 23/27] include draft for unit conversion --- R/archchem_unit_manipulation.R | 46 +++++++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/R/archchem_unit_manipulation.R b/R/archchem_unit_manipulation.R index d81034f..e90be16 100644 --- a/R/archchem_unit_manipulation.R +++ b/R/archchem_unit_manipulation.R @@ -54,14 +54,42 @@ unify_concentration_unit <- function(x, unit, ...) { } #' @export unify_concentration_unit <- function(x, unit, ...) { - dplyr::mutate( - x, - dplyr::across( - tidyselect::where(function(y) { - class(y) == "units" && - units::ud_are_convertible(units::deparse_unit(y), "%") - }), - function(z) units::set_units(z, unit, mode = "standard") - ) + switch(unit, + # Conversion to atP + atP = { + # identify all oxides and present, convert to wt% + + # convert all other concentrations to wt% + + # convert to at% + + }, + # Conversion to oxP + oxP = { + # identify all elements that are not oxides and convert to wt% + + # convert all elements to ox% + + }, + # conversion to all other units + { + # something here to convert from atP and oxP to wt% first + + dplyr::mutate( + x, + dplyr::across( + tidyselect::where(function(y) { + class(y) == "units" && + units::ud_are_convertible(units::deparse_unit(y), "mg/kg") + }), + function(z) units::set_units(z, unit, mode = "standard") + ) + ) + } ) } + +# units_to_convert <- unlist( +# sapply(df[elements], function(x) class(x) == "units" & units(x)[1] != unit) +# ) +# df_convert <- cbind(df$ID, subset(df[elements], select = units_to_convert)) From a69aebe67209d7401eca72dfd6234eca976de4b5 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Sun, 15 Mar 2026 18:17:31 -0400 Subject: [PATCH 24/27] attempt to link wtP to SI units --- R/zzz.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/zzz.R b/R/zzz.R index aed5a1c..9e99950 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -39,6 +39,7 @@ NULL ) safe_install( symbol = "wtP", + #def = "(0.01 g) / g", def = "0.01 mass_basis", name = "weight percent" ) From 3255fa271c3a392e11e44c89a6efdbb085a80fdd Mon Sep 17 00:00:00 2001 From: Clemens Schmid Date: Wed, 18 Mar 2026 15:31:09 +0100 Subject: [PATCH 25/27] defined wtP and mass_basis explicitly as a dimensionless unit --- R/zzz.R | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/R/zzz.R b/R/zzz.R index 9e99950..d575db4 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -23,15 +23,19 @@ NULL # when the package is loaded .onLoad <- function(libname, pkgname) { safe_install <- function(...) { + # tryCatch to prevent an error when these units are already defined in + # the current session tryCatch( units::install_unit(...), error = function(e) NULL ) } - # dummy base units (purely semantic) + # define dummy base units (purely semantic) + # see ?units::install_unit for details on this mechanism safe_install("atomic_basis") - safe_install("mass_basis") - # percent-like units + safe_install("mass_basis", "unitless") + # create percent-like units + # the %-sign can not be used in custom unit names, so we use P safe_install( symbol = "atP", def = "0.01 atomic_basis", @@ -39,7 +43,6 @@ NULL ) safe_install( symbol = "wtP", - #def = "(0.01 g) / g", def = "0.01 mass_basis", name = "weight percent" ) From 2b903db0501aad035b76962d2dff6cd124c495e0 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Wed, 18 Mar 2026 21:22:49 -0400 Subject: [PATCH 26/27] add additional information about supported relative units. --- vignettes/VG.ASTRschema.0.0.2.Rmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vignettes/VG.ASTRschema.0.0.2.Rmd b/vignettes/VG.ASTRschema.0.0.2.Rmd index bc4f461..597c556 100644 --- a/vignettes/VG.ASTRschema.0.0.2.Rmd +++ b/vignettes/VG.ASTRschema.0.0.2.Rmd @@ -26,7 +26,7 @@ All columns that contain element and oxide compositions and analytical errors, i - The names of oxides and trace elements are self-explanatory (e.g. `SiO2` or `Si`). Total iron, if given, should be expressed as: `FeOtot` or `Fe2O3tot`. Loss on ignition, where known, should be expressed as `LOI`. -- Units for values should follow element or oxide, as `_`. Units supported include *all SI units*, as well as *ppm*, *ppb*, *ppt*, *%*, *wt%*, *at%*, *w/w%*, *‰*, *counts*, and *cps*, noted as such. +- Units for values should follow element or oxide, as `_`. Units supported include *all SI units*, as well as *ppm*, *ppb*, *ppt*, *ppq*, *%*, *wt%*, *at%*, *‰*, *counts*, and *cps*, noted as such. For relative units, the type of concentration can be specified, e.g. *w/w%* or *ppm(m/v)* - Where known, specify *wt%* and *at%* instead of using *%*. For *‘per mille’*, use the symbol *‰*. From 517194474d131e27f66f9eb014e10b6edf761421 Mon Sep 17 00:00:00 2001 From: Thomas Rose Date: Wed, 18 Mar 2026 21:25:47 -0400 Subject: [PATCH 27/27] attempt to define methods for setting archchem column classes. This would be handy for columns created in functions to be then included in an archchem object. --- R/archchem_column_define.R | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 R/archchem_column_define.R diff --git a/R/archchem_column_define.R b/R/archchem_column_define.R new file mode 100644 index 0000000..41ed462 --- /dev/null +++ b/R/archchem_column_define.R @@ -0,0 +1,11 @@ +# functions to define classes for columns in archchem (e.g. for columns newly defined in functions) + +#' @rdname archchem +#' @export +as_contextual_column <- function(x, ...) { + UseMethod("as_contextual_column") +} +#' @export +as_contextual_column.archchem <- function(x, ...) { + sapply(x, structure, class = "archchem_context") +}