# traitstrap-workflow

This vignette explains how to use the traitstrap package (Telford et al 2023). For more details on the methods see Maitner et al. (2021).

First of all, relax and turn on some music. We have prepared the traitstrap playlist for you!

## The aim of traitstrap

Trait distributions can be used to infer the importance of community assembly processes and the role of climate drivers in shaping species and community responses to climate change. Community ecology has typically focused on the mean, however the higher moments (variance, skewness, and kurtosis) of trait distributions can reveal information about the various processes shaping species diversity.

Measuring trait distributions is often difficult and time-consuming as it requires information on measuring trait values of all individuals present. Sampling protocols often limit sampling to a non-representative subset of the community, or rely upon species-level average traits values calculated in other locations or across many locations.

Traditionally the moments of trait distributions have been estimated using weighting approaches that rely on the average traits of species weighted by some measure of abundance within the community. Such community-weighted trait moments methods assume that a species’ trait expression can be adequately represented by the mean, ignoring intraspecific trait variation.

To more accurately estimate local trait distributions, trait sampling should thus occur both across multiple individuals within each species, and across multiple locations or experimental treatments across the extent of the study in order to capture both inter- and intra-specific variability.

Traitstrap is an R package to estimate the moments of community trait distributions using a bootstrapping approach. Further, this package uses a hierarchical sampling design, which allows accounting for incomplete trait collections, traits from different spatial or temporal levels (e.g. local traits vs. databases), taxonomic hierarchies (e.g., species vs genus) and experimental designs (e.g., multiple sites, or treated vs. control sampling units).

The package has three main functions:

- trait filling function which allows to account for intraspecific trait variation and hierarchical sampling design.
- a resample method using bootstrapping (parametric or nonparametric method) to calculate community weighted mean and happy moments (variance, skewness and kurtosis).
- a summary function that summarizes the trait moments and calculates confidence intervals.

Note that for this tutorial we are calling the mean and the higher moments the happy moments :-)

## The data

For this vignette we will use part of a vascular plant dataset from two sites near Longyearbyen on Svalbard. The data was collected during the Plant Functional Trait Course in 2018 and contains data on the plant community composition and functional traits. For more details see this GitHub repo

Note that some of the species names have been adapted.

## Organize your data

To run traitstrap two datasets are required:

- one dataset with information on abundance (e.g. biomass, size, cover, etc.) of the community, which is used to weight species traits by abundance in the community.
- one dataset with the traits for each species (or as many species and individuals you have data for) in your community.

The datasets need to be organized in a tidy and long format and certain columns (see below) are required, but the naming of these columns are up to the user.

Let us have a look at these datasets in an example.

The community data should have information the abundance of species in the community. This dataset will be used to weigh the traits by species abundance. Note that abundance can also be cover, size, biomass, or something similar.

In this example the contains species names (e.g. Taxon), cover of each species per plot (e.g. Cover) and two columns with information about the hierarchy (i.e. Site and PlotID).

    #> # A tibble: 110 × 4
    #>    Taxon             Cover Site  PlotID
    #>    <chr>             <dbl> <chr> <chr>
    #>  1 alopecurus ovatus   0.5 1     B
    #>  2 alopecurus ovatus   1   1     C
    #>  3 alopecurus ovatus   1   1     D
    #>  4 alopecurus ovatus   2   1     F
    #>  5 alopecurus ovatus   0.1 1     G
    #>  6 bistorta vigdis    10   1     A
    #>  7 bistorta vigdis    25   1     B
    #>  8 bistorta vigdis    10   1     C
    #>  9 bistorta vigdis     2   1     D
    #> 10 bistorta vigdis     1   1     F
    #> # ℹ 100 more rows

