Introduction to TaxResolveR

Overview

TaxResolveR provides a reproducible workflow for preparing, resolving, assessing, reviewing, summarising, and documenting scientific names used in ecological and biodiversity datasets.

The package separates three related tasks: preparing scientific names for taxonomic resolution, obtaining matches from an external taxonomic source, and evaluating whether those matches should be accepted directly or reviewed manually.

This distinction is important because successful taxonomic resolution does not necessarily imply an exact species-level match.

Loading TaxResolveR

library(TaxResolveR)

For this introduction, consider a small vector containing valid species names, a genus placeholder, an empty value, and a missing value.

scientific_names <- c(
  "Homo sapiens",
  "  Parastacus brasiliensis  ",
  "Chilina sp.",
  "",
  NA_character_
)

scientific_names
#> [1] "Homo sapiens"                "  Parastacus brasiliensis  "
#> [3] "Chilina sp."                 ""                           
#> [5] NA

Scientific-name preparation

TaxResolveR provides several functions for inspecting and preparing scientific names before external taxonomic resolution.

Cleaning names

clean_scientific_names() performs basic cleaning while preserving missing and empty values.

clean_scientific_names(scientific_names)
#> [1] "Homo sapiens"            "Parastacus brasiliensis"
#> [3] "Chilina sp."             ""                       
#> [5] NA

Parsing names

parse_scientific_names() separates recognised components of scientific names and identifies the detected rank or placeholder structure.

parse_scientific_names(scientific_names)
#>                 original_name      genus specific_epithet infraspecific_rank
#> 1                Homo sapiens       Homo          sapiens               <NA>
#> 2   Parastacus brasiliensis   Parastacus     brasiliensis               <NA>
#> 3                 Chilina sp.    Chilina             <NA>               <NA>
#> 4                                   <NA>             <NA>               <NA>
#> 5                        <NA>       <NA>             <NA>               <NA>
#>   infraspecific_epithet     rank_detected
#> 1                  <NA>           species
#> 2                  <NA>           species
#> 3                  <NA> genus_placeholder
#> 4                  <NA>              <NA>
#> 5                  <NA>              <NA>

Classifying names

classify_scientific_names() combines parsing information with a structural classification of each input record.

classify_scientific_names(scientific_names)
#>                 original_name              clean_name      genus
#> 1                Homo sapiens            Homo sapiens       Homo
#> 2   Parastacus brasiliensis   Parastacus brasiliensis Parastacus
#> 3                 Chilina sp.             Chilina sp.    Chilina
#> 4                                                           <NA>
#> 5                        <NA>                    <NA>       <NA>
#>   specific_epithet infraspecific_rank infraspecific_epithet     rank_detected
#> 1          sapiens               <NA>                  <NA>           species
#> 2     brasiliensis               <NA>                  <NA>           species
#> 3             <NA>               <NA>                  <NA> genus_placeholder
#> 4             <NA>               <NA>                  <NA>              <NA>
#> 5             <NA>               <NA>                  <NA>              <NA>
#>            canonical_name parse_success     name_status
#> 1            Homo sapiens          TRUE valid_structure
#> 2 Parastacus brasiliensis          TRUE valid_structure
#> 3             Chilina sp.          TRUE     placeholder
#> 4                    <NA>         FALSE           empty
#> 5                    <NA>         FALSE         missing

For example, a conventional binomial can be classified as valid_structure, whereas Chilina sp. is recognised as a placeholder. Empty and missing inputs remain explicitly distinguishable.

Validating species-name structure

validate_species_names() provides simple structural indicators that are useful when screening input data.

validate_species_names(scientific_names)
#>                 original_name missing_name empty_name multiple_spaces binomial
#> 1                Homo sapiens        FALSE      FALSE           FALSE     TRUE
#> 2   Parastacus brasiliensis          FALSE      FALSE           FALSE     TRUE
#> 3                 Chilina sp.        FALSE      FALSE           FALSE    FALSE
#> 4                                    FALSE       TRUE           FALSE    FALSE
#> 5                        <NA>         TRUE      FALSE           FALSE    FALSE

