Calculating home-range overlaps with amt

Johannes Signer and John Fieberg

2021-12-02

Background

Several different indices have been proposed for measuring home-range overlap. These are reviewed by Fieberg & Kochany (2005)1. There are two general approaches used to calculate home-range overlap: 1) calculate the percentage overlap at a given isopleth level (this works for geometric and probabilistic home ranges) or 2) calculate an index of similarity between the two utilization distributions (UD; this only works for probabilistic estimators)2.

Implementation in amt

amt currently implements all methods to calculate overlaps that were reviewed by Fieberg and Kochany (2005). These are:

These overlap indices can be calculated with the function hr_overlap. The type of overlap measure an be controlled with the argument type.

All of these estimators can be calculated for a given home-range level (i.e., using conditional UDs). Whether or not a conditional overlap is desired or not, can be controlled with the argument conditional. For hr, the argument conditional has no effect and the isopleths used for home-range estimation will always be used for the overlap calculation.

The function hr_overlap() can also be provided with a list of home-range estimates in situations when overlap between many different instances are required. Currently, there are three options for calculating overlap among multiple instances: which = "all" calculates overlap for each pair of home ranges, which = "one_to_all" calculates overlap between the first element in the list and all others, and which = "consecutive" will calculate overlap between consecutive elements in the list.

Examples

First we need to load the required packages:

library(amt)
library(ggplot2)
library(tidygraph)
library(ggraph)

Two instances

We will use tracking data from Fishers from New York State, USA.

leroy <- amt_fisher %>% filter(name == "Leroy")
lupe <- amt_fisher %>% filter(name == "Lupe")

Create a template raster for the KDE

trast <- make_trast(amt_fisher %>% filter(name %in% c("Leroy", "Lupe")), res = 50)

And estimate home-ranges for both fishers

hr_leroy <- hr_kde(leroy, trast = trast, levels = c(0.5, 0.9))
hr_lupe <- hr_kde(lupe, trast = trast, levels = c(0.5, 0.9))

hr and phr are directional, this means the order matters. For all other overlap measures the order does not matter.

hr_overlap(hr_leroy, hr_lupe, type = "hr") 
## # A tibble: 2 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1    0.5   0.191
## 2    0.9   0.309
hr_overlap(hr_lupe, hr_leroy, type = "hr")
## # A tibble: 2 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1    0.5   0.574
## 2    0.9   0.986

By default conditional = FALSE and the full UD is used.

hr_overlap(hr_leroy, hr_lupe, type = "phr", conditional = FALSE) 
## # A tibble: 1 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1      1       1
hr_overlap(hr_lupe, hr_leroy, type = "phr", conditional = FALSE)
## # A tibble: 1 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1      1   0.723

If we set conditional = TRUE, the overlap is measured at home-range levels that were specified during estimation.

hr_overlap(hr_leroy, hr_lupe, type = "phr", conditional = TRUE) 
## # A tibble: 2 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1    0.5   0.582
## 2    0.9   0.992
hr_overlap(hr_lupe, hr_leroy, type = "phr", conditional = TRUE)
## # A tibble: 2 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1    0.5   0.221
## 2    0.9   0.401

Note, for the remaining overlap measures the order does not matter. Below we show this for the volumnic intersection (type = "vi") as an example.

hr_overlap(hr_lupe, hr_leroy, type = "vi", conditional = FALSE)
## # A tibble: 1 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1      1   0.439
hr_overlap(hr_leroy, hr_lupe, type = "vi", conditional = FALSE)
## # A tibble: 1 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1      1   0.439

\(> 2\) instances

Lets calculate daily ranges for Lupe and then and then see how different ranges overlap with each other.

We have to use the same template raster in order to make ranges comparable.

trast <- make_trast(lupe, res = 50)

Then we add a new column with day and calculate for each day a KDE home range.

dat <- lupe %>% 
  mutate(week = lubridate::floor_date(t_, "week")) %>% 
  nest(data = -week) %>% 
  mutate(kde = map(data, hr_kde, trast = trast, levels = c(0.5, 0.95, 0.99)))

Now we can use the list column with the home-range estimates to calculate overlap between the different home-ranges. By default which = "consecutive", this means for each list entry (= home-range estimate) the overlap to the next entry will be calculated.

hr_overlap(dat$kde, type = "vi")
## # A tibble: 3 × 4
##    from    to levels overlap
##   <int> <int>  <dbl>   <dbl>
## 1     1     2      1  0.0432
## 2     2     3      1  0.551 
## 3     3     4      1  0.612

