# Neighborhood basal area with calba

This vignette shows how to compute neighborhood basal area with `calba`. We start from a simple simulated plot, then use `neigh_ba()` for model-ready summaries. We then illustrate multiple radii, low-level helper functions, and basic edge correction options.

## Example data

Simulate a modest plot with 500 trees, four species, 50x50 m extent, and basal areas between 20 and 200 (e.g., cm^2).

    library(calba)
    library(ggplot2)
    set.seed(42)
    side <- 50
    n <- 500
    trees <- data.frame(
      species = sample(c("oak", "pine", "birch", "maple"), n, replace = TRUE),
      gx = runif(n, 0, side),
      gy = runif(n, 0, side),
      ba = runif(n, 20, 200)
    )

    ggplot(trees, aes(gx, gy, col = species)) +
      geom_point() +
      coord_fixed() +
      theme_bw()

## Core summaries: `neigh_ba()`

`neigh_ba()` computes conspecific and total basal area, neighbor counts, and derived metrics for a single radius.

Optionally, we can add decay-weighted summaries by supplying `mu_values`. This appends a long table of distance-weighted basal area for each decay parameter, which is returned in the `decay` component.

    nb <- neigh_ba(
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r = 5,
      mu_values = c(2, 6)
    )

    head(nb$summary)

    ##   tree_id species   con_ba total_ba con_count total_count prop_con_ba    het_ba
    ## 1       1     oak 551.5438 2539.628         4          19   0.2171751 1988.0838
    ## 2       2     oak 232.2601 1534.218         3          14   0.1513866 1301.9581
    ## 3       3     oak 592.0395 1105.628         6          12   0.5354780  513.5886
    ## 4       4     oak 509.0855 1245.698         6          12   0.4086749  736.6124
    ## 5       5    pine 601.2436 2226.411         5          19   0.2700506 1625.1673
    ## 6       6   maple 394.3903 1969.297         4          17   0.2002696 1574.9068
    ##   het_count competition_index
    ## 1        15         133.66461
    ## 2        11         109.58701
    ## 3         6          92.13568
    ## 4         6         103.80816
    ## 5        14         117.17952
    ## 6        13         115.84101

We can inspect the full structure of the returned object:

    str(nb)

    ## List of 2
    ##  $ summary:'data.frame': 500 obs. of  10 variables:
    ##   ..$ tree_id          : int [1:500] 1 2 3 4 5 6 7 8 9 10 ...
    ##   ..$ species          : chr [1:500] "oak" "oak" "oak" "oak" ...
    ##   ..$ con_ba           : num [1:500] 552 232 592 509 601 ...
    ##   ..$ total_ba         : num [1:500] 2540 1534 1106 1246 2226 ...
    ##   ..$ con_count        : num [1:500] 4 3 6 6 5 4 6 8 1 1 ...
    ##   ..$ total_count      : num [1:500] 19 14 12 12 19 17 19 23 9 17 ...
    ##   ..$ prop_con_ba      : num [1:500] 0.217 0.151 0.535 0.409 0.27 ...
    ##   ..$ het_ba           : num [1:500] 1988 1302 514 737 1625 ...
    ##   ..$ het_count        : num [1:500] 15 11 6 6 14 13 13 15 8 16 ...
    ##   ..$ competition_index: num [1:500] 133.7 109.6 92.1 103.8 117.2 ...
    ##  $ decay  :'data.frame': 1000 obs. of  5 variables:
    ##   ..$ tree_id : int [1:1000] 1 2 3 4 5 6 7 8 9 10 ...
    ##   ..$ species : chr [1:1000] "oak" "oak" "oak" "oak" ...
    ##   ..$ mu      : num [1:1000] 2 2 2 2 2 2 2 2 2 2 ...
    ##   ..$ con_ba  : num [1:1000] 157.6 61.1 88.1 102.4 115.8 ...
    ##   ..$ total_ba: num [1:1000] 692 554 180 204 381 ...

nb$decay is a long table of decay-weighted basal area for each mu.

    head(nb$decay)

    ##   tree_id species mu    con_ba total_ba
    ## 1       1     oak  2 157.61987 692.3895
    ## 2       2     oak  2  61.13062 554.2654
    ## 3       3     oak  2  88.08783 180.0602
    ## 4       4     oak  2 102.40837 203.6150
    ## 5       5    pine  2 115.84979 380.6603
    ## 6       6   maple  2  62.66209 491.2417

## Edge handling and bounds