The trait data should contain information about traits and trait values for as many species and individuals in the community data as possible. The data should be organized in the same way as the community data and should have corresponding columns. In this example the trait data contains Taxon, Site and PlotID as well as Trait and Value.

    #> # A tibble: 705 × 6
    #>    Site  PlotID Taxon                   ID      Trait              Value
    #>    <chr> <chr>  <chr>                   <chr>   <chr>              <dbl>
    #>  1 1     A      saxifraga oppositifolia ANF4320 Plant_Height_cm 2
    #>  2 1     A      saxifraga oppositifolia ANF4320 Wet_Mass_g      0.000695
    #>  3 1     A      saxifraga oppositifolia ANF4320 Thickness_mm    0.127
    #>  4 1     C      bistorta vigdis         ARQ4344 Plant_Height_cm 4.2
    #>  5 1     C      bistorta vigdis         ARQ4344 Wet_Mass_g      0.0105
    #>  6 1     C      bistorta vigdis         ARQ4344 Thickness_mm    0.154
    #>  7 2     C      festuca rubra           AMT1545 Plant_Height_cm 7
    #>  8 2     C      festuca rubra           AMT1545 Wet_Mass_g      0.00724
    #>  9 2     C      festuca rubra           AMT1545 Thickness_mm    0.257
    #> 10 1     C      bistorta vigdis         ARP2422 Plant_Height_cm 1
    #> # ℹ 695 more rows

## Trait filling

The `trait_fill()` function uses a hierarchical sampling design, which allows to account for incomplete trait collections, traits from different spatial or temporal levels (i.e. local traits vs. databases), different taxonomic resolution and/or experimental design.

The first two mandatory arguments in the function are the two datasets: `comm` and `traits`

The next four arguments are also mandatory and refer to specific columns in the trait or community dataset:

- `abundance` which is the abundance of your species in your community dataset. This can be abundance, cover, biomass, or size, etc.
- `taxon_col` is the column in your community and trait data that define the species.
- `trait_col` is the column in your trait data that defines the traits.
- `value_col` is the column in your trait data that defines the trait values.

All the other arguments are not mandatory.

With `scale_hierarchy` you can define the levels at which the traits have been collected and the order of trait filling starting with the highest level (e.g. global database, regional, site, plot). In the example below we have the levels Site and PlotID, starting with the highest level.

The `trait_fill()` function will choose if available a trait value from the lowest level, i.e. species X from plot A in site 1. If no trait value is available from that level (plot A, site 1), it will other groups in the same level and choose a trait value from species X from plot B or C at site 1. If there is no trait available, it will move up the hierarchy to the next level and choose trait values from species X from other sites (site 2, 3, etc.).

The argument `min_n_in_samples` allows users to define the minimum number in samples that are chosen at each level. If the minimum number is not reached (i.e. there are only 3 trait values at a specific level), trait values from the next higher level will be filled, to avoid sampling the same individual several times, which could result in unrealistic variances. The default minimum number of samples is 5.

In the `other_col` argument other grouping variables in the community dataset can be defined and will be kept after the trait filling.

    trait_filling <- trait_fill(
      # input data (mandatory)
      comm = community,
      traits = trait,

      # specifies columns in your data (mandatory)
      abundance_col = "Cover",
      taxon_col = "Taxon",
      trait_col = "Trait",
      value_col = "Value",

      # specifies sampling hierarchy
      scale_hierarchy = c("Site", "PlotID"),

      # min number of samples
      min_n_in_sample = 9
    )

    trait_filling
    #> # A tibble: 4,011 × 13
    #> # Groups:   global, Site, PlotID, Trait [42]
    #>    Taxon   Cover Site  PlotID global sum_abun ID    Trait Value n_sample  weight
    #>    <chr>   <dbl> <chr> <chr>  <chr>     <dbl> <chr> <chr> <dbl>    <int>   <dbl>
    #>  1 dryas …   0.1 1     A      global     38.2 ATC4… Plan…   1.3       17 0.00588
    #>  2 dryas …   0.1 1     A      global     38.2 ADC9… Plan…   1.4       17 0.00588
    #>  3 dryas …   0.1 1     A      global     38.2 AVU2… Plan…   1.5       17 0.00588
    #>  4 dryas …   0.1 1     A      global     38.2 AHU1… Plan…   2         17 0.00588
    #>  5 dryas …   0.1 1     A      global     38.2 AHS4… Plan…   2.5       17 0.00588
    #>  6 dryas …   0.1 1     A      global     38.2 AHR0… Plan…   2.4       17 0.00588
    #>  7 dryas …   0.1 1     A      global     38.2 ABE6… Plan…   0.6       17 0.00588
    #>  8 dryas …   0.1 1     A      global     38.2 BUP1… Plan…   2.2       17 0.00588
    #>  9 dryas …   0.1 1     A      global     38.2 ADE9… Plan…   1         17 0.00588
    #> 10 dryas …   0.1 1     A      global     38.2 ACH6… Plan…   3         17 0.00588
    #> # ℹ 4,001 more rows
    #> # ℹ 2 more variables: level <ord>, max_n_in_sample <int>

