diff --git a/DESCRIPTION b/DESCRIPTION index b582709..155253a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -56,6 +56,7 @@ Imports: stats, tibble, tidyselect, + tidyr, units, vctrs, ggplot2, diff --git a/NAMESPACE b/NAMESPACE index 7ceaf9c..c2ea4ff 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -28,6 +28,7 @@ export(element_to_oxide) export(geom_kde2d) export(geom_sk_labels) export(geom_sk_lines) +export(geom_spider) export(get_analytical_columns) export(get_concentration_columns) export(get_contextual_columns) @@ -36,6 +37,7 @@ export(get_error_columns) export(get_isotope_columns) export(get_ratio_columns) export(get_unit_columns) +export(normalise_geochem) export(oxide_to_element) export(pb_iso_age_model) export(pointcloud_distribution) diff --git a/R/ASTR_data.R b/R/ASTR_data.R index bd0fe3f..e95503f 100644 --- a/R/ASTR_data.R +++ b/R/ASTR_data.R @@ -69,3 +69,52 @@ #' @source #' @name ArgentinaDatabase "ArgentinaDatabase" + +#' Geochemical and other standard groups of elements, oxides, or isotopes +#' @format A list of sets of elements, oxides, or isotopes that represent +#' geochemical groups or other commonly used groups. To retrieve the full list +#' of e.g. the HFSE elements, run `standard_groups$HFSE`. +#' \describe{ +#' \item{REE}{Rare Earth Elements, taken from Periodic table of elements} +#' \item{HFSE}{High field-strength elements as listed in Salters (1998)} +#' \item{LILE}{Large-ion lithophile elements, as listed in Rudnick (1998)} +#' } +#' +#' @references Salters, V.J.M. (1998). Elements: High field strength. In: +#' Geochemistry. Encyclopedia of Earth Science. Springer, Dordrecht. +#' https://doi.org/10.1007/1-4020-4496-8_101 +#' +#' Rudnick, R.L. (1998). Elements: Large-ion lithophile. In: Geochemistry. +#' Encyclopedia of Earth Science. Springer, Dordrecht. +#' https://doi.org/10.1007/1-4020-4496-8_104 +#' +#' @name standard_groups +"standard_groups" + +#' Reference compositions +#' +#' @format A list including sets of chemical compositions often used as +#' reference composition for e.g. normalisation. Values are stored as named +#' vectors of their concentration with assigned units (using +#' \link[units]{set_units}). To retrieve the list of elements and their +#' concentrations for e.g. the PM, run `references_geochem$PM`. +#' \describe{ +#' \item{chondrite}{CI chondrite composition in ppm, as defined by Sun & +#' McDonough (1989)} +#' \item{PM}{Primitive mantle composition in ppm, as defined by Sun & +#' McDonough (1989)} +#' \item{NMORB}{Normal Mid-ocean ridge basalt composition in ppm, as defined +#' by Sun & McDonough (1989)} +#' \item{EMORB}{Enhanced Mid-Ocean Ridge Basalt composition in ppm, as +#' defined by Sun & McDonough (1989)} +#' \item{OIB}{Ocean Island Basalt composition in ppm, as defined by Sun & +#' McDonough (1989)} +#' } +#' +#' @references Sun, S.-S. & McDonough, W.F. (1989). Chemical and isotopic +#' systematics of oceanic basalts. Geological Society, London, Special +#' Publications 42, pp.313-345. Table 1, page 318. +#' . +#' +#' @name references_geochem +"references_geochem" diff --git a/R/ASTR_geom_spider.R b/R/ASTR_geom_spider.R new file mode 100644 index 0000000..8cc4862 --- /dev/null +++ b/R/ASTR_geom_spider.R @@ -0,0 +1,179 @@ +#' Spidergram geom for ggplot2 +#' +#'Descriptions goes here ################### +#' #### This will likely only work with ASTR objects or similarly wide data frame!? +#' #### Or is there a way to identify short vs. long form!? +#' #### e.g. single value to x and col content = character: long format, +#' x = character vector length > 1 and column content of x = numeric: wide format +#' #### y = single value and numeric: long format, concentration, y is not provided: wide format +#' #### long format values do not need to be transformed (but normalised!?), +#' wide format must be transformed to long format +#' +#' Normalisation is done with [normalise_geochem()]. If data are geochemically +#' normalised, only normalised elements are plotted. Otherwise, all elements +#' are plotted. +#' +#' @inheritParams ggplot2::layer +#' @param reference `NULL`, the default, if data should not be normalised or +#' name of the geochemical reference composition to which data should be +#' normalised. See [references_geochem] for a list of names. +#' @param na.rm Logical: remove NA values +#' @param ... Other arguments passed on to [ggplot2::layer()]. These are often +#' aesthetics used to set a fixed value, such as `colour = "red"` or `alpha = +#' 0.5`. +#' +#' @export +#' +#' @examples +#' # include example with data[[standard_groups$REE]] +#' +#' library(ggplot2) +#' +#' test <- data.frame( +#' Sample = c("A","B"), +#' Yb = c(8,9), +#' La = c(10,5), +#' Ce = c(20,8) +#' ) +#' +#' ggplot(test) + geom_spider(mapping = aes(x = standard_groups$REE, color = Sample)) +#' +geom_spider <- function(mapping = NULL, + data = NULL, + inherit.aes = TRUE, + elements = NULL, + reference = NULL, + na.rm = FALSE, + show.legend = NA, + ...) { + + .data <- data + .elements <- elements + .reference <- reference + + mapping <- utils::modifyList( + ggplot2::aes(x = .data$x, y = .data$y), + if (!is.null(mapping)) mapping else ggplot2::aes() + ) + + list( + suppressWarnings( + ggplot2::layer( + geom = GeomSpider, + mapping = mapping, + data = function(x) { + d <- if (!is.null(.data)) .data else x + + if (!is.null(.reference)) { + d <- normalise_geochem(d, reference = .reference) + } + + elements_present <- intersect(.elements, colnames(d)) + if (length(elements_present) == 0) { + stop("None of the requested elements are present as columns in the data.") + } + if (length(elements_present) < length(.elements)) { + warning("Some requested elements are absent and will be skipped: ", + paste(setdiff(.elements, elements_present), collapse = ", ")) + } + + meta_cols <- setdiff(colnames(d), elements_present) + + data_long <- do.call(rbind, lapply(elements_present, function(el) { + row <- d[meta_cols] + row$elements <- el + row$y <- d[[el]] + row + })) + + data_long$elements <- factor(data_long$elements, levels = elements_present) + data_long$x <- as.numeric(data_long$elements) + + data_long + }, + stat = "identity", + position = "identity", + show.legend = show.legend, + inherit.aes = inherit.aes, + params = list(na.rm = na.rm, ...) + ) + ), + ggplot2::scale_x_continuous( + breaks = seq_along(.elements), + labels = .elements + ), + ggplot2::labs(x = NULL, y = "Concentration") + ) +} + +GeomSpider <- ggplot2::ggproto( + "GeomSpider", + ggplot2::Geom, + + required_aes = character(0), + + default_aes = ggplot2::aes( + colour = "black", + linewidth = 0.6, + linetype = 1, + alpha = NA + ), + + extra_params = c("na.rm"), + + draw_key = ggplot2::draw_key_path, + + setup_data = function(data, params) { + data + }, + + draw_group = function(data, panel_params, coord) { + + if (nrow(data) < 2) return(grid::nullGrob()) + + # Replace NA alpha with 1 + data$alpha[is.na(data$alpha)] <- 1 + + data <- data[order(data$x), ] + + # Fast path — no NAs + if (!any(is.na(data$y))) { + coords <- coord$transform(data, panel_params) + return( + grid::polylineGrob( + coords$x, coords$y, + gp = grid::gpar( + col = coords$colour[1], + lwd = coords$linewidth[1] * ggplot2::.pt, + lty = coords$linetype[1], + alpha = coords$alpha[1] + ) + ) + ) + } + + # NA handling — breaks in line at missing elements + not_na <- !is.na(data$y) + run_ids <- cumsum(c(TRUE, diff(not_na) != 0)) + + grobs <- lapply(unique(run_ids[not_na]), function(run) { + segment <- data[not_na & run_ids == run, , drop = FALSE] + if (nrow(segment) < 2) return(NULL) + + coords <- coord$transform(segment, panel_params) + grid::polylineGrob( + coords$x, coords$y, + gp = grid::gpar( + col = coords$colour[1], + lwd = coords$linewidth[1] * ggplot2::.pt, + lty = coords$linetype[1], + alpha = coords$alpha[1] + ) + ) + }) + + grobs <- Filter(Negate(is.null), grobs) + if (length(grobs) == 0) return(grid::nullGrob()) + do.call(grid::grobTree, grobs) + } +) diff --git a/R/ASTR_normalise_geochem.R b/R/ASTR_normalise_geochem.R new file mode 100644 index 0000000..32b04c6 --- /dev/null +++ b/R/ASTR_normalise_geochem.R @@ -0,0 +1,108 @@ +#' Geochemical normalisation of data +#' +#' Normalises selected elemental concentrations to a reference composition such +#' as chondrite or MORB. +#' +#' See [references_geochem] for the supported reference compositions. +#' Normalisation to other reference compositions is possible by adding them as +#' named vectors, see examples. They will not be stored permanently in the +#' package and must be exported and reloaded (or their definition included in +#' the script). If you would like to include a new reference composition in +#' ASTR, please reach out to the package maintainers or create a pull request in +#' the [package's GitHub repo](https://github.com/archaeothommy/ASTR) with the +#' values to be included and a literature reference. +#' +#' The function converts all elements in the data frame for which a reference +#' composition is available. For [ASTR objects][ASTR], unit conversion is +#' handled by the function. For all other objects, the user must ensure that +#' values and reference composition have the same unit. +#' +#' @param df A data frame in wide format. +#' @param reference Character string with the normalisation. See Details for +#' further information. +#' +#' @return If `df` is an [ASTR object][ASTR], the output is an object of the +#' same type including the ID column, the contextual columns, the +#' compositional data that was normalised, and the normalised values of the +#' respective elements. In all other cases, the data frame provided as input +#' with columns added for the calculated age model parameters. +#' +#' The used reference composition is indicated in the column names of the +#' output by the value of `reference`. +#' +#' @examples +#' df <- data.frame( +#' Sample = c("A","B"), +#' La = c(10,5), +#' Ce = c(20,8) +#' ) +#' +#' normalise_geochem( +#' df, +#' elements = c("La","Ce"), +#' reference = "chondrite" +#' ) +#' +#' # For ASTR objects, units are automatically converted +#' test_file <- system.file("extdata", "test_data_input_good.csv", package = "ASTR") +#' arch <- read_ASTR(test_file, id_column = "Sample", context = 1:7) +#' +#' arch_norm <- normalise_geochem(arch, "chondrite") +#' +#' # adding reference composition for 31X 7835.8A of the CHARM Set +#' references_geochem$"31X 7835.8A" <- units::set_units( +#' value = "%", +#' x = c(P = 0.122, Mn = 0.093, Fe = 0.1, Co = 0.313, Ni = 0.158, Cu = 69.93, +#' Zn = 24.83, As = 0.143, Ag = 0.463, Cd = 0.087, Sn = 0.516, Sb = 0.115, +#' Pb = 3.15, Bi = 0.112 +#' ) +#' ) +#' +#' references_geochem$"31X 7835.8A" +#' +#' @export +#' +normalise_geochem <- function(df, reference = names(references_geochem)) { + + reference <- match.arg(reference) + + # Basic checks + checkmate::assert_data_frame(df) + + elements <- intersect(colnames(df), names(references_geochem[[reference]])) + + if (length(elements) == 0) { + stop("Dataset does not include any element of the reference data.") + } + + # Normalisation + df_norm <- as.data.frame( + mapply(function(x) { + t(t(df[[x]]) / references_geochem[[reference]][x]) + }, + elements + ) + ) + + # rename column names + colnames(df_norm) <- paste0(elements, "_", reference) + + if (inherits(df, "ASTR")) { + df_norm <- cbind(df["ID"], df_norm) + + df_norm <- suppressWarnings( + as_ASTR( + df_norm, + context = colnames(df_norm)[-1] + ) + ) + + df_norm <- cbind(get_contextual_columns(df), df[elements], df_norm[-1]) + df_norm <- preserve_ASTR_attrs(df_norm, df) + + } else { + df_norm <- cbind(df, df_norm) + } + + return(df_norm) +} diff --git a/R/ASTR_utils.R b/R/ASTR_utils.R index 7bf5198..f39e0fe 100644 --- a/R/ASTR_utils.R +++ b/R/ASTR_utils.R @@ -87,3 +87,25 @@ transform_notation <- function(unit) { } unit } + +#' Transforms ASTR format into long format for plotting with ggplot2 +#' +#' The function expects that a data frame is passed as aesthetic to ggplot, +#' resulting in a tibble with a nested dataframe with the name of the aesthtic. +#' +#' @param df the data frame to be transformed into long format +#' @param aesthetic the aesthetic that contains the values from ASTR +#' @param value the name of the column with the values after transformation into +#' long format +#' @keywords internal +#' +ASTR_to_long <- function(df, aesthetic, value) { + colnames(df[[aesthetic]]) <- paste(aesthetic, colnames(df[[aesthetic]]), sep = ".") + tidyr::unnest(df, tidyselect::all_of(aesthetic)) %>% + tidyr::pivot_longer( + cols = tidyselect::starts_with(aesthetic), + names_to = aesthetic, + values_to = value, + names_prefix = paste0(aesthetic, ".") + ) +} diff --git a/_pkgdown.yml b/_pkgdown.yml index 9ffedbd..661ff47 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -32,6 +32,8 @@ reference: - elements_data - oxides_data - special_oxide_states + - standard_groups + - references_geochem - title: Unit conversions desc: > Functions and data tables for conversions between untis not covered in @@ -44,6 +46,7 @@ reference: desc: > Functions for data transformation contents: + - normalise_geochem - copper_alloy_bb - copper_alloy_pollard - copper_group_bray diff --git a/data-raw/geochem_reference_data.R b/data-raw/geochem_reference_data.R new file mode 100644 index 0000000..b704d92 --- /dev/null +++ b/data-raw/geochem_reference_data.R @@ -0,0 +1,83 @@ +# Spidergram internal data + +# Element groups + +# REE retrieved from https://en.wikipedia.org/wiki/Rare-earth_element, +# HFSE retrieved from https://link.springer.com/rwe/10.1007/1-4020-4496-8_101 +# LILE retrieved from https://link.springer.com/rwe/10.1007/1-4020-4496-8_104 + +standard_groups <- list( + REE = c( + "La", "Ce", "Pr", "Nd", "Sm", "Eu", "Gd", "Tb", "Dy", + "Ho", "Er", "Tm", "Yb", "Lu" + ), + HFSE = c("Nb", "Ta", "Zr", "Hf", "Ti"), + LILE = c("Rb", "Ba", "K", "Sr", "Cs") +) + +# Geochemical reference compositions +# units: ppm +references_geochem <- list( + chondrite = units::set_units( + value = "mg/kg", + x = c( + Cs = 0.188, TI = 0.14, Rb = 2.32, Ba = 2.41, W = 0.095, Th = 0.029, + U = 0.008, Nb = 0.246, Ta = 0.014, K = 545, La = 0.237, Ce = 0.612, + Pb = 2.47, Pr = 0.095, Mo = 0.92, Sr = 7.26, P = 1220, Nd = 0.467, + F = 60.7, Sm = 0.153, Zr = 3.87, Hf = 0.1066, Eu = 0.058, Sn = 1.72, + Sb = 0.16, Ti = 445, Gd = 0.2055, Tb = 0.0374, Dy = 0.254, Li = 1.57, + Y = 1.57, Ho = 0.0566, Er = 0.1655, Tm = 0.0255, Yb = 0.17, Lu = 0.0254 + ) + ), + PM = units::set_units( + value = "mg/kg", + x = c( + Cs = 0.032, TI = 0.005, Rb = 0.635, Ba = 6.989, W = 0.02, Th = 0.085, + U = 0.021, Nb = 0.713, Ta = 0.041, K = 250, La = 0.687, Ce = 1.775, + Pb = 0.185, Pr = 0.276, Mo = 0.063, Sr = 21.1, P = 95, Nd = 1.354, F = 26, + Sm = 0.444, Zr = 11.2, Hf = 0.309, Eu = 0.168, Sn = 0.17, Sb = 0.005, + Ti = 1300, Gd = 0.596, Tb = 0.108, Dy = 0.737, Li = 1.6, Y = 4.55, + Ho = 0.164, Er = 0.48, Tm = 0.074, Yb = 0.493, Lu = 0.074 + ) + ), + NMORB = units::set_units( + value = "mg/kg", + x = c( + Cs = 0.007, TI = 0.0014, Rb = 0.56, Ba = 6.3, W = 0.01, Th = 0.12, + U = 0.047, Nb = 2.33, Ta = 0.132, K = 600, La = 2.5, Ce = 7.5, Pb = 0.3, + Pr = 1.32, Mo = 0.31, Sr = 90, P = 510, Nd = 7.3, F = 210, Sm = 2.63, + Zr = 74, Hf = 2.05, Eu = 1.02, Sn = 1.1, Sb = 0.01, Ti = 7600, Gd = 3.68, + Tb = 0.67, Dy = 4.55, Li = 4.3, Y = 28, Ho = 1.01, Er = 2.97, Tm = 0.456, + Yb = 3.05, Lu = 0.455 + ) + ), + EMORB = units::set_units( + value = "mg/kg", + x = c( + Cs = 0.063, TI = 0.013, Rb = 5.04, Ba = 57, W = 0.092, Th = 0.6, U = 0.18, + Nb = 8.3, Ta = 0.47, K = 2100, La = 6.3, Ce = 15, Pb = 0.6, Pr = 2.05, + Mo = 0.47, Sr = 155, P = 620, Nd = 9, F = 250, Sm = 2.6, Zr = 73, + Hf = 2.03, Eu = 0.91, Sn = 0.8, Sb = 0.01, Ti = 6000, Gd = 2.97, Tb = 0.53, + Dy = 3.55, Li = 3.5, Y = 22, Ho = 0.79, Er = 2.31, Tm = 0.356, Yb = 2.37, + Lu = 0.354 + ) + ), + OIB = units::set_units( + value = "mg/kg", + x = c( + Cs = 0.387, TI = 0.077, Rb = 31, Ba = 350, W = 0.56, Th = 4, U = 1.02, + Nb = 48, Ta = 2.7, K = 12000, La = 37, Ce = 80, Pb = 3.2, Pr = 9.7, + Mo = 2.4, Sr = 660, P = 2700, Nd = 38.5, F = 1150, Sm = 10, Zr = 280, + Hf = 7.8, Eu = 3, Sn = 2.7, Sb = 0.03, Ti = 17200, Gd = 7.62, Tb = 1.05, + Dy = 5.6, Li = 5.6, Y = 29, Ho = 1.06, Er = 2.62, Tm = 0.35, Yb = 2.16, + Lu = 0.3 + ) + ) +) + +# Save as internal data +usethis::use_data(standard_groups, + references_geochem, + internal = FALSE, + overwrite = TRUE +) diff --git a/data/references_geochem.rda b/data/references_geochem.rda new file mode 100644 index 0000000..6c7061e Binary files /dev/null and b/data/references_geochem.rda differ diff --git a/data/standard_groups.rda b/data/standard_groups.rda new file mode 100644 index 0000000..c32feaf Binary files /dev/null and b/data/standard_groups.rda differ diff --git a/man/ASTR_to_long.Rd b/man/ASTR_to_long.Rd new file mode 100644 index 0000000..657f976 --- /dev/null +++ b/man/ASTR_to_long.Rd @@ -0,0 +1,21 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ASTR_utils.R +\name{ASTR_to_long} +\alias{ASTR_to_long} +\title{Transforms ASTR format into long format for plotting with ggplot2} +\usage{ +ASTR_to_long(df, aesthetic, value) +} +\arguments{ +\item{df}{the data frame to be transformed into long format} + +\item{aesthetic}{the aesthetic that contains the values from ASTR} + +\item{value}{the name of the column with the values after transformation into +long format} +} +\description{ +The function expects that a data frame is passed as aesthetic to ggplot, +resulting in a tibble with a nested dataframe with the name of the aesthtic. +} +\keyword{internal} diff --git a/man/geom_spider.Rd b/man/geom_spider.Rd new file mode 100644 index 0000000..ab89075 --- /dev/null +++ b/man/geom_spider.Rd @@ -0,0 +1,96 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/geom_spider.R +\name{geom_spider} +\alias{geom_spider} +\title{Spidergram geom for ggplot2} +\usage{ +geom_spider( + mapping = NULL, + data = NULL, + inherit.aes = TRUE, + reference = NULL, + na.rm = FALSE, + show.legend = NA, + ... +) +} +\arguments{ +\item{mapping}{Set of aesthetic mappings created by \code{\link[ggplot2:aes]{aes()}}. If specified and +\code{inherit.aes = TRUE} (the default), it is combined with the default mapping +at the top level of the plot. You must supply \code{mapping} if there is no plot +mapping.} + +\item{data}{The data to be displayed in this layer. There are three +options: + +If \code{NULL}, the default, the data is inherited from the plot +data as specified in the call to \code{\link[ggplot2:ggplot]{ggplot()}}. + +A \code{data.frame}, or other object, will override the plot +data. All objects will be fortified to produce a data frame. See +\code{\link[ggplot2:fortify]{fortify()}} for which variables will be created. + +A \code{function} will be called with a single argument, +the plot data. The return value must be a \code{data.frame}, and +will be used as the layer data. A \code{function} can be created +from a \code{formula} (e.g. \code{~ head(.x, 10)}).} + +\item{inherit.aes}{If \code{FALSE}, overrides the default aesthetics, +rather than combining with them. This is most useful for helper functions +that define both data and aesthetics and shouldn't inherit behaviour from +the default plot specification, e.g. \code{\link[ggplot2:annotation_borders]{annotation_borders()}}.} + +\item{reference}{\code{NULL}, the default, if data should not be normalised or +name of the geochemical reference composition to which data should be +normalised. See \link{references_geochem} for a list of names.} + +\item{na.rm}{Logical: remove NA values} + +\item{show.legend}{logical. Should this layer be included in the legends? +\code{NA}, the default, includes if any aesthetics are mapped. +\code{FALSE} never includes, and \code{TRUE} always includes. +It can also be a named logical vector to finely select the aesthetics to +display. To include legend keys for all levels, even +when no data exists, use \code{TRUE}. If \code{NA}, all levels are shown in legend, +but unobserved levels are omitted.} + +\item{...}{Other arguments passed on to \code{\link[ggplot2:layer]{ggplot2::layer()}}. These are often +aesthetics used to set a fixed value, such as \code{colour = "red"} or \code{alpha = 0.5}.} +} +\description{ +Descriptions goes here ################### +\subsection{This will likely only work with ASTR objects or similarly wide data frame!?}{ +} + +\subsection{Or is there a way to identify short vs. long form!?}{ +} + +\subsection{e.g. single value to x and col content = character: long format, x = character vector length > 1 and column content of x = numeric: wide format}{ +} + +\subsection{y = single value and numeric: long format, concentration, y is not provided: wide format}{ +} + +\subsection{long format values do not need to be transformed (but normalised!?), wide format must be transformed to long format}{ +} +} +\details{ +Normalisation is done with \code{\link[=normalise_geochem]{normalise_geochem()}}. If data are geochemically +normalised, only normalised elements are plotted. Otherwise, all elements +are plotted. +} +\examples{ +# include example with data[[standard_groups$REE]] + +library(ggplot2) + +test <- data.frame( + Sample = c("A","B"), + Yb = c(8,9), + La = c(10,5), + Ce = c(20,8) +) + +ggplot(test) + geom_spider(mapping = aes(x = standard_groups$REE, color = Sample)) + +} diff --git a/man/normalise_geochem.Rd b/man/normalise_geochem.Rd new file mode 100644 index 0000000..de37bc4 --- /dev/null +++ b/man/normalise_geochem.Rd @@ -0,0 +1,74 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ASTR_normalise_geochem.R +\name{normalise_geochem} +\alias{normalise_geochem} +\title{Geochemical normalisation of data} +\usage{ +normalise_geochem(df, reference = names(references_geochem)) +} +\arguments{ +\item{df}{A data frame in wide format.} + +\item{reference}{Character string with the normalisation. See Details for +further information.} +} +\value{ +If \code{df} is an \link[=ASTR]{ASTR object}, the output is an object of the +same type including the ID column, the contextual columns, the +compositional data that was normalised, and the normalised values of the +respective elements. In all other cases, the data frame provided as input +with columns added for the calculated age model parameters. + +The used reference composition is indicated in the column names of the +output by the value of \code{reference}. +} +\description{ +Normalises selected elemental concentrations to a reference composition such +as chondrite or MORB. +} +\details{ +See \link{references_geochem} for the supported reference compositions. +Normalisation to other reference compositions is possible by adding them as +named vectors, see examples. They will not be stored permanently in the +package and must be exported and reloaded (or their definition included in +the script). If you would like to include a new reference composition in +ASTR, please reach out to the package maintainers or create a pull request in +the \href{https://github.com/archaeothommy/ASTR}{package's GitHub repo} with the +values to be included and a literature reference. + +The function converts all elements in the data frame for which a reference +composition is available. For \link[=ASTR]{ASTR objects}, unit conversion is +handled by the function. For all other objects, the user must ensure that +values and reference composition have the same unit. +} +\examples{ +df <- data.frame( + Sample = c("A","B"), + La = c(10,5), + Ce = c(20,8) +) + +normalise_geochem( + df, + elements = c("La","Ce"), + reference = "chondrite" +) + +# For ASTR objects, units are automatically converted +test_file <- system.file("extdata", "test_data_input_good.csv", package = "ASTR") +arch <- read_ASTR(test_file, id_column = "Sample", context = 1:7) + +arch_norm <- normalise_geochem(arch, "chondrite") + +# adding reference composition for 31X 7835.8A of the CHARM Set +references_geochem$"31X 7835.8A" <- units::set_units( + value = "\%", + x = c(P = 0.122, Mn = 0.093, Fe = 0.1, Co = 0.313, Ni = 0.158, Cu = 69.93, + Zn = 24.83, As = 0.143, Ag = 0.463, Cd = 0.087, Sn = 0.516, Sb = 0.115, + Pb = 3.15, Bi = 0.112 + ) +) + +references_geochem$"31X 7835.8A" + +} diff --git a/man/references_geochem.Rd b/man/references_geochem.Rd new file mode 100644 index 0000000..79c6c44 --- /dev/null +++ b/man/references_geochem.Rd @@ -0,0 +1,38 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ASTR_data.R +\docType{data} +\name{references_geochem} +\alias{references_geochem} +\title{Reference compositions} +\format{ +A list including sets of chemical compositions often used as +reference composition for e.g. normalisation. Values are stored as named +vectors of their concentration with assigned units (using +\link[units]{set_units}). To retrieve the list of elements and their +concentrations for e.g. the PM, run \code{references_geochem$PM}. +\describe{ +\item{chondrite}{CI chondrite composition in ppm, as defined by Sun & +McDonough (1989)} +\item{PM}{Primitive mantle composition in ppm, as defined by Sun & +McDonough (1989)} +\item{NMORB}{Normal Mid-ocean ridge basalt composition in ppm, as defined +by Sun & McDonough (1989)} +\item{EMORB}{Enhanced Mid-Ocean Ridge Basalt composition in ppm, as +defined by Sun & McDonough (1989)} +\item{OIB}{Ocean Island Basalt composition in ppm, as defined by Sun & +McDonough (1989)} +} +} +\usage{ +references_geochem +} +\description{ +Reference compositions +} +\references{ +Sun, S.-S. & McDonough, W.F. (1989). Chemical and isotopic +systematics of oceanic basalts. Geological Society, London, Special +Publications 42, pp.313-345. Table 1, page 318. +\url{https://doi.org/10.1144/gsl.sp.1989.042.01.19}. +} +\keyword{datasets} diff --git a/man/standard_groups.Rd b/man/standard_groups.Rd new file mode 100644 index 0000000..885ab83 --- /dev/null +++ b/man/standard_groups.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ASTR_data.R +\docType{data} +\name{standard_groups} +\alias{standard_groups} +\title{Geochemical and other standard groups of elements, oxides, or isotopes} +\format{ +A list of sets of elements, oxides, or isotopes that represent +geochemical groups or other commonly used groups. To retrieve the full list +of e.g. the HFSE elements, run \code{standard_groups$HFSE}. +\describe{ +\item{REE}{Rare Earth Elements, taken from Periodic table of elements} +\item{HFSE}{High field-strength elements as listed in Salters (1998)} +\item{LILE}{Large-ion lithophile elements, as listed in Rudnick (1998)} +} +} +\usage{ +standard_groups +} +\description{ +Geochemical and other standard groups of elements, oxides, or isotopes +} +\references{ +Salters, V.J.M. (1998). Elements: High field strength. In: +Geochemistry. Encyclopedia of Earth Science. Springer, Dordrecht. +https://doi.org/10.1007/1-4020-4496-8_101 + +Rudnick, R.L. (1998). Elements: Large-ion lithophile. In: Geochemistry. +Encyclopedia of Earth Science. Springer, Dordrecht. +https://doi.org/10.1007/1-4020-4496-8_104 +} +\keyword{datasets} diff --git a/tests/testthat/test_normalise_geochem.R b/tests/testthat/test_normalise_geochem.R new file mode 100644 index 0000000..a6cc7d1 --- /dev/null +++ b/tests/testthat/test_normalise_geochem.R @@ -0,0 +1,61 @@ +test_that("normalise_geochem handles all cases", { + + df <- data.frame( + Sample = c("A", "B", "C", "D"), + La = c(10, NA, 5, 20), + Ce = c(20, 8, NA, 40), + Nd = c(15, 6, 3, 30), + X = c(1, 2, 3, 4) + ) + + # Run normalisation using internal reference data + result <- normalise_geochem(df, reference = "chondrite") + + # Check normalization math (using real reference values) + expect_equal(result$La_chondrite, units::drop_units(df$La / references_geochem$chondrite["La"])) + expect_equal(result$Ce_chondrite, units::drop_units(df$Ce / references_geochem$chondrite["Ce"])) + expect_equal(result$Nd_chondrite, units::drop_units(df$Nd / references_geochem$chondrite["Nd"])) + + # Check non-element columns unchanged + expect_equal(result$X, df$X) + expect_equal(result$Sample, df$Sample) + + # Check NA values preserved + expect_true(is.na(result$La[2])) + expect_true(is.na(result$Ce[3])) + + # Error when no matching elements + df_none <- data.frame(Sample = c("A", "B"), X = c(1, 2)) + expect_error( + normalise_geochem(df_none, reference = "chondrite"), + regexp = "does not include any element" + ) + + # Structure checks + expect_s3_class(result, "data.frame") + expect_equal(nrow(result), nrow(df)) + expect_equal(names(result), c(names(df), "La_chondrite", "Ce_chondrite", "Nd_chondrite")) +}) + +test_that("ASTR objects handled as intended", { + + suppressWarnings( + test_input <- read_ASTR( + system.file("extdata", "test_data_input_good.csv", package = "ASTR"), + id_column = "Sample", + context = 1:7 + ) + ) + + normalised <- normalise_geochem(test_input, "chondrite") + + expect_true("ASTR" %in% class(normalised)) + expect_equal(get_contextual_columns(normalised[1:8]), get_contextual_columns(test_input)) + expect_equal( + attributes(normalised[["Sb_chondrite"]]), + attributes(test_input[["Sample"]]) + ) + +}) + +