Use `edge_correction = "safe"` to return NA for focal trees within r of the plot boundary. By default, bounds are the min and max of gx and gy. Supply bounds = c(xmin, xmax, ymin, ymax) if your plot is larger than the observed coordinates.

    nb_bounds <- neigh_ba(
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r = 5,
      edge_correction = "safe",
      bounds = c(0, 50, 0, 50)
    )$summary

    head(nb_bounds)

    ##   tree_id species   con_ba total_ba con_count total_count prop_con_ba    het_ba
    ## 1       1     oak 551.5438 2539.628         4          19   0.2171751 1988.0838
    ## 2       2     oak       NA       NA        NA          NA          NA        NA
    ## 3       3     oak 592.0395 1105.628         6          12   0.5354780  513.5886
    ## 4       4     oak 509.0855 1245.698         6          12   0.4086749  736.6124
    ## 5       5    pine 601.2436 2226.411         5          19   0.2700506 1625.1673
    ## 6       6   maple       NA       NA        NA          NA          NA        NA
    ##   het_count competition_index
    ## 1        15         133.66461
    ## 2        NA                NA
    ## 3         6          92.13568
    ## 4         6         103.80816
    ## 5        14         117.17952
    ## 6        NA                NA

Then in Low-level helpers you keep the detailed explanation of the exponential and exponential-normal kernels and the `ba_decay()` code.

## Multiple radii in one pass: `neigh_multi_r()`

Reuse a single distance structure across radii.

    multi <- neigh_multi_r(
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r_values = c(3, 5, 10),
      dist_weighted = FALSE
    )

    multi_r5 <- subset(multi, radius == 5)
    head(multi_r5)

    ##     tree_id species radius   con_ba total_ba con_count total_count prop_con_ba
    ## 501       1     oak      5 551.5438 2539.628         4          19   0.2171751
    ## 502       2     oak      5 232.2601 1534.218         3          14   0.1513866
    ## 503       3     oak      5 592.0395 1105.628         6          12   0.5354780
    ## 504       4     oak      5 509.0855 1245.698         6          12   0.4086749
    ## 505       5    pine      5 601.2436 2226.411         5          19   0.2700506
    ## 506       6   maple      5 394.3903 1969.297         4          17   0.2002696
    ##        het_ba het_count competition_index
    ## 501 1988.0838        15         133.66461
    ## 502 1301.9581        11         109.58701
    ## 503  513.5886         6          92.13568
    ## 504  736.6124         6         103.80816
    ## 505 1625.1673        14         117.17952
    ## 506 1574.9068        13         115.84101

## Low-level helpers

`calba` provides several lower-level functions used by `neigh_ba()` and `neigh_multi_r()`:

- `ba_simple()` for unweighted or distance-weighted basal area at one radius
- `ba_decay()` for decay kernels (exponential or exponential-normal)
- `count_con()` and `count_total()` for neighbor counts

### Decay kernels in `ba_decay()`

`ba_decay()` computes distance–weighted basal area using either of two kernels.

Exponential:

\[ w(d, \mu) = \exp\left(-\frac{d}{\mu}\right) \]

Exponential-normal (Gaussian in distance):

\[ w(d, \mu) = \exp\left(-\frac{d^2}{\mu^2}\right) \]

Neighbors contribute `ba * w(d, mu)` to the focal tree, where `d` is Euclidean distance and `mu` controls the decay scale.

    mu_vals <- c(2, 6)

    decay_exp <- ba_decay(
      mu_values = mu_vals,
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r = 5,
      exponential_normal = FALSE
    )
    head(decay_exp$con_ba_matrix)

    ##           [,1]     [,2]
    ## [1,] 157.61987 334.6172
    ## [2,]  61.13062 143.7360
    ## [3,]  88.08783 306.0831
    ## [4,] 102.40837 281.1512
    ## [5,] 115.84979 342.8400
    ## [6,]  62.66209 210.8749

    decay_expnorm <- ba_decay(
      mu_values = mu_vals,
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r = 5,
      exponential_normal = TRUE
    )
    head(decay_expnorm$con_ba_matrix)

    ##           [,1]     [,2]
    ## [1,] 141.78479 394.8385
    ## [2,]  52.13371 177.6364
    ## [3,]  30.15202 375.5627
    ## [4,]  77.55324 338.8034
    ## [5,]  52.24974 433.3304
    ## [6,]  19.18204 263.5829

### Unweighted or distance-weighted basal area: ba_simple()

    ba_simple_out <- ba_simple(
      sp = trees$species,
      gx = trees$gx,
      gy = trees$gy,
      ba = trees$ba,
      r = 5,
      dist_weighted = TRUE
    )

    str(ba_simple_out)

    ## List of 2
    ##  $ con_ba  : num [1:500] 315.2 91.1 156 169.9 184.3 ...
    ##  $ total_ba: num [1:500] 1328 1224 307 356 668 ...

### Neighbor counts

    count_total_out <- count_total(trees$gx, trees$gy, r = 5)
    head(count_total_out)

    ## [1] 19 14 12 12 19 17