This vignette describes a series of functions in the forestexplorR package that provide quantitative descriptions of local neighborhoods in mapped forest stands. The neighborhood of a specific location in a mapped stand is the collection of trees growing within a specified distance of that location.

Of these functions, neighborhoods() constructs the list of all trees in each neighborhood. neighborhood_summary() takes the output of neighborhoods() as input and calculates tree species richness and species-specific densities for each neighborhood. site_by_species() also takes the output of neighborhoods() as input but creates a site-by-species matrix, where each site is a different neighborhood, that can be used to calculate diversity metrics. tree_utm() takes a mapping object as input and calculates UTM coordinates for each tree, thereby allowing trees to be linked to fine-scale spatial data such as topography.

neighborhoods(): Construct neighborhoods

The neighborhoods() function uses a mapping dataset to determine, for each tree, all the trees that are within a certain distance (radius). The argument stand can be used to indicate that neighborhoods should be constructed only for the trees of one or more of the stands included in mapping. If stand is not specified, neighborhoods will be constructed for all trees in mapping.

The output is a data frame where each row contains information on a focal tree and one other tree growing in the focal tree’s neighborhood. Therefore, the information for each focal tree will appear on x lines, where x is the number of trees in that focal tree’s neighborhood. This structure is shown below by isolating a single focal tree in the output of applying neighborhoods() to the built-in mapping dataset:

nbhds <- neighborhoods(mapping, stand = "AB08", radius = 10)
nbhds %>%
  filter(tree_id == "AB08000100022")
#>         tree_id stand_id species  dbh      abh x_coord y_coord       id_comp
#> 1 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100001
#> 2 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100002
#> 3 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100003
#> 4 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100004
#> 5 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100005
#> 6 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100007
#> 7 AB08000100022     AB08    TSHE 15.1 179.0786   18.11   95.68 AB08000100017
#>       prox sps_comp dbh_comp  abh_comp
#> 1 6.423161     TSHE     50.4 1995.0370
#> 2 6.079679     TSHE     81.1 5165.7287
#> 3 4.925627     TSHE     20.0  314.1593
#> 4 5.221571     TSHE     53.7 2264.8448
#> 5 7.283028     TSHE     24.4  467.5947
#> 6 7.504265     THPL     57.0 2551.7586
#> 7 6.124255     TSHE     24.6  475.2916

The neighborhoods() function can also be used to construct neighborhoods for specific coordinates within the mapped stand instead of constructing a neighborhood centered on each tree in the dataset. This is achieved by providing a data frame of coordinates under the optional argument coords (see neighborhoods() documentation for details on how this data frame should be structured).

# Create a data frame of coordinates
locations <- data.frame(
  loc_id = paste("A", 1:81, sep = ""),
  x_coord = rep(seq(10, 90, 10), times = 9),
  y_coord = rep(seq(10, 90, 10), each = 9))

# Construct neighborhood for each coordinate
coord_nbhds <- neighborhoods(mapping, stands = "AB08", radius = 10,
                             coords = locations)
head(coord_nbhds)
#>   loc_id stand_id x_coord y_coord       id_comp     prox sps_comp dbh_comp
#> 1     A1     AB08      10      10 AB08001600008 6.144689     TSHE     20.2
#> 2     A1     AB08      10      10 AB08001600009 7.062294     TSHE     68.2
#> 3     A1     AB08      10      10 AB08001600010 9.539193     TSHE     55.4
#> 4     A1     AB08      10      10 AB08001600011 2.344462     TSHE     17.5
#> 5     A1     AB08      10      10 AB08001600012 2.979564     TSHE     59.7
#> 6     A1     AB08      10      10 AB08001600013 4.928539     PSME     72.5
#>    abh_comp
#> 1  320.4739
#> 2 3653.0754
#> 3 2410.5126
#> 4  240.5282
#> 5 2799.2297
#> 6 4128.2491

neighborhood_summary(): Summarize neighborhoods

The neighborhood_summary() function takes output of the neighborhoods() function and calculates species diversity metrics, overall tree density, and densities of each tree species for each neighborhood. Diversity metrics include species richness, Shannon-Wiener diversity, Simpson’s diversity, and evenness (Pielou’s J).