Traitstrap also allows to include taxonomy and experimental design in the trait filling step.

With the argument `taxon_col` the taxonomic hierarchy for sampling can be defined. This means if traits for a specific species are not available, trait values from the same genus will be used. For this a list of the taxonomic hierarchy has to be defined (e.g. “Taxon”, “Genus”). Note that traits from species of the same genus can have very different traits and it might not be meaningful to use these traits. Therefore, you should always check the trait distributions for the same genus before using taxonomic trait filling.

The argument `treatment_col` allows to incorporate an experimental design where traits are filled from the same experimental treatment or the first factor level, which is assumed to be the control. Therefore, it is important to order the levels of a treatment in the right order, i.e. the first level has to be the control. The filling step can be defined at certain level using the `treatment_level` argument. Depending on the experimental design it might make sense to fill traits at a certain level, e.g. block or site.

Here is an example how to include taxonomy and experimental design in the trait filling function (code not run).

    trait_filling2 <- trait_fill(
      comm = community,
      traits = trait,
      abundance_col = "Cover",

      # defining taxonomic hierarchy
      taxon_col = c("Taxon", "Genus"),
      trait_col = "Trait",
      value_col = "Value",
      scale_hierarchy = c("Site", "PlotID"),
      min_n_in_sample = 3,

      # specifying experimental design
      treatment_col = "Treatment",
      treatment_level = "Site",
    )

## Nonparametric bootstrapping

The output of the trait filling function is then used to do a nonparametric bootstrapping using the `trait_np_bootstrap()` function.

Nonparametric bootstrapping is a resampling method to estimate the trait moments. The traits are re-sampled in proportion to their weight in the community (e.g. by the abundance of the species).

The trait values across all individuals in a community are resampled n times (`sample_size`; the default is 200) to incorporate the full spectrum of trait variation, generating n number (`nrep`; the default is 100) of trait distributions.

From these trait distributions the happy moments are estimated: mean, variance, skewness and kurtosis.

This function also allows to extract raw distributions by setting the argument `raw = TRUE`. The raw data can be useful for visualizing the trait distributions. If the raw data is extracted, `nrep` is forced to 1 to avoid memory issues.

    # run nonparametric bootstrapping
    np_bootstrapped_moments <- trait_np_bootstrap(
      trait_filling,
      nrep = 50
    )

    np_bootstrapped_moments
    #> # A tibble: 2,100 × 8
    #> # Groups:   global, Site, PlotID [14]
    #>    global Site  PlotID Trait             mean  variance skewness kurtosis
    #>    <chr>  <chr> <chr>  <chr>            <dbl>     <dbl>    <dbl>    <dbl>
    #>  1 global 1     A      Plant_Height_cm 1.62   2.50         3.25    12.6
    #>  2 global 1     A      Thickness_mm    0.212  0.00351      1.27     2.26
    #>  3 global 1     A      Wet_Mass_g      0.0116 0.0000516    0.524   -0.944
    #>  4 global 1     B      Plant_Height_cm 1.53   2.66         3.32    12.9
    #>  5 global 1     B      Thickness_mm    0.203  0.00171      0.139   -0.855
    #>  6 global 1     B      Wet_Mass_g      0.0117 0.0000420    0.567   -0.941
    #>  7 global 1     C      Plant_Height_cm 1.71   3.84         2.70     7.22
    #>  8 global 1     C      Thickness_mm    0.202  0.00277      2.04     9.08
    #>  9 global 1     C      Wet_Mass_g      0.0130 0.000213     5.50    41.9
    #> 10 global 1     D      Plant_Height_cm 2.04   7.97         2.42     5.19
    #> # ℹ 2,090 more rows

