Quantify size- and distance-dependent competition using inventory data
Source:R/compete_inventory.R
compete_inv.Rd
compete_inv()
computes one or several distance-height- or
distance-dbh-dependent competition indices based on forest inventory data.
Usage
compete_inv(
inv_source,
target_source = "buff_edge",
radius,
method = "all_methods",
x = NULL,
y = NULL,
dbh = NULL,
height = NULL,
size = NULL,
id = NULL,
dbh_unit = c("cm", "m", "mm"),
height_unit = c("m", "cm", "mm"),
keep_rest = FALSE,
verbose = TRUE,
parallelize = FALSE,
cores = ifelse(!is.null(options("cores")[[1]]), options("cores")[[1]],
parallel::detectCores()),
tol = 1,
kmax = 999,
...
)
Arguments
- inv_source
either an object of class
forest_inv
ortarget_inv
, or any object that can be imported byread_inv()
. If the object is read in from a source path or an unformatted data.frame,x
,y
,id
,dbh
,height
and/orsize
can be specified as inread_inv()
(see the corresponding documentation for details). If the input is aforest_inv
ortarget_inv
object, the column definitions and further arguments passed toread_inv()
will be ignored. Fortarget_inv
objects, the function also ignorestarget_source
and overrides further arguments passed todefine_target()
.- target_source
one of the following:
a path to an any object that can be imported by
read_inv()
(in this case, column specifications have to be the same as ininv_source
- if this is not possible, load outside ofcompete_inv()
).a vector of class
"character"
containing the tree IDs identifying the target trees in the same format as in theid
column ofinv
,a vector of class
logical
specifying for each row ofinv
whether the corresponding tree is a target tree,another object of class
forest_inv
containing the coordinates of the target trees. In this case, the coordinates are matched against the coordinates ininv
and IDs may differ (useful e.g. when target trees are defined based on GPS coordinates and maWarumtched against an airborne laser scanning dataset).a character vector of length 1 defining the method by which the target trees should be determined. Allowed are
"buff_edge"
for excluding all trees that are at least one search radius from the forest edge,"exclude_edge"
for only excluding edge trees or"all_trees"
for including all trees of the dataset (which is hardly ever a good idea unless all trees in the entire forest are in the dataset). The standard is"buff_edge"
. Seedefine_target()
for details.
- radius
numeric of length 1. Search radius (in m) around the target tree. All neighboring trees within this radius are classified as competitors.
- method
character string assigning the competition index functions to calculate. Can either be a vector with one or several of
"CI_Hegyi"
,"CI_RK1"
,"CI_RK2"
,"CI_Braathe"
,"CI_RK3"
,"CI_RK4"
,"CI_size"
and/or the names of user-specified functions, or"all_methods"
(the default)."all_methods"
computes all built-in indices that can be calculated with the available data.- x
character of length 1 or name of the variable in
inv_source
containing the x coordinates of the tree in m. IfNULL
(default), the function tries to identify the x coordinate from the data.- y
character of length 1 or name of the variable in
inv_source
containing the y coordinates of the tree in meters. IfNULL
(default), the function tries to identify the y coordinate from the data.- dbh
character of length 1 or name of the variable in
inv_source
containing the diameter at breast height of the tree (by default in cm, but can be defined viadbh_unit
). IfNULL
(default), the function tries to identify the dbh from the data.- height
character of length 1 or name of the variable in
inv_source
containing the height of the tree (by default in m, but can be defined viaheigh_unit
). IfNULL
(default), the function tries to identify the height from the data.- size
character of length 1 or name of the variable in
inv_source
containing a generic size-related variable forCI_size()
. IfNULL
(default), the variable is not assigned.- id
character of length 1 or name of the variable in
inv_source
containing a unique tree ID. IfNULL
(default), the function tries to identify the ID from the data. If this is not possible, the trees are assigned a unique number. All IDs are coerced to character.- dbh_unit
character of length 1. Unit for the diameter measurements (one of "cm", "m" or "mm". defaults to "cm").
- height_unit
character of length 1. Unit for the diameter measurements (one of "m", "cm" or "mm". defaults to "m").
- keep_rest
logical of length 1. Keep additional variables in the inventory table besides x, y, dbh, size and id for filtering or further computations? Defaults to FALSE.
- verbose
logical of length 1. Should information about progress be printed? Defaults to TRUE.
- parallelize
logical of length 1. Should the computation of the competition indices be split over several cores? Defaults to FALSE.
- cores
numeric of length 1. If
parallelize = TRUE
, on how many cores should the computations be run on? Defaults to the value registered inoptions("cores")[[1]]
, or, if this is not available, toparallel::detectCores())
.- tol
numeric of length 1. Tolerance for the match with the tree coordinates. If coordinates are measured in the field with GPS, but
inv_source
contains x and y coordinates from a larger number of trees obtained by segmentation, this is the tolerance for the matching the forest inventory against the target tree positions. If no forest tree is within the tolerance to a target tree, no competition indices will be calculated for this tree and the function will return a warning.tol
defaults to 1 m, but should be chosen depending on the measurement accuracy of the GPS coordinates.- kmax
integer of length 1. maximum number of nearest neighbors to consider for determining the neighbor trees of each tree. Lower values speed up computation, which is likely not an issue unlike your inventory contains tens of thousands of trees, but is very relevant for big datasets. Defaults to 999, which should be reasonable in almost all forest settings, but has to be adjusted when there are warnings (especially for large search radii and/or stands with small trees). If
kmax
is larger than the number of trees in the plot, the latter is passed on ask
tonabor::knn()
.- ...
additional arguments passed on to
data.table::fread()
.
Value
object of classcompete_inv
: a modified data.table with the
position and size of the designated target tree(s) and one or more
competition indices depending on chosen method(s).
Details
compete_inv()
calculates one or several distance-dependent tree
competition indices based on forest inventory data. These can be obtained
either with classical forest inventory methods, or be derived from LiDAR
point clouds (see below).
Inventory data can be either loaded from source, imported from an object
inheriting from class data.frame
(i.e., data.frames, tibbles,
data.table objects etc.) or a forest_inv
type object created with
read_inv()
. compete_inv()
takes the same arguments for reading
inventory data and has the same flexibility as read_inv()
.
To compute competition indices for trees from an inventory dataset, it is
necessary to decide on the target trees for the analysis. While it is also
possible to calculate competition indices for all trees in the inventory,
this is almost never a good idea because unless the dataset covers all
trees in the entire forest, there will be intense edge effects for the
trees at the edge of the spatial extent of the dataset.
compete_inv()
allows to define target trees in a number of different
ways based on the function define_target()
that is called internally.
Available competition indices
All competition indices are calculated based on the subset of trees that are within the search radius of the target tree. The competition indices are computed according to the following equations, where \(d_i\) and \(h_i\) are the dbh and height of neighbor tree \(i\), \(d\) and \(h\) are dbh and height of the target tree, and \(dist_i\) is the distance from neighbor tree \(i\) to the target tree.
Diameter-based competition indices
CI_Hegyi introduced by Hegyi (1974):
\(CI_{Hegyi} = \sum_{i=1}^{n} d_{i} / (d \cdot dist_{i})\)CI_RK1 according to CI1 Rouvinen & Kuuluvainen (1997):
\(CI_{RK1} = \sum_{i=1}^{n} \mathrm{arctan}(d_{i} / dist_{i})\)CI_RK2 according to CI3 in Rouvinen & Kuuluvainen (1997):
\(CI_{RK2} =\sum_{i=1}^{n} (d_{i} / d) \cdot \mathrm{arctan}(d_{i} / dist_{i})\)
Height-based competition indices
CI_Braathe according to Braathe (1980):
\(CI_{Braathe} = \sum_{i=1}^{n} h_{i} / (h \cdot dist_{i})\)CI_RK3 according to CI5 in Rouvinen & Kuuluvainen (1997):
\(CI_{RK3} = \sum_{i=1}^{n} \mathrm{arctan}(h_{i} / dist_{i})\) for all trees with \(h_{i} > h\)CI_RK4 based on CI3 in Rouvinen & Kuuluvainen (1997) and Contreras et al. (2011):
\(CI_{RK4} = \sum_{i=1}^{n} (h_{i} / h) \cdot \mathrm{arctan}(h_{i} / dist_{i})\)
Generic size-based Hegyi-type competition index
CI_size based on Hegyi (1974), but with a user-specified size-related variable (\(s_i\): size for neighbor tree \(i\), \(s\): size of the target tree): \(CI_{size} = \sum_{i=1}^{n} s_{i} / (s \cdot dist_{i})\)
Further indices can be user-specified by developing a corresponding function (see competition_indices for details).
To efficiently deal with large inventory datasets (as can be expected from
ALS sources), the neighbors within the search radius are computed with
nabor::knn()
, which is based on an efficient C++ implementation
of the k-nearest neighbor algorithm from
libnabo. For this, it is
required to specify a maximum number of neighbors to take into account
(kmax
, which is passed to nabor::knn()
as k
) that should be chosen as
low as possible, but high enough to ensure that it is sufficiently larger
than the maximum number of trees expected in the search radius. When
working with small datasets (from classical forest inventories or MLS/TLS
sources), this will likely not matter, but when working with large ALS
datasets this implementation can speed up calculation by several orders of
magnitude.
For even larger datasets, it is possible to compute the indices in parallel
using foreach::foreach()
via the doParallel backend. If
parallelize = TRUE
, the computation of the competition indices will be
distributed onto cores
parallel cores.
As all these indices are distance-weighted sums of the relative size of all competitor trees within the search radius compared to the target tree (or a sum of transformations thereof), they are very sensitive to the choice of the search radius. It is not generally possible to meaningfully compare competition indices computed with different search radii. Over which distance competitors affect the growth of the central tree is certainly specific and likely also depends on the average size of trees of the same species as the target tree and how far its roots spread under the local conditions. Lorimer (1983) recommends to use 3.5 times the mean crown radius of the target trees, but it is likely that there is no single value that works well under all conditions, and possible that the same values of competition indices calculated with the same radius have different meanings for different species.
Tree Segmentation
Various approaches can be used to segment (airborne) laser scanning point clouds into single trees and to obtain inventory data based it. Existing R packages for this are for example:
TreeLS
package for automated segmentation of terrestrial/mobile laser scanslidR
package with different options to segment the point cloud or a Canopy Height Model (CHM)itcLiDARallo()
from the packageitcSegment
Be careful with low resolution/low density point clouds, as oversegmentation of trees is usually an issue!
For examples of workflows to obtain inventory dator from airborne laser scanning data or terrestrial/mobile laser scanning data, see ALS workflow and TLS workflow, respectively.
Literature
Hegyi, F., 1974. A simulation model for managing jackpine stands. In: Fries, J. (Ed.), Proceedings of IUFRO meeting S4.01.04 on Growth models for tree and stand simulation, Royal College of Forestry, Stockholm.
Braathe, P., 1980. Height increment of young single trees in relation to height and distance of neighboring trees. Mitt. Forstl. VersAnst. 130, 43–48.
Rouvinen, S., Kuuluvainen, T., 1997. Structure and asymmetry of tree crowns in relation to local competition in a natural mature Scot pine forest. Can. J. For. Res. 27, 890–902.
Contreras, M.A., Affleck, D. & Chung, W., 2011. Evaluating tree competition indices as predictors of basal area increment in western Montana forests. Forest Ecology and Management, 262(11): 1939-1949.
Lorimer, C.G., 1983. Tests of age-independent competition indices for individual trees in natural hardwood stands. For. Ecol. Manage. 6, 343–360.
See also
read_inv()
to read forest inventory data,
define_target()
for designating target trees,
competition_indices for a list of available indices,
plot_target()
to plot target tree positions in target_inv
and
compete_inv
objects.
Examples
if (FALSE) { # \dontrun{
# get target trees
targets4 <- readr::read_csv("data/inventory4.csv") %>%
dplyr::filter(plot_id == "Plot 1") %>%
read_inv(dbh = diam, verbose = FALSE) %>%
define_target(target_source = "buff_edge", radius = 8)
# compute Hegyi index based on existing target_inv object
CI1 <- compete_inv(inv_source = targets4, radius = 8,
method = "CI_Hegyi")
# compute Hegyi based on a file source
CI2 <- compete_inv(
inv_source = "data/inventory5.csv",
target_source = "buff_edge",
radius = 12,
method = "CI_Hegyi",
x = Koord_x,
y = Koord_y,
id = Baumname,
dbh = Durchmesser,
sep = ";",
dec = ","
)
# compute all built-in indices that are possible with a dataset
(CI3 <- compete_inv(inv_source = targets4,
radius = 8, method = "all_methods"))
# new implementation of Hegyi competition index
CI_Hegyi_new <- function(target, neigh)
sum(neigh$dbh / (target$dbh * neigh$dist))
# R1 index only for trees taller than the target tree
CI_RK1_tall <- function(target, neigh)
CI_RK1(target, neigh[neigh$height > target$height, ])
# get output for new indices
compete_inv(inv_source = targets4, radius = 8,
method = c("CI_Hegyi", "CI_Hegyi_new", "CI_RK1_tall"))
# partial Hegyi index for oak
CI_Hegyi_QURO <- function(target, neigh)
CI_Hegyi(target, neigh[neigh$species == "Quercus robur", ])
# partial Hegyi index for hornbeam
CI_Hegyi_CABE <- function(target, neigh)
CI_Hegyi(target, neigh[neigh$species == "Carpinus betulus", ])
# partial Hegyi index for ash
CI_Hegyi_FREC <- function(target, neigh)
CI_Hegyi(target, neigh[neigh$species == "Fraxinus excelsior", ])
# load dataset with species
inv_species <- read_inv("data/inventory6.csv", keep_rest = TRUE)
inv_species
# compute species-decomposed Hegyi indices
comp_species <- compete_inv(
inv_source = inv_species, target_source = "buff_edge", radius = 12,
method = c("CI_Hegyi_QURO", "CI_Hegyi_CABE", "CI_Hegyi_FREC", "CI_Hegyi")
)
} # }