Skip to contents

Introduction

RESY implements the Expert System (ESy) framework for vegetation classification (Bruelheide 1997; Chytrý et al. 2020). An ESy file encodes three sections:

  1. Taxonomy – species aggregations and synonyms
  2. Taxon groups – species assemblages referenced in classification conditions
  3. Vegetation types – logical formulas that combine group thresholds into named habitat types

The package can work with any ESy-compliant classification. This vignette uses two bundled systems:

  • EUNIS (European Nature Information System habitat types; FloraVeg.EU)
  • Apennine-test (a simplified test system for the Italian Apennines)

For EUNIS specifically, many formulas also require geographic columns (Ecoreg, Country, Coast_EEA, Dunes_Bohn) in a sites header table. Those preparation steps are covered in vignette("EUNIS").


1. Load and prepare species data

resy_classify() expects the observation table to have three columns:

Column Description
PlotObservationID Plot identifier (auto-detected)
TaxonName Species name
Cover_Perc Cover value (percent or Braun-Blanquet scale)
data_species <- read_csv(
  system.file("extdata", "data_example_species.csv", package = "RESY"),
  show_col_types = FALSE
) |>
  rename(TaxonName = species, Cover_Perc = cover)

glimpse(data_species)
#> Rows: 3,580
#> Columns: 3
#> $ PlotObservationID <chr> "HU32", "HU32", "HU32", "HU32", "HU32", "HU32", "HU3…
#> $ Cover_Perc        <dbl> 0.1, 0.1, 0.5, 0.5, 0.5, 0.5, 87.5, 3.0, 3.0, 3.0, 0…
#> $ TaxonName         <chr> "Dryopteris filix-mas (L.) Schott", "Cephalanthera l…

A minimal header table (one row per plot) is required even when no geographic conditions are used. It must contain the same plot ID column as the observation table.

header <- data_species |>
  distinct(PlotObservationID) |>
  as.data.frame()

2. Load expert systems

resy_available_classifications() lists the schemes and versions bundled with the package.

resy_available_classifications() |> select(scheme, version)
#>          scheme    version
#> 1 Apennine-test 2026-06-27
#> 2         EUNIS 2025-10-03

To add a custom expert system, use resy_add_classification().

resy_load_expert() parses a bundled classification into a resy_parsed_expert object, used here for taxonomy checking. resy_classify() loads the expert internally from scheme/version.

parsed_apennine <- resy_load_expert(scheme = "Apennine-test")

3. Check taxonomy

resy_check_taxonomy() maps observation names against Section 1 of the expert system (canonical names + synonyms). This is classification-specific: a name may resolve in one system but not another.

tax_apennine <- resy_check_taxonomy(
  obs    = data_species,
  parsed = parsed_apennine,
  col    = "TaxonName"
)
cat("Apennine-test — matched:", sum(tax_apennine$matched),
    "/ unmatched:", sum(!tax_apennine$matched), "\n")
#> Apennine-test — matched: 1009 / unmatched: 0

4. Classify

resy_classify() evaluates all membership formulas and returns a resy_result object. Pass scheme to select the bundled expert system; it will be loaded automatically.

res_apennine <- resy_classify(
  obs    = data_species,
  header = header,
  scheme = "Apennine-test"
)

5. Inspect results

tree_filled <- resy_expert_tree(parsed_apennine, fill = TRUE)
print(tree_filled)
#> <resy_expert_tree> 5 node(s), 2 top-level
#> F Forest
#>   FB Beech-fir montane forest
#> N Non-forest
#>   NG Nardus acidic grassland
#>   NS Sub-Mediterranean scrub and woodland

To browse the type hierarchy of a loaded system, use resy_view_expert().

Vegetation type details

resy_eval_type() shows conditiosn of a type is evaluated.

resy_eval_type(res_apennine, t = "FB")
#> FB    Beech-fir montane forest
#> 
#> (<#TC Beech-forest-trees GR 15> AND <#TC Beech-forest-herbs GR 10>)
#> 
#> (col2 & col3)
#> 
#>   expressions                 
#> 2 #TC Beech-forest-trees GR 15
#> 3 #TC Beech-forest-herbs GR 10

Long table of candidates for all plots

resy_candidates() extracts ranked classification results. Use top_n to limit the number of types returned per plot.

cand_apennine <- resy_candidates(res_apennine, top_n = 3)
head(cand_apennine)
#>    plot_id   type priority priority_rank
#>     <char> <char>    <ord>         <int>
#> 1:    AM30      F        2             1
#> 2:    AN57      F        2             1
#> 3:    BE71      F        2             1
#> 4:    BE71     FB        5             3
#> 5:    BK34      N        2             1
#> 6:    BK34     NS        3             2

Plot-level details

resy_eval_plot() prints the full evidence for one plot: which species matched, which group conditions fired, and which vegetation-type formulas evaluated to TRUE.

# Replace "AN57" with a PlotObservationID present in your data
resy_eval_plot(res_apennine, p = "AN57")
#> Plant observations for plot AN57 :
#>     PlotObservationID Cover_Perc               TaxonName        group_names
#>                <char>      <num>                  <char>             <char>
#>  1:              AN57        0.1 Gymnocarpium dryopteris               <NA>
#>  2:              AN57        0.1   Athyrium filix-femina               <NA>
#>  3:              AN57        0.1     Prenanthes purpurea Beech-forest-herbs
#>  4:              AN57        0.1      Dryopteris expansa               <NA>
#>  5:              AN57        0.1      Polypodium vulgare               <NA>
#>  6:              AN57        0.5       Oxalis acetosella Beech-forest-herbs
#>  7:              AN57        0.5        Sorbus aucuparia               <NA>
#>  8:              AN57        0.5              Abies alba Beech-forest-trees
#>  9:              AN57       62.5         Fagus sylvatica Beech-forest-trees
#> 10:              AN57       37.5     Vaccinium myrtillus   Nardus-grassland
#> Possible types of plot "AN57" (135): F
#> Priorities of these types: 1 
#> Classified as: F

Next steps