One advantage of using a bootstrapping approach, is that we get much more than a mean trait value. We can also estimate the variance and other moments of these trait distributions. In traitstrap happy moments can be summarized and the confidence intervals calculated using the `trait_summarise_boot_moments()` function. The input variable for this function is the output from the nonparametric bootstrapping function (or the parametric bootstrapping function, see below).

The confidence interval can be calculated parametrically, using the mean and standard deviation, or nonparametrically using quantiles. The default is using the mean and standard deviation (`parametric = TRUE`) with one standard deviation around each trait moment (`sd_mult = 1`). For the nonparametric approach the default is a 0.95 confidence level.

    # summarizes bootstrapping output
    sum_boot_moment <- trait_summarise_boot_moments(
      np_bootstrapped_moments
    )

    sum_boot_moment
    #> # A tibble: 42 × 17
    #> # Groups:   global, Site, PlotID [14]
    #>    global Site  PlotID Trait           n   mean ci_low_mean ci_high_mean     var
    #>    <chr>  <chr> <chr>  <chr>       <int>  <dbl>       <dbl>        <dbl>   <dbl>
    #>  1 global 1     A      Plant_Heig…    50 1.61        1.49         1.73   3.16e+0
    #>  2 global 1     A      Thickness_…    50 0.214       0.210        0.218  3.48e-3
    #>  3 global 1     A      Wet_Mass_g     50 0.0109      0.0104       0.0114 4.81e-5
    #>  4 global 1     B      Plant_Heig…    50 1.62        1.50         1.75   3.08e+0
    #>  5 global 1     B      Thickness_…    50 0.203       0.200        0.206  1.70e-3
    #>  6 global 1     B      Wet_Mass_g     50 0.0122      0.0117       0.0126 4.68e-5
    #>  7 global 1     C      Plant_Heig…    50 1.70        1.57         1.82   3.97e+0
    #>  8 global 1     C      Thickness_…    50 0.205       0.201        0.208  2.76e-3
    #>  9 global 1     C      Wet_Mass_g     50 0.0118      0.0110       0.0126 1.24e-4
    #> 10 global 1     D      Plant_Heig…    50 1.82        1.61         2.03   5.93e+0
    #> # ℹ 32 more rows
    #> # ℹ 8 more variables: ci_low_var <dbl>, ci_high_var <dbl>, skew <dbl>,
    #> #   ci_low_skew <dbl>, ci_high_skew <dbl>, kurt <dbl>, ci_low_kurt <dbl>,
    #> #   ci_high_kurt <dbl>

## Parametric bootstrapping

Traitstrap also offers the option to run a parametric bootstrapping.

The `trait_fit_distributions()` function fits parametric distributions for each species-by-trait combination at the finest scale of the user-supplied hierarchy. This function takes as input:

- an object of class filled traits (as produced by the function `trait_fill`), and
- the type of distribution to be fitted.

Either a single distribution type can be used for all traits, or traits can be assigned specific distributions types by supplying the function with a named vector of traits (e.g. `c(height = "normal", mass = "lognormal")`).

Currently the function supports normal, log-normal, and beta (values between 0 and 1) distributions.

