diff --git a/NAMESPACE b/NAMESPACE index 3435416..853f7b4 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,5 +14,6 @@ export(standardise_taxon_rank) export(state_diversity_counts) export(strip_names) export(strip_names_extra) +export(synonyms_for_accepted_names) export(update_taxonomy) importFrom(dplyr,"%>%") diff --git a/NEWS.md b/NEWS.md index 58c285e..ce4a01c 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,8 +1,11 @@ # APCalign 1.2.0 +- New function `synonyms_for_accepted_names()` to list synonyms for currently accepted taxon names. - `load_taxonomic_resources()` now caches results in memory for the duration of the R session, so repeated calls with the same version return immediately without re-downloading or re-processing data. - New function `clear_cached_resources()` to remove the session cache and force a reload. - `load_taxonomic_resources()` now works offline when parquet files have been previously downloaded; `default_version()` falls back to the most recently cached local version when no internet connection is available. +- Internal taxonomic resource tables renamed to snake_case; `family` column added to resource tables. + # APCalign 1.1.6 - Fix issue #262: filter to accepted species only in genus-family lookup diff --git a/R/APCalign-package.R b/R/APCalign-package.R index 77bf461..1a715af 100644 --- a/R/APCalign-package.R +++ b/R/APCalign-package.R @@ -100,7 +100,10 @@ utils::globalVariables( "alternative_accepted_name_tmp", "pro_parte", "suggested_collapsed_name", - "versions" + "versions", + "name_with_status", + "synonyms", + "taxon_name" ) ) diff --git a/R/synonyms_for_accepted_names.R b/R/synonyms_for_accepted_names.R new file mode 100644 index 0000000..fc7cc95 --- /dev/null +++ b/R/synonyms_for_accepted_names.R @@ -0,0 +1,115 @@ +#' @title Synonyms for Currently Accepted Names +#' +#' @description +#' This function generates lists a string of synonyms for currently accepted names to facilitate working out past names of a taxon +#' when the current name is known +#' +#' @param accepted_names A character vector of currently accepted taxon names to look up synonyms for. +#' @param collapse Offering the option to return a long data table with each synonym in its own row, +#' versus collapsed into a vector for each accepted name +#' @param resources Taxonomic resources loaded via [load_taxonomic_resources()]. +#' +#' @returns A table with the currently accepted name and columns documenting all synonyms and all synonyms with taxonomic status. +#' @export +#' +#' @examples +#' synonyms_for_accepted_names( +#' accepted_names = c("Justicia tenella", "Acacia aneura"), +#' collapse = TRUE +#' ) +#' +synonyms_for_accepted_names <- function(accepted_names, collapse = TRUE, resources = load_taxonomic_resources()) { + + if(is.null(resources)){ + message("Not finding taxonomic resources; check internet connection?") + return(NULL) + } + + # generate list of accepted_name_usage_ID's for accepted species + accepted_names_with_usageID <- resources$APC_accepted |> + dplyr::select(accepted_name_usage_ID, accepted_name = canonical_name) |> + dplyr::filter(accepted_name %in% accepted_names) + + # preferred order of taxonomic updates (function from `update_taxonomy.R`) + relevel_taxonomic_status_preferred_order <- function(taxonomic_status) { + + preferred_order <- + c( + "accepted", + "taxonomic synonym", + "basionym", + "nomenclatural synonym", + "isonym", + "orthographic variant", + "common name", + "doubtful taxonomic synonym", + "replaced synonym", + "doubtful pro parte taxonomic synonym", + "pro parte nomenclatural synonym", + "pro parte taxonomic synonym", + "pro parte misapplied", + "misapplied", + "unplaced", + "excluded", + "doubtful misapplied", + "doubtful pro parte misapplied", + "included" + ) + + factor(taxonomic_status, levels = + subset( + preferred_order, + preferred_order %in% taxonomic_status + ) + ) + } + + # generate list of accepted_name_usage_ID's for accepted species + APC_synonyms_tmp <- resources$APC |> + dplyr::filter(taxon_rank %in% c("species", "variety", "form", "subspecies")) |> + # merge currently accepted names for each taxon onto all the synonyms + dplyr::right_join(accepted_names_with_usageID, by = "accepted_name_usage_ID") |> + dplyr::select(canonical_name, taxonomic_status, accepted_name, accepted_name_usage_ID) |> + # remove the accepted names themselves + dplyr::filter(taxonomic_status != "accepted") |> + dplyr::mutate( + taxonomic_status = (relevel_taxonomic_status_preferred_order(taxonomic_status)), + ) |> + dplyr::distinct(accepted_name, canonical_name, .keep_all = TRUE) |> + dplyr::arrange(accepted_name, taxonomic_status, taxonomic_status) + + + if(collapse == TRUE) { + # Generate list of delimited synonyms and their taxonomic status + APC_synonyms <- APC_synonyms_tmp |> + dplyr::group_by(accepted_name, accepted_name_usage_ID) |> + dplyr::mutate( + name_with_status = paste0(canonical_name, " (", taxonomic_status, ")"), + synonyms = paste0(name_with_status, collapse = "; ") + ) |> + dplyr::ungroup() |> + dplyr::distinct(accepted_name_usage_ID, synonyms) + + accepted_names_with_synonyms <- resources$APC |> + dplyr::select(canonical_name, taxon_rank, name_type, genus, family, scientific_name, accepted_name_usage_ID) |> + dplyr::filter(canonical_name %in% accepted_names_with_usageID$accepted_name & accepted_name_usage_ID %in% accepted_names_with_usageID$accepted_name_usage_ID) |> + dplyr::distinct(canonical_name, .keep_all = T) |> + dplyr::left_join(APC_synonyms, by = "accepted_name_usage_ID") |> + dplyr::rename(taxon_name = canonical_name) |> + dplyr::arrange(family, taxon_name) + + } else { + + # Create a long list if collapse = F, with one row per synonym + accepted_names_with_synonyms <- resources$APC |> + dplyr::select(canonical_name, taxon_rank, name_type, genus, family, scientific_name, accepted_name_usage_ID) |> + dplyr::filter(canonical_name %in% accepted_names_with_usageID$accepted_name & accepted_name_usage_ID %in% accepted_names_with_usageID$accepted_name_usage_ID) |> + dplyr::distinct(canonical_name, .keep_all = T) |> + dplyr::select(-canonical_name) |> + dplyr::left_join(APC_synonyms_tmp, by = "accepted_name_usage_ID") |> + dplyr::arrange(family, accepted_name) + } + + accepted_names_with_synonyms + +} diff --git a/_pkgdown.yml b/_pkgdown.yml index 51c1176..e169129 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -37,6 +37,9 @@ reference: - standardise_taxon_rank - strip_names - strip_names_extra +- subtitle: Synonyms for accepted names +- contents: + - synonyms_for_accepted_names - subtitle: Established status across states/territories - contents: - create_species_state_origin_matrix diff --git a/man/APCalign.Rd b/man/APCalign.Rd index 4a6d4b6..e9b9e7e 100644 --- a/man/APCalign.Rd +++ b/man/APCalign.Rd @@ -46,11 +46,11 @@ Useful links: } \author{ -\strong{Maintainer}: Daniel Falster \email{daniel.falster@unsw.edu.au} (\href{https://orcid.org/0000-0002-9814-092X}{ORCID}) [copyright holder] +\strong{Maintainer}: Elizabeth Wenk \email{e.wenk@unsw.edu.au} (\href{https://orcid.org/0000-0001-5640-5910}{ORCID}) [copyright holder] Authors: \itemize{ - \item Elizabeth Wenk \email{e.wenk@unsw.edu.au} (\href{https://orcid.org/0000-0001-5640-5910}{ORCID}) [contributor] + \item Daniel Falster \email{daniel.falster@unsw.edu.au} (\href{https://orcid.org/0000-0002-9814-092X}{ORCID}) [contributor] \item Will Cornwell \email{w.cornwell@unsw.edu.au} (\href{https://orcid.org/0000-0003-4080-4073}{ORCID}) [contributor] \item Fonti Kar \email{f.kar@unsw.edu.au} (\href{https://orcid.org/0000-0002-2760-3974}{ORCID}) [contributor] } diff --git a/man/synonyms_for_accepted_names.Rd b/man/synonyms_for_accepted_names.Rd new file mode 100644 index 0000000..d230cdf --- /dev/null +++ b/man/synonyms_for_accepted_names.Rd @@ -0,0 +1,34 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/synonyms_for_accepted_names.R +\name{synonyms_for_accepted_names} +\alias{synonyms_for_accepted_names} +\title{Synonyms for Currently Accepted Names} +\usage{ +synonyms_for_accepted_names( + accepted_names, + collapse = TRUE, + resources = load_taxonomic_resources() +) +} +\arguments{ +\item{accepted_names}{A character vector of currently accepted taxon names to look up synonyms for.} + +\item{collapse}{Offering the option to return a long data table with each synonym in its own row, +versus collapsed into a vector for each accepted name} + +\item{resources}{Taxonomic resources loaded via \code{\link[=load_taxonomic_resources]{load_taxonomic_resources()}}.} +} +\value{ +A table with the currently accepted name and columns documenting all synonyms and all synonyms with taxonomic status. +} +\description{ +This function generates lists a string of synonyms for currently accepted names to facilitate working out past names of a taxon +when the current name is known +} +\examples{ +synonyms_for_accepted_names( + accepted_names = c("Justicia tenella", "Acacia aneura"), + collapse = TRUE +) + +} diff --git a/tests/testthat/test-operation_outputs.R b/tests/testthat/test-operation_outputs.R index 7daeabb..7a43135 100644 --- a/tests/testthat/test-operation_outputs.R +++ b/tests/testthat/test-operation_outputs.R @@ -296,3 +296,35 @@ test_that("No warnings if trying to match input name to empty accepted name set. ), NA) } ) + +test_that("synonyms_for_accepted_names outputs expected number of rows", { + + expect_silent( + x <- synonyms_for_accepted_names( + accepted_names = c("Justicia tenella", "Acacia aneura"), + collapse = TRUE, resources = resources + ) + ) + + expect_equal( + nrow(x), + 2 + ) + + expect_silent( + x <- synonyms_for_accepted_names( + accepted_names = c("Justicia tenella", "Acacia aneura"), + collapse = FALSE, resources = resources + ) + ) + + # currently there are 9 rows, but this can increase with additional synonyms being added + expect_gte( + nrow(x), + 8 + ) + + expect_contains(x$canonical_name, "Racosperma aneurum") + +} +)