Fuzzy matching corrects a typo by finding the nearest real name. Most of the time the nearest name is the one the recorder meant, but two species can sit a single edit apart while living on different continents. A recorder working in Belgium who writes a slightly misspelled name meant a Belgian plant, not its one-letter neighbour from New Zealand. The string distance alone cannot tell the two apart; the geography can.
taxify() takes a region argument for
exactly this. When you set it, taxify() prefers the fuzzy
candidates that actually occur where you work and sets the others aside.
It never touches an exact match, so declaring a region only ever changes
which spelling correction wins, never a name that was already right.
The filter rests on two range sources. For vascular plants it uses
WCVP, the World Checklist of Vascular Plants, which records where each
accepted species occurs by TDWG botanical region. For marine taxa it
uses WoRMS distribution records rolled up to Marine Ecoregions of the
World. taxify() resolves your region input to
codes in whichever vocabulary it belongs to, looks the candidate fuzzy
names up in the matching source, and drops an out-of-region candidate
when a better one survives. Three rules keep it conservative:
So the constraint is a soft preference. It breaks ties toward local species and otherwise stays out of the way.
The clearest input is a name. The bundled WGSRPD crosswalk accepts botanical regions at three levels, so a country, a sub-continental region, or a continent all work, case- and accent-insensitively.
#> input_name accepted_name family match_type fuzzy_dist backbone
#> 1 Gentiana acaulis Gentiana acaulis Gentianaceae exact NA WFO
The exact match comes back unchanged, since the region never touches one. The constraint earns its keep on the fuzzy names in the same call: when a typo has two corrections a single edit apart and only one of them grows in Europe, the European one wins the tie. A name with no such conflict resolves exactly as it would without a region.
"Europe" is a Level 1 region and expands to every
European code; "Middle Europe" is a Level 2 region;
"Belgium" is a single Level 3 country. You can pass
several, and they union:
A three-letter token is read as a TDWG code directly, so
region = "BGM" (Belgium) and
region = "Belgium" reach the same place. An unrecognised
region is dropped with a warning rather than failing the call, and a
code that matches no WCVP record simply makes the filter a no-op, so a
typo in the region degrades gracefully instead of producing wrong
matches.
When the data carry coordinates, hand them over directly. A point is mapped to its botanical region by point-in-polygon against the WGSRPD Level 3 boundaries, and the resulting codes are used the same way a region name would be.
The order is c(lon, lat). A single point, a two-column
matrix or data.frame of points, or a point-geometry spatial object all
work; an sf object or a terra SpatVector is
reprojected to longitude/latitude on the way in. Points and a
region name can be combined, and their regions union.
occ <- data.frame(
lon = c(4.35, 5.12, 4.40),
lat = c(50.85, 51.21, 50.50)
)
taxify(field_names, coords = occ)The boundary file downloads once and stays cached. By default the
lookup runs a native ray-casting test, so no spatial package is
required. With terra or sf installed taxify uses that instead, which is
faster on large point sets, and
options(taxify.pip_engine = "terra" | "sf" | "native")
forces the choice.
By default any WCVP record counts as in-region, native or introduced
alike. The range argument narrows that.
# only count regions where WCVP lists the species as native
taxify(field_names, region = "Europe", range = "native")
# only introduced occurrences
taxify(field_names, region = "Europe", range = "introduced")range = "present" is the default and the most
permissive. "native" is stricter and suits work that should
ignore naturalised populations; a species present in your region only as
an introduction will not satisfy it, and its out-of-region native
correction can lose the tie. "introduced" is the mirror
image, for invasion work that wants the alien records specifically. The
argument is ignored when no region is set.
taxify_regions() returns the regions
region= accepts so you can find the right code or confirm a
name resolves. With no argument it lists both vocabularies; with a
search term it filters, matching the code and all three level names. The
scheme column names the vocabulary a row belongs to, and
scheme= restricts the listing to one of them.
#> code name level2_name level1_name scheme
#> 1 BGM Belgium Middle Europe EUROPE wgsrpd
# every code Europe expands to
nrow(taxify_regions("Europe"))
#> [1] 41
# browse the full table
head(taxify_regions())The same crosswalk powers add_wcvp(), so the botanical
codes here are the ones that appear in native-range enrichment
output.
Marine names use the second range source: WoRMS distribution records
rolled up to Marine Ecoregions of the World. It arrives with the
marine_distribution asset, which downloads on first use,
and from then on region= takes MEOW names and codes the
same way it takes botanical ones.
#> input_name accepted_name family match_type backbone
#> 1 Carcinus maenus Carcinus maenas Carcinidae fuzzy col
#> 2 Gadus morhua Gadus morhua Gadidae exact col
An ecoregion name resolves to its own code. A province or realm name expands to its member ecoregions, the way a TDWG Level 1 continent expands to its Level 3 codes.
#> code name level2_name
#> 1 25042 Carolinian Warm Temperate Northwest Atlantic
#> 2 20043 Northern Gulf of Mexico Warm Temperate Northwest Atlantic
#> 3 25040 Gulf of Maine/Bay of Fundy Cold Temperate Northwest Atlantic
#> 4 20041 Virginian Cold Temperate Northwest Atlantic
#> 5 20030 Adriatic Sea Mediterranean Sea
#> 6 25033 Tunisian Plateau/Gulf of Sidra Mediterranean Sea
#> level1_name scheme
#> 1 Temperate Northern Atlantic meow
#> 2 Temperate Northern Atlantic meow
#> 3 Temperate Northern Atlantic meow
#> 4 Temperate Northern Atlantic meow
#> 5 Temperate Northern Atlantic meow
#> 6 Temperate Northern Atlantic meow
Coordinates behave the same way. A point at sea maps to the MEOW
ecoregion containing it, and taxify() unions that with the
botanical lookup, so a single coords argument serves a list
holding both plants and marine animals.
MEOW maps coastal and shelf waters, so a point over a deep ocean basin belongs to no ecoregion. The filter treats that as absence of data and leaves those names alone.
The two sources cover vascular plants and marine taxa. A name outside both scopes has no range records, so the filter leaves it alone by design, which is why a mixed list can carry a region without harming the matches it has no data for.
Marine ranges inherit the grain of the underlying record. WoRMS files a distribution against a named locality, and those run from a single bay to a whole ocean basin; a record against a basin maps to every ecoregion inside it. The median species spans 4 ecoregions and a quarter span exactly one, so most ranges are sharp. About 0.3% span more than half the ocean, and for those the filter is permissive: a species recorded that widely is in region wherever you ask, so it breaks no ties. The constraint also acts on fuzzy candidates only, so it changes nothing for a list that matches exactly throughout. It is most useful on regional field lists with the usual crop of misspellings, where the right correction and a plausible wrong one are a single edit apart.
The related check in inspect() looks at the other end of
the pipeline. Rather than steering a correction, it takes matched names
and flags the ones WCVP does not record in your region, surfacing a real
but geographically out-of-place species for review. The two share the
region, coords, and range
arguments. See the name
inspection vignette for that pass.
Inspecting a name list for the geographic outlier check that uses the same region inputs.
Fuzzy matching for the candidate generation the region filter refines.
Enrichments
for add_wcvp(), which attaches native range on the same
TDWG codes. ```