The function returns a dataframe containing fitted distribution parameters.

    # fit distributions
    fitted_distributions <- trait_fit_distributions(
      filled_traits = trait_filling,
      distribution_type = "lognormal"
    )

    fitted_distributions
    #> # A tibble: 303 × 15
    #> # Groups:   global, Site, PlotID, Trait, Taxon, Cover, n_sample [303]
    #>    global Site  PlotID Trait Taxon Cover n_sample distribution_type  parm1 parm2
    #>    <chr>  <chr> <chr>  <chr> <chr> <dbl>    <int> <chr>              <dbl> <dbl>
    #>  1 global 1     A      Plan… bist…  10         17 lognormal          0.156 0.587
    #>  2 global 1     A      Plan… drya…   0.1       17 lognormal          0.434 0.810
    #>  3 global 1     A      Plan… fest…   1         11 lognormal          1.61  0.377
    #>  4 global 1     A      Plan… luzu…   0.5       10 lognormal          1.45  0.287
    #>  5 global 1     A      Plan… luzu…   1         14 lognormal          0.405 0.501
    #>  6 global 1     A      Plan… sali…  20         22 lognormal         -0.320 1.08
    #>  7 global 1     A      Plan… saxi…   2          6 lognormal          1.13  0.469
    #>  8 global 1     A      Plan… saxi…   2          2 lognormal          0.693 0
    #>  9 global 1     A      Plan… sile…   1          3 lognormal         -0.221 0.277
    #> 10 global 1     A      Thic… bist…  10         17 lognormal         -1.55  0.194
    #> # ℹ 293 more rows
    #> # ℹ 5 more variables: sd1 <lgl>, sd2 <lgl>, ks <dbl>, cvm <dbl>, ad <dbl>

    # fit several types of distributions
    fitted_distributions <- trait_fit_distributions(
      filled_traits = trait_filling,
      distribution_type = c(Plant_Height_cm = "normal",
                               Wet_Mass_g = "lognormal")
    )

    fitted_distributions

The `trait_parametric_bootstrap()` function is a parametric analogue of the `trait_np_bootstrap()` function. It takes in fitted trait distributions produced by `trait_fit_distributions` and randomly samples from among the fitted distributions proportionally to species abundances in the community.

As with `trait_np_bootstrap()`, the number of samples per replicated draw are specified with the parameter `sample_size`, and the number of replicated draws is specified by the parameter `nrep.` The argument `raw` allows to extract raw distributions (see above).

    # run parametric bootstrapping
    p_bootstrapped_moments <- trait_parametric_bootstrap(
      fitted_distributions = fitted_distributions,
      nrep = 50
    )

    p_bootstrapped_moments
    #> # A tibble: 2,100 × 8
    #> # Groups:   global, Site, PlotID, Trait [42]
    #>    global Site  PlotID Trait             mean  variance skewness kurtosis
    #>    <chr>  <chr> <chr>  <chr>            <dbl>     <dbl>    <dbl>    <dbl>
    #>  1 global 1     A      Plant_Height_cm 1.57   2.24         1.96    4.19
    #>  2 global 1     A      Thickness_mm    0.211  0.00316      1.29    3.34
    #>  3 global 1     A      Wet_Mass_g      0.0105 0.0000416    0.802   0.0631
    #>  4 global 1     B      Plant_Height_cm 1.70   2.58         1.47    1.41
    #>  5 global 1     B      Thickness_mm    0.204  0.00159      0.303  -0.432
    #>  6 global 1     B      Wet_Mass_g      0.0130 0.0000454    0.649   0.228
    #>  7 global 1     C      Plant_Height_cm 1.47   1.99         1.84    3.20
    #>  8 global 1     C      Thickness_mm    0.208  0.00305      2.89   14.9
    #>  9 global 1     C      Wet_Mass_g      0.0112 0.0000537    2.38    9.65
    #> 10 global 1     D      Plant_Height_cm 1.61   3.72         2.15    4.57
    #> # ℹ 2,090 more rows

The output of trait_parametric_bootstrap can be summarized using `trait_summarize_boot_moments()` (see above).

## Extracting raw distributions

