In this tutorial, I cover:

  1. What is home range overlap?
  2. A few basic ways to measure it using the amt package.

The code and sample data from this tutorial are available on my tracking workshop GitHub.

Note: Go to the source and also read the amt vignette on overlapping home ranges.

Home range overlap

When measuring animal space use, we often want to measure how much an animal’s home range overlaps with another or how much one animal’s seasonal home range (or some other time chunk) overlaps with a home range at another time. For example, how much overlap is there between an animal’s home range between years.

Home range overlap is how much two animal’s home ranges overlap in area (e.g., using polygons to describe home ranges) or volume (e.g., using kernel density estimators or utilization distributions to describe home ranges).

The amt package is a great and intuitive R package for analyzing animal movement, including habitat selection and home ranges. The standard home range overlap indices (reviewed in Fieberg and Kochanny 2005) are implemented using the hr_overlap() function.

We will focus on two basic indices describing home range overlap, including:

  1. hr (proportion of overlapping area between individual i and individual j)
  2. vi (the intersection of volume between two utilization distributions)

To illustrate, let’s look at an example.

Calculating home ranges with amt

To get started, we need to estimate home ranges using the amt package.

In my previous tutorials, I’ve used adehabitatHR for estimating home ranges. However, for estimating home range overlap, we will work entirely within the amt package. We will use the same example dataset (turtles, from tracking_sample.csv) used in this series of tutorials.

library(dplyr) # for data organization
library(ggplot2) # for plotting
# install.packages("amt") # first time only
library(amt) # for movement and overlap

# Load tracking data
turtles <- read.csv("tracking_sample.csv", 
                    stringsAsFactors = FALSE) 

# Make an amt `track` object with our sample data set
turtles_track <- turtles %>%
  # Remove NA x and y rows
  filter(!is.na(x), !is.na(y)) %>%
  # Add formatted date-time
  # This stage can be tricky, so double-check those date values in your own data.
  mutate(ts = as.POSIXct(paste(date, time, sep = " "))) %>%
  # Make track with coordinates, date-time, id
  make_track(x, y, ts, id = id,
             # Make sure to specify coordinate reference system (CRS)
             crs = sp::CRS("+proj=utm +zone=18 +datum=WGS84 +units=m +no_defs")) %>%
  # Use nest() to allow us to deal with multiple animals (5 in sample set)
  # Each animal's track is stored in a tibble (table) nested within the data column
  nest(data = -"id") %>%
  arrange(id)

# Examine object. It is a tibble with a row for each individual. The value of 'data' contains the nested track_xyt object for each individual
turtles_track
## # A tibble: 5 × 2
##   id    data                
##   <chr> <list>              
## 1 T001  <track_xyt [29 × 3]>
## 2 T002  <track_xyt [33 × 3]>
## 3 T003  <track_xyt [40 × 3]>
## 4 T004  <track_xyt [27 × 3]>
## 5 T005  <track_xyt [39 × 3]>
# Examine one individual's track
head(turtles_track$data[1])
## [[1]]
## # A tibble: 29 × 3
##        x_      y_ t_                 
##     <int>   <int> <dttm>             
##  1 347725 4944678 2013-07-07 09:24:00
##  2 347670 4944599 2013-07-12 08:57:00
##  3 347682 4944619 2013-07-21 09:53:00
##  4 347662 4944609 2013-07-28 11:30:00
##  5 347877 4944554 2013-08-03 09:24:00
##  6 348037 4944498 2013-08-09 08:34:00
##  7 348035 4944496 2013-08-18 10:40:00
##  8 347828 4944548 2013-09-06 09:36:00
##  9 347593 4944543 2013-09-28 08:59:00
## 10 347635 4944489 2013-10-12 10:41:00
## # … with 19 more rows

The object turtles_track is a tibble where each row is an individual, and the “data” field includes a track. The track is a tibble that contains fields for coordinates (x_ and y_) and date-time (t_). Simply put, the relocations are stored in a table within the ‘data’ field corresponding to each ‘id’. This is a tidy way to store information, similar to how the sf package stores geometry data in a dataframe field.