Structural validation should not be interpreted as taxonomic resolution. For example, a genus placeholder is not a binomial species name but can still contain enough information to support a genus-level query.

Standardising names

standardize_scientific_names() combines cleaned and parsed information into a standardised representation.

standardize_scientific_names(scientific_names)
#>                 original_name              clean_name      genus
#> 1                Homo sapiens            Homo sapiens       Homo
#> 2   Parastacus brasiliensis   Parastacus brasiliensis Parastacus
#> 3                 Chilina sp.             Chilina sp.    Chilina
#> 4                                                           <NA>
#> 5                        <NA>                    <NA>       <NA>
#>   specific_epithet infraspecific_rank infraspecific_epithet     rank_detected
#> 1          sapiens               <NA>                  <NA>           species
#> 2     brasiliensis               <NA>                  <NA>           species
#> 3             <NA>               <NA>                  <NA> genus_placeholder
#> 4             <NA>               <NA>                  <NA>              <NA>
#> 5             <NA>               <NA>                  <NA>              <NA>
#>            canonical_name parse_success
#> 1            Homo sapiens          TRUE
#> 2 Parastacus brasiliensis          TRUE
#> 3             Chilina sp.          TRUE
#> 4                    <NA>         FALSE
#> 5                    <NA>         FALSE

Preparing taxonomic queries

prepare_taxonomic_queries() determines which records contain sufficient information for taxonomic resolution and constructs the corresponding query names.

prepare_taxonomic_queries(scientific_names)
#>                 original_name              clean_name      genus
#> 1                Homo sapiens            Homo sapiens       Homo
#> 2   Parastacus brasiliensis   Parastacus brasiliensis Parastacus
#> 3                 Chilina sp.             Chilina sp.    Chilina
#> 4                                                           <NA>
#> 5                        <NA>                    <NA>       <NA>
#>   specific_epithet infraspecific_rank infraspecific_epithet     rank_detected
#> 1          sapiens               <NA>                  <NA>           species
#> 2     brasiliensis               <NA>                  <NA>           species
#> 3             <NA>               <NA>                  <NA> genus_placeholder
#> 4             <NA>               <NA>                  <NA>              <NA>
#> 5             <NA>               <NA>                  <NA>              <NA>
#>            canonical_name parse_success     name_status              query_name
#> 1            Homo sapiens          TRUE valid_structure            Homo sapiens
#> 2 Parastacus brasiliensis          TRUE valid_structure Parastacus brasiliensis
#> 3             Chilina sp.          TRUE     placeholder                 Chilina
#> 4                    <NA>         FALSE           empty                    <NA>
#> 5                    <NA>         FALSE         missing                    <NA>
#>   query_ready
#> 1        TRUE
#> 2        TRUE
#> 3        TRUE
#> 4       FALSE
#> 5       FALSE

A placeholder such as Chilina sp. is therefore converted to the genus query Chilina, whereas empty and missing records are marked as not ready for querying.

Taxonomic resolution

resolve_taxonomy() sends prepared names to an external taxonomic source. GBIF is currently the implemented source.

A typical call is:

resolved <- resolve_taxonomy(
  c("Homo sapiens", "Homo sapens", "Chilina sp."),
  source = "gbif"
)

Because external taxonomic services require network access and can change over time, live GBIF queries are not evaluated while this vignette is built. This keeps package documentation reproducible and prevents temporary network or service problems from causing vignette-build failures.

Complete workflow with taxresolve()

For most users, taxresolve() is the main entry point. It combines taxonomic resolution, match assessment, and review flagging.

For example:

results <- taxresolve(
  c(
    "Homo sapiens",
    "Homo sapens",
    "Chilina sp.",
    "Parastacus brasiliensis"
  ),
  source = "gbif"
)

The returned data frame contains the taxonomic information supplied by the resolution stage together with fields describing resolution status, match quality, acceptance status, and whether manual review is recommended.

Reproducible offline example