In traitstrap both the parametric and nonparametric bootstrapping functions allow returning raw trait distributions.

    # run nonparametric bootstrapping
    raw_dist_np <- trait_np_bootstrap(
      filled_traits = trait_filling,
      raw = TRUE
    )

    raw_dist_np
    #> # A tibble: 8,400 × 13
    #> # Groups:   global, Site, PlotID, Trait [42]
    #>    Taxon    Cover Site  PlotID global sum_abun ID    Trait Value n_sample weight
    #>    <chr>    <dbl> <chr> <chr>  <chr>     <dbl> <chr> <chr> <dbl>    <int>  <dbl>
    #>  1 salix p…    20 1     A      global     38.2 AXW1… Plan…   0.6       22  0.909
    #>  2 salix p…    20 1     A      global     38.2 AMQ6… Plan…   0.7       22  0.909
    #>  3 bistort…    10 1     A      global     38.2 AUA5… Plan…   0.8       17  0.588
    #>  4 salix p…    20 1     A      global     38.2 AQT8… Plan…   0.3       22  0.909
    #>  5 salix p…    20 1     A      global     38.2 ASL0… Plan…   1.6       22  0.909
    #>  6 salix p…    20 1     A      global     38.2 ATU7… Plan…   0.9       22  0.909
    #>  7 bistort…    10 1     A      global     38.2 BRP6… Plan…   0.7       17  0.588
    #>  8 bistort…    10 1     A      global     38.2 ASE0… Plan…   2.3       17  0.588
    #>  9 salix p…    20 1     A      global     38.2 AQT8… Plan…   0.3       22  0.909
    #> 10 silene …     1 1     A      global     38.2 AUE6… Plan…   0.5        3  0.333
    #> # ℹ 8,390 more rows
    #> # ℹ 2 more variables: level <ord>, max_n_in_sample <int>

The raw data can be useful for visualizing the trait distributions.

Use colour and facets to separate between the different traits, hierarchies and treatments.

    ggplot(raw_dist_np, aes(x = log(Value), fill = Site)) +
      geom_density(alpha = 0.4) +
      scale_fill_manual(values = col_palettes$GrandBudapest1) +
      labs(x = "log(trait value)") +
      facet_wrap(facets = vars(Trait), scales = "free")

## Multivariate analyses

While the core functionality of traitstrap was designed with univariate distributions in mind, it can also be used for multivariate questions as well using the function `trait_multivariate_bootstrap()`. This function allows users to supply any appropriate function for analyses of multivariate data, including those included in popular packages for functional diversity calculations. Here, we demonstrate how to combine `trait_multivariate_bootstrap()` with the function `dbFD()` from the FD package.

We first use the `trait_fill()` function similar to the univariate case. Note that we are also using the argument `complete_only` to specify that we are only interested in individuals that have all traits and the argument `leaf_id`, which tells the function which column contains the unique individual ID. Also, we are subsetting the data to make it run faster.

    library(FD)
    #> Loading required package: ade4
    #> Loading required package: ape
    #>
    #> Attaching package: 'ape'
    #> The following object is masked from 'package:dplyr':
    #>
    #>     where
    #> Loading required package: geometry
    #> Loading required package: vegan
    #> Loading required package: permute
    #> Loading required package: lattice
    #> This is vegan 2.6-4

    multivariate_traits <- trait_fill(
      comm = community |>
        # to make the example faster, we'll only use a subset of plots
        filter(
          Site == "1",
          PlotID %in% c("A", "B")
        ),
      traits = trait,
      scale_hierarchy = c("Site", "PlotID"),
      taxon_col = "Taxon",
      value_col = "Value",
      trait_col = "Trait",
      abundance_col = "Cover",
      complete_only = TRUE,
      leaf_id = "ID"
    )

Then we are using the `trait_multivariate_bootstrap()` function to do the actual bootstrapping. This function needs the output from the trait filling as `filled_traits`, number bootstrapped of replications `nrep`, bootstrap size `sample_size` and `id`. The last argument is `fun`, that takes any function to apply to the data. In this case we are using diversity functions from the `dbFD` package.

Note that we are using a low number of replicates (nrep) to make it run faster.

The `dbFD()` function returns a list as output, and we need to wrangle the data a bit to get it into a more useful format. We will extract Rao’s Quadratic Entropy, since that performs well with bootstrapping.

    boot_multi <- trait_multivariate_bootstrap(
      filled_traits = multivariate_traits,
      nrep = 5, # number of reps is set low for demo purposes
      sample_size = 200,
      id = "ID",
      fun = function(x) {
        dbFD(
          x = x,
          calc.FRic = FALSE,
          calc.FDiv = FALSE,
          calc.CWM = FALSE,
          stand.x = FALSE,
          scale.RaoQ = FALSE
        )
      }
    )

    # data wrangling
    raoq_est <- boot_multi |>
      mutate(result = map(result, as.data.frame)) |>
      unnest(result)