The densities argument is used to specify how densities should be calculated. The optimal method for density calculation is likely to vary between study systems and processes of interest. If the differential effects of neighbor tree species are likely to be influenced by how close the neighbors of each species are to the focal (e.g. when differences relate to competition for soil moisture or nutrients), the angular method may be more appropriate. However, if the forest is in the vertical diversification stage of successional development, the influence of a large canopy-forming neighbor species may be relatively unaffected by whether neighbors of that species are 5m vs. 10m from the focal tree - in this case the raw or proportional measures may be more relevant.

There is also an option to correct the density values for neighborhoods that overlap the stand boundary. When edge_correction = T, the density and species richness values for neighborhoods that overlap the stand boundary will be multiplied according to the proportion of their neighborhood that lies beyond the stand boundary (species richness values are rounded to the nearest whole number). See function documentation for details.

neighborhood_summary(densities = "raw")

Overall tree density and species-specific densities will be presented in units of \(m^{2}ha^{-1}\), with the area occupied by each tree calculated as the area of the trunk at breast height i.e. \[\pi(\frac{dbh}{2})^{2}\]

nbhd_summ <- neighborhood_summary(nbhds, id_column = "tree_id", radius = 10,
                                  densities = "raw")
nbhd_summ %>%
  select(tree_id, sps_richness, shannon, ABAM_density, TSHE_density, all_density)
#> # A tibble: 401 x 6
#>   tree_id       sps_richness shannon ABAM_density TSHE_density all_density
#>   <chr>                <int>   <dbl>        <dbl>        <dbl>       <dbl>
#> 1 AB08000100022            2   0.490         0            34.0        42.1
#> 2 AB08000100001            3   0.808         2.27         21.0        31.4
#> 3 AB08000100002            3   0.847         2.27         18.1        28.5
#> 4 AB08000100003            3   0.809         2.27         29.2        47.3
#> # ... with 397 more rows

neighborhood_summary(densities = "proportional")

Overall tree density is presented in units of \(m^{2}ha^{-1}\) but species-specific densities represent the proportion of overall tree density that each species accounts for.

nbhd_summ <- neighborhood_summary(nbhds, id_column = "tree_id", radius = 10,
                                  densities = "proportional")
nbhd_summ %>%
  select(tree_id, sps_richness, shannon, ABAM_density, TSHE_density, all_density)
#> # A tibble: 401 x 6
#>   tree_id       sps_richness shannon ABAM_density TSHE_density all_density
#>   <chr>                <int>   <dbl>        <dbl>        <dbl>       <dbl>
#> 1 AB08000100022            2   0.490       0             0.807        42.1
#> 2 AB08000100001            3   0.808       0.0721        0.669        31.4
#> 3 AB08000100002            3   0.847       0.0794        0.636        28.5
#> 4 AB08000100003            3   0.809       0.0479        0.618        47.3
#> # ... with 397 more rows

neighborhood_summary(densities = "angular")

Overall tree density and species-specific densities calculated as the sum of angles occupied by trees, where the angle occupied by a single tree is: \[arctan(\frac{dbh}{distance})\] In the above, dbh is the dbh of the neighbor tree and distance is the distance from the neighborhood center to the neighbor tree.

nbhd_summ <- neighborhood_summary(nbhds, id_column = "tree_id", radius = 10,
                                  densities = "angular")
nbhd_summ %>%
  select(tree_id, sps_richness, shannon, ABAM_angle_sum, TSHE_angle_sum,
         all_angle_sum)
#> # A tibble: 401 x 6
#>   tree_id       sps_richness shannon ABAM_angle_sum TSHE_angle_sum all_angle_sum
#>   <chr>                <int>   <dbl>          <dbl>          <dbl>         <dbl>
#> 1 AB08000100022            2   0.490         0               0.428         0.503
#> 2 AB08000100001            3   0.808         0.0379          0.671         0.779
#> 3 AB08000100002            3   0.847         0.0323          0.530         0.626
#> 4 AB08000100003            3   0.809         0.0391          0.509         0.805
#> # ... with 397 more rows

neighborhood_summary(neighbors_incl = "larger_only")

It is also possible to restrict the diversity and density calculations to include only the neighbor trees that are larger than the focal. This may be useful if trees in the study system are expected to compete far less strongly with neighbors that are smaller than them.

nbhd_summ <- neighborhood_summary(nbhds, id_column = "tree_id", radius = 10,
                                  densities = "angular",
                                  neighbors_incl = "larger_only")
nbhd_summ %>%
  select(tree_id, sps_richness, shannon, ABAM_angle_sum, TSHE_angle_sum,
         all_angle_sum)
