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"]])
+ )
+
+})
+
+