This works as well, if we set conditional = TRUE:

hr_overlap(dat$kde, type = "vi", conditional = TRUE)
## # A tibble: 9 × 4
##    from    to levels overlap
##   <int> <int>  <dbl>   <dbl>
## 1     1     2   0.5   0     
## 2     1     2   0.95  0.0264
## 3     1     2   0.99  0.0354
## 4     2     3   0.5   0.259 
## 5     2     3   0.95  0.528 
## 6     2     3   0.99  0.547 
## 7     3     4   0.5   0.317 
## 8     3     4   0.95  0.592 
## 9     3     4   0.99  0.608

Sometimes it can be useful to provide meaningful labels. We can do this with the labels argument.

hr_overlap(dat$kde, type = "vi", labels = dat$week)
## # A tibble: 3 × 4
##   from       to         levels overlap
##   <chr>      <chr>       <dbl>   <dbl>
## 1 2010-12-12 2010-12-19      1  0.0432
## 2 2010-12-19 2010-12-26      1  0.551 
## 3 2010-12-26 2011-01-02      1  0.612

Different options exist for the argument which. For example, which = "one_to_all" calculates the overlap between the first and all other home ranges.

Finally, we can calculate the overlap between all elements inside a list (use which = "all" for this). We will use the puechcir from the adehabitatMA package to illustrate this.

data("puechabonsp", package = "adehabitatMA")
dat <- puechabonsp$relocs %>% as.data.frame() %>% 
  make_track(X, Y, id = Name)
trast <- make_trast(dat, res = 50)
dat1 <- dat %>% nest(data = -id) %>% 
  mutate(kde = map(data, ~ hr_kde(., trast = trast, level = c(0.5, 0.9, 0.99))))

Now we can calculate the overlaps between animals:

ov2 <- hr_overlap(dat1$kde, type = "hr", labels = dat1$id, which = "all", 
                  conditional = TRUE) %>% 
  filter(overlap > 0)
graph <- as_tbl_graph(ov2) %>% 
  mutate(Popularity = centrality_degree(mode = 'in')) 

ggraph(graph, layout = 'stress') + 
  #geom_edge_fan(aes(col = overlap), show.legend = TRUE, arrow = arrow()) + 
  geom_edge_arc(aes(col = overlap), arrow = arrow(length = unit(4, 'mm'), type = "closed"), 
                start_cap = circle(3, 'mm'),
                end_cap = circle(3, 'mm')) + 
  geom_node_point(size = 4) + 
  geom_node_label(aes(label = name), repel = TRUE, alpha = 0.7) +
  facet_edges(~ levels, ncol = 2) + 
  theme_light() +
  scale_edge_color_gradient(low = "blue", high = "red")

Overlap between a home range and a simple feature

The function hr_overlap_feature allows to calculate percentage overlap (\(HR\) index) between a home. To illustrate this feature, we will use again the data from lupe and calculate the intersection with an arbitrary polygon.

poly <- bbox(lupe, buffer = -500, sf = TRUE)
poly1 <- bbox(lupe, sf = TRUE)
hr <- hr_mcp(lupe)
ggplot() + geom_sf(data = hr_isopleths(hr)) + 
  geom_sf(data = poly, fill = NA, col = "red") +
  geom_sf(data = poly1, fill = NA, col = "blue")

hr_overlap_feature(hr, poly, direction = "hr_with_feature")
## # A tibble: 1 × 3
##    from    to overlap
##   <dbl> <int>   <dbl>
## 1  0.95     1   0.828
hr_overlap_feature(hr, poly1, direction = "hr_with_feature")
## # A tibble: 1 × 3
##    from    to overlap
##   <dbl> <int>   <dbl>
## 1  0.95     1    1.00
hr_overlap_feature(hr, poly, direction = "feature_with_hr")
## # A tibble: 1 × 3
##    from    to overlap
##   <int> <dbl>   <dbl>
## 1     1  0.95   0.854
hr_overlap_feature(hr, poly1, direction = "feature_with_hr")
## # A tibble: 1 × 3
##    from    to overlap
##   <int> <dbl>   <dbl>
## 1     1  0.95   0.542

The same work with several home-range levels:

hr <- hr_mcp(lupe, levels = c(0.5, 0.9, 0.95))
hr_overlap_feature(hr, poly, direction = "hr_with_feature")
## # A tibble: 3 × 3
##    from    to overlap
##   <dbl> <int>   <dbl>
## 1  0.5      1   0.990
## 2  0.9      1   0.860
## 3  0.95     1   0.828

