## ----include=FALSE------------------------------------------------------------
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  warning = FALSE,
  message = FALSE)

## ----flowchart, echo=FALSE, out.width="100%", fig.alt="Flowchart of a hellometry() call, from the input data through data_checker(), make_measurement_table(), full_estimation_table() for sizes then models, and predict_biomass(), to the returned list of three tables."----
knitr::include_graphics("hellometry_flowchart.svg")

## -----------------------------------------------------------------------------
# Load library
library(hellometry)

# We need to define a suite of levels to be used for estimation, i.e. columns in the data, 
# from lowest to coarsest resolution
level_vec  <-
  c("species", "genus", "family", "order")

# Read in the reference measurements, the real body sizes and body masses used to build the models
measurements <-
  bromeliad_inverts_measurements() %>%
  ## Rename columns to those expected by hellometry()
  dplyr::rename(size_col = body_size_mm,
                biomass_col = body_mass_mg,
                biomass_type = mass_type) %>%
  ## size_col holds both numbers and categories, so it must be character;
  ## biomass_col must be numeric for the allometric models
  dplyr::mutate(size_col = as.character(size_col),
                biomass_col = as.numeric(biomass_col))

# Have a look at the reference measurements
dplyr::glimpse(measurements)

# Read in the Trinidadian communities, the taxa we want size and biomass estimates for
communities <-
  trini_communities() %>%
  ## Rename the abundance column and add the columns hellometry() needs
  dplyr::rename(abundance = n) %>%
  ## Here we do not have any measurement for these invertebrates, so size is "unknown"
  ## and biomass NA, to be estimated. We use dry biomass because it tends to be more precise
  dplyr::mutate(size_col = "unknown",
                biomass_col = NA,
                biomass_type = "dry")

# Have a look at the communities to estimate
dplyr::glimpse(communities)

# Combine the reference measurements and the target communities into one table
my_invertebrates <-
  communities %>%
  dplyr::bind_rows(measurements)


# Now we can use the package to estimate sizes and biomasses!

# Get the estimates
my_invertebrates_estimated <-
  hellometry(dats = my_invertebrates, ## The data to be used
              level_vec  = level_vec , ## The taxonomic levels to be used
              biomass_type = "dry") ## Type of biomass to be estimated

# The result is a list of three elements
# - `data` - your data with estimated body sizes and biomasses, and column with 
# information on the level at which were performed the estimation. Note that 
# it also includes the measurements supplied so will need to filter out these rows
dplyr::glimpse(my_invertebrates_estimated$data)
# - `size_estimates` - a tibble with all unique size estimates that were joined to your data
dplyr::glimpse(my_invertebrates_estimated$size_estimates)
# - `model_estimates` - a tibble with all unique allometric models that were joined to your data
dplyr::glimpse(my_invertebrates_estimated$model_estimates)


# But now let's say you do not really want to do estimation on the data, 
# but just see the results of all possible estimations. Fear not, 
# it is easy!
## First compile the measurement table
measurement_table <-
  make_measurement_table(dats = my_invertebrates, ## Data to be used
                         level_vec  = level_vec) ## Taxonomic levels to be used
## Get all possible size estimates
size_estimation_table <-
  full_estimation_table(level_vec  = level_vec ,
                        measurement_table = measurement_table,
                        what = "size_col")
### Have a glimpse of it
dplyr::glimpse(size_estimation_table)

## Get all possible models
model_estimation_table <-
  full_estimation_table(level_vec  = level_vec ,
                        measurement_table = dry_wet(measurement_table,
                                                        biomass_type = "dry"), ## Filter for dry biomass
                        what = "biomass_col")
### Have a glimpse of it
dplyr::glimpse(model_estimation_table)


## -----------------------------------------------------------------------------

# Read in data with traits
trait_data <-
  trini_communities(traits = TRUE)

# The fuzzy traits are every column after the taxonomy, i.e. from the first trait
# column (AS1) up to the last column of the table
trait_columns <-
  trait_data %>%
  ## Get the block of trait columns ..
  dplyr::select(AS1:dplyr::last_col()) %>%
  ## Keep just their names
  names()

# Rebuild a measurement table, this time asking it to keep every taxonomic level
# we want to match on (make_measurement_table() only keeps the levels we give it)
measurement_table <-
  make_measurement_table(dats = my_invertebrates,
                         level_vec = c("species", "genus", "subfamily",
                                       "family", "order", "class"))