#> # A tibble: 364 x 6
#>   tree_id       sps_richness shannon ABAM_angle_sum TSHE_angle_sum all_angle_sum
#>   <chr>                <int>   <dbl>          <dbl>          <dbl>         <dbl>
#> 1 AB08000100022            2   0.490         0               0.428         0.503
#> 2 AB08000100001            2   0.635         0               0.535         0.606
#> 3 AB08000100003            3   0.813         0.0391          0.479         0.774
#> 4 AB08000100004            1   0             0               0.174         0.174
#> # ... with 360 more rows

site_by_species(): Create site-by-species matrices

The site_by_species() function takes output of the neighborhoods() function and creates a site-by-species matrix where each site is a neighborhood. The resulting matrix can be used as input for functions in other packages (e.g. picante, vegan) that calculate various diversity metrics from site-by-species matrices.

The default is to present presence/absence data in the matrix:

ss_mat <- site_by_species(nbhds, id_column = "tree_id")
head(ss_mat)
#>               ABAM ABPR CANO PSME TABR THPL TSHE
#> AB08000100022    0    0    0    0    0    1    1
#> AB08000100001    1    0    0    0    0    1    1
#> AB08000100002    1    0    0    0    0    1    1
#> AB08000100003    1    0    0    0    0    1    1
#> AB08000100004    0    0    0    0    0    0    1
#> AB08000100005    1    0    0    0    0    1    1

But if abundance = T is specified, matrix element values will represent the number of trees of the associated species in the neighborhood:

ss_mat_abund <- site_by_species(nbhds, id_column = "tree_id", abundance = T)
head(ss_mat_abund)
#>               ABAM ABPR CANO PSME TABR THPL TSHE
#> AB08000100022    0    0    0    0    0    1    6
#> AB08000100001    1    0    0    0    0    1    5
#> AB08000100002    1    0    0    0    0    1    6
#> AB08000100003    1    0    0    0    0    2    7
#> AB08000100004    0    0    0    0    0    0    3
#> AB08000100005    1    0    0    0    0    2    8

tree_utm(): Calculate tree UTM coordinates

The tree_utm() function takes a mapping object as input and calculates UTM coordinates for each tree. The output spatial object can be used to extract further data for each tree from data sources with very high spatial resolution e.g. topography.

The required arguments for tree_utm() are: geographic coordinates of the stands (stand_locs), the ESPG code of the coordinate reference system of stand_locs (original_crs), and the ESPG code of the UTM zone in which the stands exist (utm_crs).

The built-in stand_locations dataset shows the expected structure of stand_locs - see ?stand_locations for details:

head(stand_locations)
#>   stand_id y_azim latitude longitude
#> 1     TO04  12.58 46.74002 -121.8887
#> 2     TB13  90.99 46.74086 -121.8487
#> 3     AX15 298.32 46.74979 -121.8242
#> 4     AG05  84.42 46.74765 -121.8052
#> 5     AV06  45.74 46.77602 -121.7851
#> 6     AM16  58.80 46.76848 -121.7585

The output of tree_utm() looks like the following:

mapping_utm <- tree_utm(mapping, stand_locations, 4326, 32610)
head(mapping_utm)
#> Simple feature collection with 6 features and 8 fields
#> Geometry type: POINT
#> Dimension:     XY
#> Bounding box:  xmin: 611415.1 ymin: 5197317 xmax: 611421.6 ymax: 5197329
#> Projected CRS: WGS 84 / UTM zone 10N
#>         tree_id stand_id  tag species year  dbh x_coord y_coord
#> 1 AB08000100022     AB08   19    TSHE 2012 15.1   18.11   95.68
#> 2 AB08000100001     AB08 4001    TSHE 2012 50.4   11.78   96.77
#> 3 AB08000100002     AB08 4002    TSHE 2012 81.1   12.47   97.95
#> 4 AB08000100003     AB08 4003    TSHE 2012 20.0   14.94   91.91
#> 5 AB08000100004     AB08 4004    TSHE 2012 53.7   22.09   99.06
#> 6 AB08000100005     AB08 4005    TSHE 2012 24.4   12.16   91.48
#>                   geometry
#> 1 POINT (611417.2 5197322)
#> 2 POINT (611420.8 5197327)
#> 3 POINT (611421.6 5197326)
#> 4 POINT (611415.1 5197326)
#> 5 POINT (611418.6 5197317)
#> 6 POINT (611415.9 5197329)