Let’s plot RaoQ:

    ggplot(data = raoq_est, mapping = aes(x = RaoQ, fill = PlotID)) +
      geom_density(alpha = 0.5) +
      scale_fill_manual(values = col_palettes$GrandBudapest1) +
      xlim(c(0, 6))

Next, we’ll show how we can use a similar method to combine `trait_multivariate_bootstrap` with the functions `TPDs()` and `REND()` from the TPD package.

For each replicated site, we convert the distribution into a trait probability distribution and then derive functional diversity metrics.

    library(TPD)

    boot_tpd <- trait_multivariate_bootstrap(multivariate_traits,
      id = "ID",
      nrep = 5, # Note that the number of reps is set low for demo purposes
      fun = function(x) {
        TPDs(
          species = rep(1, 200),
          traits = x
        ) |>
          REND(TPDs = _)
      }
    )

    # wrangling data
    tpd <- boot_tpd |>
      mutate(result = map(result, as.data.frame)) |>
      unnest(result) |>
      pivot_longer(
        cols = species.FRichness:species.FDivergence,
        values_to = "value",
        names_to = "metric"
      ) |>
      mutate(metric = str_remove(metric, "species\\."))

    # and make plot
    ggplot(data = tpd) +
      geom_violin(
        mapping = aes(y = value, x = PlotID, fill = PlotID),
        alpha = 0.5,
        draw_quantiles = c(0.025, 0.975)
      ) +
      scale_fill_manual(values = col_palettes$GrandBudapest1) +
      facet_wrap(facets = vars(metric), scales = "free")

## Check your data

Traitstrap has a couple of functions to check the data.

The `coverage_plot()` function shows the trait coverage of the community for each level. Basically, this function summarizes from which level the traits are filled, and how much coverage of the community is reached.

Based on simulations, we recommend to collect traits for at least 80% of the community cover (Maitner et al. in prep).

    # show coverage plot
    autoplot(trait_filling) +
      scale_fill_manual(values = col_palettes$GrandBudapest1) +
      theme(axis.text.x = element_text(size = 8, angle = 90, vjust = 0.5))

Another important information is to know of which taxa traits are missing. This can be useful if the data sampling is not finished and you want to know which species should be sampled. The function also tells you the maximal abundance of each missing species, and gives you useful information if the missing species are abundant or rare.

Traitstrap has a function `trait_missing()` which gives you a table with all missing values.

    # list missing traits
    trait_missing(
      filled_trait = trait_filling,
      comm = community
    )
    #> # A tibble: 20 × 4
    #> # Groups:   Taxon, max_abun [20]
    #>    Taxon                      max_abun     n n_traits
    #>    <chr>                         <dbl> <int>    <int>
    #>  1 alopecurus ovatus               2       5        3
    #>  2 bistorta vigdis                25      14        3
    #>  3 calamagrostis neglecta         60       4        3
    #>  4 cassiope tetragona              5       3        3
    #>  5 dryas octopetala               20       9        3
    #>  6 enquistetum scirpoides          2       6        3
    #>  7 festuca rubra                   1       7        3
    #>  8 juncus biglumis                 0.5     2        0
    #>  9 luzula confusa                  5      10        3
    #> 10 luzula nivalis                  5       8        3
    #> 11 maitneranthes hieracifolia      0.5     2        0
    #> 12 oxyria tanyna                   2       5        3
    #> 13 poa pratensis                   1       6        3
    #> 14 salix polaris                  43      14        3
    #> 15 saxifraga hirculus              2       3        3
    #> 16 saxifraga oppositifolia         2       1        3
    #> 17 silene acaudis                  1       1        3
    #> 18 stelfordaria humifusa           0.5     1        0
    #> 19 stellaria longipes              0.1     4        0
    #> 20 trisetum spicatum               3       5        3