Todo

ctmm implements overlap for stationary dist with ci: https://besjournals.onlinelibrary.wiley.com/doi/10.1111/2041-210X.13027

Session

sessioninfo::session_info()
## ─ Session info ───────────────────────────────────────────────────────────────
##  setting  value                       
##  version  R version 4.1.2 (2021-11-01)
##  os       Ubuntu 20.04.3 LTS          
##  system   x86_64, linux-gnu           
##  ui       X11                         
##  language (EN)                        
##  collate  C                           
##  ctype    en_US.UTF-8                 
##  tz       Europe/Berlin               
##  date     2021-12-02                  
## 
## ─ Packages ───────────────────────────────────────────────────────────────────
##  package      * version date       lib source        
##  amt          * 0.1.5   2021-12-02 [1] local         
##  assertthat     0.2.1   2019-03-21 [3] CRAN (R 4.1.1)
##  backports      1.3.0   2021-10-27 [3] CRAN (R 4.1.1)
##  bslib          0.3.1   2021-10-06 [5] CRAN (R 4.1.1)
##  checkmate      2.0.0   2020-02-06 [3] CRAN (R 4.1.1)
##  class          7.3-19  2021-05-03 [6] CRAN (R 4.0.5)
##  classInt       0.4-3   2020-04-07 [3] CRAN (R 4.1.1)
##  cli            3.1.0   2021-10-27 [3] CRAN (R 4.1.1)
##  codetools      0.2-18  2020-11-04 [6] CRAN (R 4.0.3)
##  colorspace     2.0-2   2021-06-24 [3] CRAN (R 4.1.1)
##  crayon         1.4.2   2021-10-29 [3] CRAN (R 4.1.1)
##  DBI            1.1.1   2021-01-15 [3] CRAN (R 4.1.1)
##  digest         0.6.28  2021-09-23 [3] CRAN (R 4.1.1)
##  dplyr        * 1.0.7   2021-06-18 [3] CRAN (R 4.1.1)
##  e1071          1.7-9   2021-09-16 [3] CRAN (R 4.1.1)
##  ellipsis       0.3.2   2021-04-29 [3] CRAN (R 4.1.1)
##  evaluate       0.14    2019-05-28 [3] CRAN (R 4.1.1)
##  fansi          0.5.0   2021-05-25 [3] CRAN (R 4.1.1)
##  farver         2.1.0   2021-02-28 [3] CRAN (R 4.1.1)
##  fastmap        1.1.0   2021-01-25 [3] CRAN (R 4.1.1)
##  generics       0.1.1   2021-10-25 [3] CRAN (R 4.1.1)
##  ggforce        0.3.3   2021-03-05 [3] CRAN (R 4.1.1)
##  ggplot2      * 3.3.5   2021-06-25 [3] CRAN (R 4.1.1)
##  ggraph       * 2.0.5   2021-02-23 [3] CRAN (R 4.1.1)
##  ggrepel        0.9.1   2021-01-15 [3] CRAN (R 4.1.1)
##  glue           1.4.2   2020-08-27 [3] CRAN (R 4.1.1)
##  graphlayouts   0.7.1   2020-10-26 [3] CRAN (R 4.1.1)
##  gridExtra      2.3     2017-09-09 [3] CRAN (R 4.1.1)
##  gtable         0.3.0   2019-03-25 [3] CRAN (R 4.1.1)
##  highr          0.9     2021-04-16 [3] CRAN (R 4.1.1)
##  htmltools      0.5.2   2021-08-25 [3] CRAN (R 4.1.1)
##  igraph         1.2.7   2021-10-15 [3] CRAN (R 4.1.1)
##  jquerylib      0.1.4   2021-04-26 [3] CRAN (R 4.1.1)
##  jsonlite       1.7.2   2020-12-09 [3] CRAN (R 4.1.1)
##  KernSmooth     2.23-20 2021-05-03 [6] CRAN (R 4.0.5)
##  knitr          1.36    2021-09-29 [3] CRAN (R 4.1.1)
##  labeling       0.4.2   2020-10-20 [3] CRAN (R 4.1.1)
##  lattice        0.20-45 2021-09-22 [6] CRAN (R 4.1.1)
##  lifecycle      1.0.1   2021-09-24 [3] CRAN (R 4.1.1)
##  lubridate      1.8.0   2021-10-07 [3] CRAN (R 4.1.1)
##  magrittr       2.0.1   2020-11-17 [3] CRAN (R 4.1.1)
##  MASS           7.3-54  2021-05-03 [6] CRAN (R 4.0.5)
##  Matrix         1.3-4   2021-06-01 [6] CRAN (R 4.1.0)
##  munsell        0.5.0   2018-06-12 [3] CRAN (R 4.1.1)
##  pillar         1.6.4   2021-10-18 [3] CRAN (R 4.1.1)
##  pkgconfig      2.0.3   2019-09-22 [3] CRAN (R 4.1.1)
##  polyclip       1.10-0  2019-03-14 [3] CRAN (R 4.1.1)
##  proxy          0.4-26  2021-06-07 [3] CRAN (R 4.1.1)
##  purrr          0.3.4   2020-04-17 [3] CRAN (R 4.1.1)
##  R6             2.5.1   2021-08-19 [3] CRAN (R 4.1.1)
##  raster         3.5-2   2021-10-11 [3] CRAN (R 4.1.1)
##  rbibutils      2.2.4   2021-10-11 [3] CRAN (R 4.1.1)
##  Rcpp           1.0.7   2021-07-07 [3] CRAN (R 4.1.1)
##  Rdpack         2.1.2   2021-06-01 [3] CRAN (R 4.1.1)
##  rgdal          1.5-27  2021-09-16 [3] CRAN (R 4.1.1)
##  rgeos          0.5-8   2021-09-22 [3] CRAN (R 4.1.1)
##  rlang          0.4.12  2021-10-18 [3] CRAN (R 4.1.1)
##  rmarkdown      2.11    2021-09-14 [3] CRAN (R 4.1.1)
##  rstudioapi     0.13    2020-11-12 [5] CRAN (R 4.0.3)
##  sass           0.4.0   2021-05-12 [5] CRAN (R 4.0.5)
##  scales         1.1.1   2020-05-11 [3] CRAN (R 4.1.1)
##  sessioninfo    1.1.1   2018-11-05 [3] CRAN (R 4.1.1)
##  sf             1.0-3   2021-10-07 [3] CRAN (R 4.1.1)
##  sp             1.4-5   2021-01-10 [3] CRAN (R 4.1.1)
##  stringi        1.7.5   2021-10-04 [3] CRAN (R 4.1.1)
##  stringr        1.4.0   2019-02-10 [3] CRAN (R 4.1.1)
##  survival       3.2-13  2021-08-24 [6] CRAN (R 4.1.1)
##  terra          1.4-11  2021-10-11 [3] CRAN (R 4.1.1)
##  tibble         3.1.5   2021-09-30 [3] CRAN (R 4.1.1)
##  tidygraph    * 1.2.0   2020-05-12 [3] CRAN (R 4.1.1)
##  tidyr          1.1.4   2021-09-27 [3] CRAN (R 4.1.1)
##  tidyselect     1.1.1   2021-04-30 [3] CRAN (R 4.1.1)
##  tweenr         1.0.2   2021-03-23 [3] CRAN (R 4.1.1)
##  units          0.7-2   2021-06-08 [3] CRAN (R 4.1.1)
##  utf8           1.2.2   2021-07-24 [3] CRAN (R 4.1.1)
##  vctrs          0.3.8   2021-04-29 [3] CRAN (R 4.1.1)
##  viridis        0.6.2   2021-10-13 [3] CRAN (R 4.1.1)
##  viridisLite    0.4.0   2021-04-13 [3] CRAN (R 4.1.1)
##  withr          2.4.2   2021-04-18 [3] CRAN (R 4.1.1)
##  xfun           0.27    2021-10-18 [3] CRAN (R 4.1.1)
##  yaml           2.2.1   2020-02-01 [3] CRAN (R 4.1.1)
## 
## [1] /tmp/RtmpXBk6U6/Rinst292653af4766d
## [2] /tmp/RtmpURq9WN/temp_libpath2126b766c140d
## [3] /home/jsigner/R/x86_64-pc-linux-gnu-library/4.1
## [4] /usr/local/lib/R/site-library
## [5] /usr/lib/R/site-library
## [6] /usr/lib/R/library

  1. https://wildlife.onlinelibrary.wiley.com/doi/abs/10.2193/0022-541X%282005%2969%5B1346%3AQHOTIO%5D2.0.CO%3B2↩︎

  2. For a discussion of geometric vs. probabilistic estimators see here: https://www.biorxiv.org/content/10.1101/2020.08.19.256859v2↩︎