Now, let’s estimate home ranges using Minimum Convex Polygons (MCPs) and then estimate how much the MCPs overlap in area between each animal in our data set using the hr method.

# Add MCP list-column to turtles_track using `map`
turtles_track <- turtles_track %>% 
  mutate(mcp = map(data, function(x) 
    # levels are for which proportions (1.0 = 100%)
    x %>% hr_mcp(levels = c(1.0)))) 

# Each id's MCP is stored in the list column "mcp"
# Check by plotting the first one
plot(turtles_track$mcp[[1]])

# Calculate overlap between T002 (row 2) and T004 (row 4)
# labels are from ID in track object 
# overlap is fraction of overlap between two home ranges
hr_overlap(turtles_track$mcp[[2]], 
           turtles_track$mcp[[4]], 
           labels = turtles_track$id[c(2,4)],
           type = "hr")
## # A tibble: 1 × 2
##   level overlap
##   <dbl>   <dbl>
## 1     1   0.441
# Calculate overlap between each of the five individuals
turtle_hr_overlap <- hr_overlap(turtles_track$mcp,
                                labels = turtles_track$id, 
                                which = "all", 
                                # alternative which = "consecutive",
                                # "one_to_all"
                                conditional = FALSE)

# Look at first 10 comparisons
head(turtle_hr_overlap, n = 10)
## # A tibble: 10 × 4
##    from  to    level overlap
##    <chr> <chr> <dbl>   <dbl>
##  1 T001  T002      1   0    
##  2 T001  T003      1   0    
##  3 T001  T004      1   0    
##  4 T001  T005      1   0.493
##  5 T002  T001      1   0    
##  6 T002  T003      1   0.146
##  7 T002  T004      1   0.441
##  8 T002  T005      1   0    
##  9 T003  T001      1   0    
## 10 T003  T002      1   0.201

Importantly, you’ll notice that the overlap between individuals depends on the direction of the comparison. You can see the home range overlap from T002 to T003 is not the same as from T003 to T002. They would only be the same if they have the same home range size. That’s because the proportion depends on the individual’s home range size (the “from”).

The hr_overlap function offers several other indices, so let’s try at least one more where the proportion of overlap considers the utilization distribution.

Home range overlap using utilization distributions

For our second example, we’ll consider overlap of utilization distributions using the ‘vi’ method, where Volumetric Index = vi. The order of individuals (to, from) does not affect the vi. To use this approach we must first estimate home ranges with utilization distributions.

# Make a track of the data that is not nested.
turtles_track_nonest <- turtles %>%
  # Remove NA x and y rows
  filter(!is.na(x), !is.na(y)) %>%
  mutate(x_ = x,
         y_ = y,
         t_ = as.POSIXct(paste(date, time, sep = " "))) %>%
                           dplyr::select(x_, y_, t_, id) %>%
  make_track(x_, y_, t_, id = id,
             # Make sure to specify coordinate reference system (CRS)
             crs = sp::CRS("+proj=utm +zone=18 +datum=WGS84 +units=m +no_defs"))
## .t found, creating `track_xyt`.
# Make a base raster for whole study. Without this common raster, 
# each Kernel Density Estimator will likely use different pixel sizes and 
# output of hr_overlap will be blank.
base_trast <- make_trast(turtles_track_nonest,
                         res = 50)

# Calculate the kernel density estimator for two turtles at 95%
hr_T002 <- hr_kde(turtles_track$data[2][[1]], trast = base_trast, levels = 0.95)
hr_T004 <- hr_kde(turtles_track$data[4][[1]], trast = base_trast, levels = 0.95)

hr_overlap(hr_T002, hr_T004, type = "vi", conditional = FALSE)
## # A tibble: 1 × 2
##   levels overlap
##    <dbl>   <dbl>
## 1      1   0.197

And that’s it! We estimated the proportion of home range overlap using just area with MCP’s (with hr_overlap(....,type = hr)), and then estimated the proportion of home range overlap using utilization distributions based on volumetric overlap (with hr_overlap(....,type = vi)). The choice between these and other indices will depend on your study system and question.

Thanks for reading!