The remainder of this vignette uses a fixed example representing a taxonomic-resolution result. This allows the assessment, review, summary, and reporting stages to be demonstrated without requiring network access.

The example includes an exact species match, a non-exact match, an exact genus-level match, another exact species match, two non-queryable records, and one unresolved query.

resolved_example <- data.frame(
  source = rep("gbif", 7),
  query_name = c(
    "Homo sapiens",
    "Homo sapens",
    "Chilina",
    "Parastacus brasiliensis",
    NA_character_,
    NA_character_,
    "Xyzabc nonexistenttaxon"
  ),
  matched_name = c(
    "Homo sapiens",
    "Homo sapiens",
    "Chilina",
    "Parastacus brasiliensis",
    NA_character_, NA_character_, NA_character_
  ),
  accepted_name = c(
    "Homo sapiens",
    "Homo sapiens",
    "Chilina",
    "Parastacus brasiliensis",
    NA_character_, NA_character_, NA_character_
  ),
  taxonomic_status = c(
    "ACCEPTED", "ACCEPTED", "ACCEPTED", "ACCEPTED",
    NA_character_, NA_character_, NA_character_
  ),
  rank = c(
    "SPECIES", "SPECIES", "GENUS", "SPECIES",
    NA_character_, NA_character_, NA_character_
  ),
  kingdom = rep(NA_character_, 7),
  phylum = rep(NA_character_, 7),
  class = rep(NA_character_, 7),
  order = rep(NA_character_, 7),
  family = rep(NA_character_, 7),
  genus = rep(NA_character_, 7),
  taxon_id = c(
    2436436, 2436436, 3243720, 2224027,
    NA_real_, NA_real_, NA_real_
  ),
  match_type = c(
    "EXACT", "VARIANT", "EXACT", "EXACT",
    NA_character_, NA_character_, NA_character_
  ),
  match_confidence = c(
    99, 95, 94, 99, NA_real_, NA_real_, NA_real_
  ),
  resolution_success = c(
    TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE
  ),
  stringsAsFactors = FALSE
)

resolved_example[c(
  "query_name",
  "matched_name",
  "rank",
  "match_type",
  "match_confidence",
  "resolution_success"
)]
#>                query_name            matched_name    rank match_type
#> 1            Homo sapiens            Homo sapiens SPECIES      EXACT
#> 2             Homo sapens            Homo sapiens SPECIES    VARIANT
#> 3                 Chilina                 Chilina   GENUS      EXACT
#> 4 Parastacus brasiliensis Parastacus brasiliensis SPECIES      EXACT
#> 5                    <NA>                    <NA>    <NA>       <NA>
#> 6                    <NA>                    <NA>    <NA>       <NA>
#> 7 Xyzabc nonexistenttaxon                    <NA>    <NA>       <NA>
#>   match_confidence resolution_success
#> 1               99               TRUE
#> 2               95               TRUE
#> 3               94               TRUE
#> 4               99               TRUE
#> 5               NA              FALSE
#> 6               NA              FALSE
#> 7               NA              FALSE

Assessing taxonomic matches

assess_taxonomic_match() interprets the resolution output and adds three fields: resolution_status, match_quality, and accepted_status.

assessed_example <- assess_taxonomic_match(resolved_example)

assessed_example[c(
  "query_name",
  "resolution_status",
  "match_quality",
  "accepted_status"
)]
#>                query_name resolution_status match_quality accepted_status
#> 1            Homo sapiens          resolved          high        accepted
#> 2             Homo sapens          resolved      moderate        accepted
#> 3                 Chilina          resolved          high        accepted
#> 4 Parastacus brasiliensis          resolved          high        accepted
#> 5                    <NA>     not_queryable          <NA>            <NA>
#> 6                    <NA>     not_queryable          <NA>            <NA>
#> 7 Xyzabc nonexistenttaxon        unresolved    unresolved      unresolved

A high-quality match should not automatically be interpreted as a species-level identification. In this example, Chilina is an exact genus-level match and is therefore distinct from an exact species-level match.

