This article resolves a short list of plant names to accepted names, joins a trait table and two published datasets onto the result, and records which backbone versions produced it. It assumes basic R and data frames. At the end you have a data frame with one row per name you kept, its accepted name, how it was matched, and the attached traits.
Match the names with
taxify().
Check the matches that need a decision with
summary() and inspect().
Join your own data with add_data(),
and published data with an add_<source>()
function.
Record the data versions with
cite() and taxify_lock().
install.packages("taxify")
# or the development version
# install.packages("pak")
pak::pak("gcol33/taxify")taxify matches names against backbones: published taxonomic databases
such as the Catalogue of Life (COL), the GBIF Backbone Taxonomy and the
Integrated Taxonomic Information System (ITIS), stored as files on your
disk. The first call to taxify() downloads COL, GBIF and
ITIS, about 4 GB, into taxify_data_dir(). Later calls read
these files and need no network.
The example list has nine names as they might come from field sheets.
Each one reaches its accepted name by a different route: an author
abbreviation, a typo, a field qualifier (cf.), capitals, an
abbreviated genus, two old synonyms, a second typo, and a name that
exists in no backbone.
field_names <- c(
"Quercus robur L.", "Quercus robus", "cf. Betula pendula",
"FAGUS SYLVATICA", "Q. petraea", "Pinus abies",
"Fallopia japonica", "Festuca rubrra", "Quercus fantasia"
)
res <- taxify(field_names, verbose = FALSE)
#> Warning: 1 of 9 names resolve to more than one accepted ID (Pinus abies).
#> `accepted_id` holds one and `accepted_ids` all of them; list them with
#> taxify_ids() before requesting data by ID.
res[, c("input_name", "accepted_name", "match_type", "is_synonym",
"fuzzy_dist")]
#> input_name accepted_name match_type is_synonym fuzzy_dist
#> 1 Quercus robur L. Quercus robur exact FALSE NA
#> 2 Quercus robus Quercus robur fuzzy FALSE 0.07692308
#> 3 cf. Betula pendula Betula pendula exact FALSE NA
#> 4 FAGUS SYLVATICA Fagus sylvatica exact_ci FALSE NA
#> 5 Q. petraea Quercus petraea abbrev FALSE NA
#> 6 Pinus abies Picea abies exact TRUE NA
#> 7 Fallopia japonica Reynoutria japonica exact TRUE NA
#> 8 Festuca rubrra Festuca rubra fuzzy FALSE 0.07142857
#> 9 Quercus fantasia Quercus glauca fuzzy TRUE 0.18750000
#> Sources: COL 2026.09 | cite() for full citationstaxify() returns one row per input name, in input order,
with input_name unchanged. accepted_name is
the name the backbone currently accepts for the taxon, and
match_type says how the input reached it:
exact: the cleaned name equals a backbone name
(Quercus robur L.,
cf. Betula pendula).
exact_ci: equal after ignoring capitalisation
(FAGUS SYLVATICA).
abbrev: the genus initial and the epithet identify
one species (Q. petraea).
fuzzy: the closest backbone name of the same genus
within the threshold (Quercus robus).
Cleaning removes authors and qualifiers before matching; the
qualifier is kept in the qualifier column.
fuzzy_dist is the edit distance divided by the name length:
one changed letter in Quercus robus gives 0.077.
Pinus abies and Fallopia japonica are
synonyms: the backbone holds them, and points from them to the accepted
names Picea abies and Reynoutria japonica.
Quercus fantasia exists in no backbone. Its epithet is
close enough to a real name, Quercus matasii, to fall within
the default fuzzy threshold, and that name is itself a synonym, so the
row ends at Quercus glauca. A fuzzy match is a candidate to
check, and inspect() flags it in Checking the matches.
The warning names the input that matched a spelling with more than
one accepted taxon behind it. The pick is in accepted_name
and accepted_id; n_ids counts the candidates
and accepted_ids lists them.
Names often arrive as a column of a data frame. taxify()
takes a character vector of names, such as that column:
survey <- data.frame(plot = 1:2, species = c("Quercus robur", "Fagus sylvatica"))
taxify(survey)
#> Error:
#> ! x must be a character vectorPassing the column instead of the data frame matches both names:
taxify(survey$species, verbose = FALSE)[, c("input_name", "accepted_name")]
#> input_name accepted_name
#> 1 Quercus robur Quercus robur
#> 2 Fagus sylvatica Fagus sylvatica
#> Sources: COL 2026.09 | cite() for full citationsA backbone name has to be one of those list_backbones()
reports. A misspelt one stops with the list of valid names:
taxify("Quercus robur", backbone = "catalogue of life")
#> Error:
#> ! Unknown backbone 'catalogue of life'. Available: wfo, col, colxr, gbif, itis, ncbi, ott, worms, euromed, fungorum, algaebase, fishbase, sealifebase, reptiledb, lcvp, wcvp, mdd, avilist, lpsn, evc, vegczWith a name from that list, the call runs:
Before analysing res, look at the matches that were not
exact. summary() counts how the batch resolved and names
the backbone version.
summary(res)
#> ── taxify results ────────────────────────────────────────────────────────────
#> backbone: COL v2026.09 + GBIF + ITIS | 9 names submitted
#>
#> matched 9 (exact: 4, case-insensitive: 1, fuzzy: 3, abbrev: 1)
#> multiple ids 1 (more than one accepted ID; see taxify_ids())
#> ────────────────────────────────────────────────────────────
#> taxon groups: angiosperm: 8 gymnosperm: 1inspect() returns only the rows that need a look, each
with the name to use, what stood out and a tier: review for
a decision you have to make, note for a change you can
accept as it is.
inspect(res)
#> ── taxify inspection ──────────────────────────────────────────
#> 9 names inspected | 6 with anomalies
#> backbones: col, gbif, itis
#> review: 4 note: 2
#> ────────────────────────────────────────────────────────────
#> [review] Festuca rubrra -> Festuca rubra likely misspelling
#> [review] Pinus abies -> Picea abies ambiguous (targets: 4HPZF|8K9Y|6BRMR|4HQ4F); outdated synonym
#> [review] Quercus fantasia -> Quercus glauca likely misspelling; range outside the list's main area (EUROPE); outdated synonym
#> [review] Quercus robus -> Quercus robur likely misspelling
#> [note ] FAGUS SYLVATICA -> Fagus sylvatica case mismatch
#> [note ] Fallopia japonica -> Reynoutria japonica outdated synonymBoth typos and the invented name are marked as likely misspellings;
Quercus fantasia is also outside the area where the rest of
the list occurs. Set aside the rows you do not accept and keep the
others:
rejected <- c("Quercus fantasia")
kept <- res[!res$input_name %in% rejected, ]
kept[, c("input_name", "accepted_name", "family")]
#> input_name accepted_name family
#> 1 Quercus robur L. Quercus robur Fagaceae
#> 2 Quercus robus Quercus robur Fagaceae
#> 3 cf. Betula pendula Betula pendula Betulaceae
#> 4 FAGUS SYLVATICA Fagus sylvatica Fagaceae
#> 5 Q. petraea Quercus petraea Fagaceae
#> 6 Pinus abies Picea abies Pinaceae
#> 7 Fallopia japonica Reynoutria japonica Polygonaceae
#> 8 Festuca rubrra Festuca rubra Poaceae
#> Sources: COL 2026.09 | cite() for full citationskept is the table most analyses start from: every input
that survived your check, with one accepted name per row.
Trait tables write names the way their authors did, and usually
arrive as a file. The table below uses the synonym Fallopia
japonica where kept has Reynoutria japonica,
so a join on the name columns would miss that row. It is written to a
temporary CSV file first, with the names in a column called
latin_name, so the example reads a file the way your own
data would be read:
heights <- data.frame(
latin_name = c("Fallopia japonica", "Picea abies", "Betula pendula"),
height_m = c(3, 50, 25)
)
heights_csv <- tempfile(fileext = ".csv")
write.csv(heights, heights_csv, row.names = FALSE)add_data() reads the file, matches the names in
latin_name against the same backbones as kept,
and joins on the accepted name:
with_height <- add_data(kept, heights_csv, species_col = "latin_name")
#> Matching 3 names from 'latin_name' through COL + GBIF + ITIS backbone...
#> Matching 3 names against 3 backbones: col -> gbif -> itis
#> [col] Matching 3 names...
#> [gbif] Skipped (all names matched)
#> [itis] Skipped (all names matched)
#> add_data: 3 of 8 species matched (37.5%). 0 names in data unmatched.
with_height[, c("input_name", "accepted_name", "height_m")]
#> input_name accepted_name height_m
#> 1 Quercus robur L. Quercus robur NA
#> 2 Quercus robus Quercus robur NA
#> 3 cf. Betula pendula Betula pendula 25
#> 4 FAGUS SYLVATICA Fagus sylvatica NA
#> 5 Q. petraea Quercus petraea NA
#> 6 Pinus abies Picea abies 50
#> 7 Fallopia japonica Reynoutria japonica 3
#> 8 Festuca rubrra Festuca rubra NA
#> Sources: COL 2026.09 | cite() for full citationsThe Fallopia japonica row of the file joined onto
Reynoutria japonica in kept, because both sides
were matched to the accepted name first. A row of kept with
no counterpart in the file gets NA. For your own file, only
the path and species_col change; .tsv,
.xlsx and data frames are read the same way, as described
in Joining your own data.
Traits you did not measure can come from published datasets. Each one
taxify can attach has its own function, named after its source.
add_zanne() adds the woodiness compiled by Zanne et al. (2014), and
add_eive() the Ecological Indicator Values for Europe
(EIVE, Dengler et al. 2023). Both join on the
accepted name the same way add_data() does, and each is
downloaded on first use.
traits <- kept |> add_zanne() |> add_eive()
#> add_zanne(): attaching 1 of 2 columns. Pass cols = "all" for all, or see enrichment_cols("zanne").
#> add_eive(): attaching 5 of 18 columns. Pass cols = "all" for all, or see enrichment_cols("eive").
traits[, c("accepted_name", "woodiness", "eive_light", "eive_moisture")]
#> accepted_name woodiness eive_light eive_moisture
#> 1 Quercus robur woody 6.241077 4.802554
#> 2 Quercus robur woody 6.241077 4.802554
#> 3 Betula pendula woody 6.840881 4.445677
#> 4 Fagus sylvatica woody 3.003906 4.570789
#> 5 Quercus petraea woody 5.826045 4.200698
#> 6 Picea abies woody 4.362537 4.976672
#> 7 Reynoutria japonica <NA> 7.278168 6.042765
#> 8 Festuca rubra herbaceous 7.137641 4.686280
#> Sources: COL 2026.09, Zanne et al. 2014 2026.08, EIVE 1.0 2026.08 | cite() for full citationsThe message of each function says how many of the source’s columns
were attached; cols = "all" attaches the rest.
list_enrichments() lists every dataset, and
add_trait() gathers one trait from all sources that carry
it, as described in Attaching published
data.
A methods section, and a rerun on another machine, both need the data versions behind a result. Every row carries the backbone that matched it and that backbone’s version.
unique(traits[, c("backbone", "backbone_version")])
#> backbone backbone_version
#> 1 col col:2026.09 (2026-09-14)
#> Sources: COL 2026.09, Zanne et al. 2014 2026.08, EIVE 1.0 2026.08 | cite() for full citationscite() prints the reference for each backbone and
dataset in a result, for a methods section. taxify_lock()
writes the same versions with a checksum of each file to a JSON file;
taxify_restore() reads it on another machine and reinstalls
those exact versions.
cite(traits)
#> ── taxify citations ────────────────────────────────────────────────────────────
#> [1] Colling G (2026). taxify: Offline Taxonomic Name Matching Against Darwin Core Backbones (version 0.7.0). doi:10.32614/CRAN.package.taxify
#> [2] Banki O, Roskov Y, Doring M, Ower G, Hernandez Robles DR, Plata Corredor CA, Stjernegaard Jeppesen T, Orrell TM, Pugh D, Kostichka J, et al. (2024). Catalogue of Life Checklist. doi:10.48580/d4t2
#> [3] Zanne AE, Tank DC, Cornwell WK, Eastman JM, Smith SA, FitzJohn RG, McGlinn DJ, O'Meara BC, Moles AT, Reich PB, et al. (2014). Three keys to the radiation of angiosperms into freezing environments. Nature 506:89-92. doi:10.1038/nature12872
#> [4] Dengler J, Jansen F, Chusova O, Hullbusch E, Novak P, Van Meerbeek K, Axmanova I, Bruun HH, Chytry M, Guarino R, et al. (2023). Ecological Indicator Values for Europe (EIVE) 1.0. Vegetation Classification and Survey 4:7-29. doi:10.3897/VCS.98324
#> ────────────────────────────────────────────────────────────taxify_lock() writes the versions to a file;
content_id is each file’s checksum:
lock <- tempfile(fileext = ".json")
taxify_lock(traits, lock)
writeLines(readLines(lock))
#> {
#> "taxify_version": "0.7.0",
#> "created": "2026-10-13",
#> "r_version": "R version 4.6.0 (2026-04-24 ucrt)",
#> "backbones": [
#> {
#> "name": "col",
#> "version": "2026.09",
#> "content_id": "c2999654decfc2a45771631a59ca7ba5",
#> "downloaded_at": "2026-09-14",
#> "installed": true
#> }
#> ],
#> "enrichments": [
#> {
#> "name": "zanne",
#> "source": "Zanne et al. 2014",
#> "version": "2026.08",
#> "license": "CC0",
#> "content_id": "0caefc8d2229f59f6bafac436b3d8dd0",
#> "downloaded_at": "2026-09-23",
#> "installed": true
#> },
#> {
#> "name": "eive",
#> "source": "EIVE 1.0",
#> "version": "2026.08",
#> "license": "CC BY 4.0",
#> "content_id": "3ab5bb17891b3e182fb6a4a38efae32a",
#> "downloaded_at": "2026-09-23",
#> "installed": true
#> }
#> ]
#> }Choosing and combining backbones: the 19 backbones of taxa, fallback chains, downloads and version pinning.
Checking a name list: every
label inspect() reports, and reconcile() for a
checklist.
How names are matched:
cleaning, the fuzzy threshold, homonyms and the n_ids
columns.
Attaching published data and Joining your own data: the two kinds of join in full.
Compatibility with taxize and WorldFlora: agreement with WorldFlora and the function mapping.