# Here we want to match traits from different morphospecies, look for matches 
# from genus up to class
match_levels <-
  c("genus", "subfamily", "family", "order", "class")

# Build a lookup holding one representative trait profile per taxon, at 
# every level combined into a single long table that remembers the level 
# each profile came from
trait_lookup <-
  match_levels %>%
  purrr::map_dfr(.,
                 ~ trait_data %>%
                     ## Drop the taxa that are unnamed at this level
                     dplyr::filter(!is.na(.data[[.x]]), .data[[.x]] != "") %>%
                     ## One row per taxon name, carrying its trait profile
                     dplyr::distinct(key = .data[[.x]],
                                     dplyr::across(dplyr::all_of(trait_columns))) %>%
                     ## Keep a single profile should a taxon appear more than once
                     dplyr::distinct(key, .keep_all = TRUE) %>%
                     ## Tag which taxonomic level this profile belongs to
                     dplyr::mutate(match_level = .x))

# Now hand each measured taxon the trait profile of its most similar relative, i.e.
# the profile found at the finest taxonomic level at which the taxon matches
trait_assignment <-
  measurement_table %>%
  ## Tag each measurement row so we can return its assigned traits later
  dplyr::mutate(.row = dplyr::row_number()) %>%
  ## Keep only that tag and the taxonomy columns we match on
  dplyr::select(.row, dplyr::all_of(match_levels)) %>%
  ## Stretch to one row per measured taxon per taxonomic level
  tidyr::pivot_longer(dplyr::all_of(match_levels),
                      names_to = "match_level", values_to = "key") %>%
  ## Drop the levels where the taxon has no name
  dplyr::filter(!is.na(key), key != "") %>%
  ## Attach every trait profile that matches, at whatever level
  dplyr::inner_join(trait_lookup, 
                    by = c("match_level", "key")) %>%
  ## Rank the levels from finest to coarsest, following match_levels
  dplyr::mutate(.priority = match(match_level, 
                                  match_levels)) %>%
  ## For each measurement row, keep only the finest matching level
  dplyr::group_by(.row) %>%
  dplyr::slice_min(.priority, 
                   n = 1, 
                   with_ties = FALSE) %>%
  dplyr::ungroup() %>%
  ## Return just the row tag and its assigned trait profile
  dplyr::select(.row, dplyr::all_of(trait_columns))

# Finally, join the assigned traits back onto the measurements, and keep only taxa
# that both received traits and are identified to family (our id_col below)
dats <-
  measurement_table %>%
  ## Recreate the same row tag to join on
  dplyr::mutate(.row = dplyr::row_number()) %>%
  ## Bring in each taxon's assigned trait profile
  dplyr::left_join(trait_assignment, by = ".row") %>%
  ## Drop the helper tag
  dplyr::select(-.row) %>%
  ## Keep taxa that got traits and have a family to identify them by
  dplyr::filter(!is.na(.data[[trait_columns[1]]]),
                !is.na(family), family != "")

## Get long format data of which taxa have similar traits
matched_traits <-
  matcher_of_traits(measurement_table = dats,
                    trait_columns = trait_columns,
                    id_col = "family") ## here taxa are identified by family
### See the output
head(matched_traits)

## Get clean list for full_estimation_table, includes matcher_of_traits()
## Here you can see which taxa were grouped together
clean_trait_list <-
  make_trait_table(measurement_table = dats,
                   trait_columns = trait_columns,
                   id_col = "family")
### See the output
clean_trait_list[[1]]

# The last two functions are called in full_estimation_table if you add the trait arguments
## Size estimation table
size_estimation_table_traits <-
  full_estimation_table(level_vec  = c(),
                        measurement_table = dats,
                        traits = TRUE, ## switch for traits
                        trait_columns = trait_columns, ## vector of fuzzy trait columns
                        id_col = "family", ## where to group
                        what = "size_col")
### See the output
head(size_estimation_table_traits)

## Model table
model_estimation_table_traits <-
  full_estimation_table(level_vec  = c(),
                        measurement_table = dry_wet(dats,
                                                    biomass_type = "dry"), ## Filter for dry biomass
                        traits = TRUE, ## traits = TRUE
                        trait_columns = trait_columns, ## vector of fuzzy trait columns
                        id_col = "family", ## column identifying taxa
                        what = "biomass_col")
### See the output
head(model_estimation_table_traits)