Flagging records for manual review

flag_taxonomic_review() adds review_required and review_reason.

reviewed_example <- flag_taxonomic_review(assessed_example)

reviewed_example[c(
  "query_name",
  "resolution_status",
  "match_quality",
  "review_required",
  "review_reason"
)]
#>                query_name resolution_status match_quality review_required
#> 1            Homo sapiens          resolved          high           FALSE
#> 2             Homo sapens          resolved      moderate            TRUE
#> 3                 Chilina          resolved          high           FALSE
#> 4 Parastacus brasiliensis          resolved          high           FALSE
#> 5                    <NA>     not_queryable          <NA>            TRUE
#> 6                    <NA>     not_queryable          <NA>            TRUE
#> 7 Xyzabc nonexistenttaxon        unresolved    unresolved            TRUE
#>     review_reason
#> 1            <NA>
#> 2 non_exact_match
#> 3            <NA>
#> 4            <NA>
#> 5   not_queryable
#> 6   not_queryable
#> 7      unresolved

This separates successful resolution from records that still warrant attention. Non-exact matches, unresolved queries, and non-queryable inputs can therefore be documented explicitly rather than silently discarded.

Summarising results

summarize_taxonomic_resolution() returns a compact one-row summary of the main resolution, match-quality, acceptance, and review counts.

summary_example <- summarize_taxonomic_resolution(reviewed_example)
summary_example
#>   total_names resolved unresolved not_queryable high_quality moderate_quality
#> 1           7        4          1             2            3                1
#>   low_quality accepted not_accepted review_required
#> 1           0        4            0               4

Detailed reporting

taxonomic_resolution_report() produces a structured report containing the total summary together with category counts and proportions.

report_example <- taxonomic_resolution_report(reviewed_example)

report_example$total_summary
#>   total_names resolved unresolved not_queryable high_quality moderate_quality
#> 1           7        4          1             2            3                1
#>   low_quality accepted not_accepted review_required
#> 1           0        4            0               4
report_example$resolution_status
#>        category n proportion
#> 1      resolved 4  0.5714286
#> 2    unresolved 1  0.1428571
#> 3 not_queryable 2  0.2857143
report_example$match_quality
#>        category n proportion
#> 1          high 3  0.4285714
#> 2      moderate 1  0.1428571
#> 3           low 0  0.0000000
#> 4    unresolved 1  0.1428571
#> 5 not_queryable 0  0.0000000
report_example$review_reasons
#>                     category n proportion
#> 1            non_exact_match 1  0.1428571
#> 2      low_information_match 0  0.0000000
#> 3               not_accepted 0  0.0000000
#> 4 uncertain_taxonomic_status 0  0.0000000
#> 5                 unresolved 1  0.1428571
#> 6              not_queryable 2  0.2857143
#> 7   insufficient_information 0  0.0000000

The proportions in the detailed tables use the total number of input records as their denominator.

Exporting results

export_taxonomic_results() can write the complete reviewed data and associated summary tables to CSV files.

A typical export is:

export_taxonomic_results(
  reviewed_example,
  path = "taxonomic_results"
)

The function creates five files: taxonomic_results.csv, taxonomic_summary.csv, resolution_status.csv, match_quality.csv, and review_reasons.csv. Existing managed output files are not overwritten unless overwrite = TRUE is requested.

The export example is not evaluated while building this vignette because documentation should not create persistent user-facing output directories as a side effect.

Handling problematic input

TaxResolveR is designed to retain problematic records rather than silently remove them. Missing and empty inputs can remain in the workflow as non-queryable records, while syntactically queryable names that cannot be resolved can be retained as unresolved records.

Duplicate names can also be retained in the returned data so that row-level correspondence with the original input is preserved.

This behaviour supports data auditing because unresolved or incomplete taxonomic information remains visible in downstream summaries and review flags.

Further documentation

Individual functions are documented through the standard R help system. For example:

?taxresolve
?resolve_taxonomy
?assess_taxonomic_match
?flag_taxonomic_review
?taxonomic_resolution_report