The Fisher movement data was obtained from LaPoint, S., Gallery, P., Wikelski, M. and Kays, R. (2013). Animal behavior, cost-based corridor models, and real corridors. Landscape Ecology, 28, 1615-1630.
library(tidyverse)
library(amt)
library(copula)
library(cylcop) # available at https://github.com/floo66/cylcop
dat <- read_csv("Fisher.csv") %>%
filter(id %in% c(1465, 1466, 1072, 1078, 1016, 1469))
dat_all <- dat %>% nest(data=c(x,y,t))
dat_all$sex <- c("f", "f", "f", "m", "m", "m")
We then create a track from the data and convert it to an accurate projection for that geographic region with unit meters (epsg:5070).
dat_all <- dat_all %>%
mutate(trk = map(data, function(d) {
make_track(d, x, y, t, crs = sp::CRS("+init=epsg:4326")) %>%
transform_coords(sp::CRS("+init=epsg:5070"))
}))
Next, we show information on the sampling rate of the different individuals,
dat_all %>% mutate(sr = lapply(trk, summarize_sampling_rate)) %>%
select(id, sr) %>% unnest(cols=c(sr))
## # A tibble: 6 x 10
## id min q1 median mean q3 max sd n unit
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <int> <chr>
## 1 1072 3.92 9.8 10.0 20.7 10.3 1650. 95.2 1348 min
## 2 1465 0.4 1.97 2.03 8.93 3.98 1080. 48.1 3003 min
## 3 1466 0.417 1.97 2.07 15.7 4.08 2167. 104. 1500 min
## 4 1078 1.35 9.78 10.0 21.7 10.3 2111. 87.2 1637 min
## 5 1469 0.417 1.97 2.17 13.3 5.42 2889. 90.2 2435 min
## 6 1016 0.1 1.93 2.03 8.04 2.57 1209. 44.0 8957 min
resample tracks at a rate based on the sampling rates above,
dat_all <- dat_all %>% mutate(dat_clean = map(trk, ~ {
.x %>% track_resample(rate = minutes(10), tolerance = seconds(60))
}))
and convert to steps.
dat_all <-
dat_all %>%
mutate(stps = map(dat_clean, ~ .x %>% steps_by_burst()))
For fitting, we will pool together the step lengths and turn angles of all animals. The step length distributions are fitted to all available step lengths, not only to those where the corresponding turning angle is not NA.
dat_pool <- dat_all %>% select(stps) %>% unnest(cols=c(stps))
x <- dat_pool$sl_
theta <- dat_pool$ta_[!is.na(dat_pool$ta_)]
We can now fit distributions to the step lengths.
cylcop::fit_steplength(x, parametric = "gamma")
cylcop::fit_steplength(x, parametric = "weibull")
marg_dist <- cylcop::fit_steplength(x, parametric = "lognorm")
The lowest AIC is achieved with a log-normal distribution.
marg_dist
## $coef
## $coef$meanlog
## [1] 3.836631
##
## $coef$sdlog
## [1] 1.413075
##
##
## $se
## $se$meanlog
## [1] 0.01899871
##
## $se$sdlog
## [1] 0.01343412
##
##
## $logL
## [1] -30986.6
##
## $AIC
## [1] 61977.2
##
## $name
## [1] "lnorm"
Next, we fit circular distributions to the turn angles.
cylcop::fit_angle(theta, parametric = "mixedvonmises")
cylcop::fit_angle(theta, parametric = "vonmises")
cylcop::fit_angle(theta, parametric = "wrappedcauchy")
marg_angle <- cylcop::fit_angle(theta, parametric = "mixedvonmises", mu = c(0, pi))
The lowest AIC is achieved with a mixed von Mises distribution with fixed location parameters.
marg_angle
## $coef
## $coef$mu1
## [1] 0
##
## $coef$mu2
## [1] 3.141593
##
## $coef$kappa1
## [1] 0.5028997
##
## $coef$kappa2
## [1] 6.028979
##
## $coef$prop
## [1] 0.7710268
##
##
## $se
## list()
##
## $logL
## [1] -7817.993
##
## $AIC
## [1] 15641.99
##
## $name
## [1] "mixedvonmises"
First, we remove step lengths that correspond to NA in turn angles from the data.
x <- dat_pool$sl_[!is.na(dat_pool$ta_)]
Next, we investigate circular-linear correlation measures of the bivariate data.
cylcop::cor_cyl(theta = theta, x = x)
## [1] 0.1035015
cylcop::mi_cyl(
theta = theta,
x = x,
normalize = T,
symmetrize = T
)
## [1] 0.03765744
After this, we roughly estimate copula parameters using a grid search.
start_quadsec <-
cylcop::optCor(
copula = cyl_quadsec(),
theta = theta,
x = x,
method = "cor_cyl"
)
start_quadsec <-
cylcop::optCor(
copula = cyl_quadsec(),
theta = theta,
x = x,
method = "mi_cyl"
)
start_cubsec <-
cylcop::optCor(
copula = cyl_cubsec(),
theta = theta,
x = x,
method = "cor_cyl",
acc = 0.05,
n = 10000
)
For cyl_rect_combine copulae with symmetric rectangles spanning the entire unit square, we can calculate parameters from Kendall’s tau.
start_rect_frank <-
cylcop::optCor(
copula = cyl_rect_combine(
copula = frankCopula(),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
method = "tau"
)
start_rect_clayton <-
cylcop::optCor(
copula = cyl_rect_combine(
copula = claytonCopula(),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
method = "tau"
)
start_rect_gumbel <-
cylcop::optCor(
copula = cyl_rect_combine(
copula = gumbelCopula(),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
method = "tau"
)
Using these parameter values as starting points, we optimize copula parameters with a maximum pseudo likelihood approach.
copula_quadsec <-
cylcop::optML(
copula = cyl_quadsec(),
theta = theta,
x = x,
parameters = "a",
start = start_quadsec
)
copula_cubsec <-
cylcop::optML(
copula = cyl_cubsec(),
theta = theta,
x = x,
parameters = c("a", "b"),
start = start_cubsec,
traceOpt = T
)
copula_rect_frank <-
cylcop::optML(
copula = cyl_rect_combine(
copula = frankCopula(param = 2),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
parameters = "alpha",
start = start_rect_frank,
traceOpt = T
)
With a Clayton copula with negative parameter the rectangular patchwork copula fits the data very badly, and we therefore set the lower bound close to 0.
copula_rect_clayton <-
cylcop::optML(
copula = cyl_rect_combine(
copula = claytonCopula(),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
parameters = "alpha",
start = start_rect_clayton,
traceOpt = T,
lower = 0.0001
)
copula_rect_gumbel <-
cylcop::optML(
copula = cyl_rect_combine(
copula = gumbelCopula(),
low_rect = c(0, 0.5),
up_rect = "symmetric"
),
theta = theta,
x = x,
parameters = "alpha",
start = start_rect_gumbel,
traceOpt = T
)
With the following copula below, optimizing the upper bound of the lower rectangle (and with it the lower bound of the upper rectangle, because up_rect=“symmetric”) results either in rectangles of area 0 and a cyl_quadsec copula or in rectangles spanning the entire unit square, i.e. copula_rect_frank.
cylcop::optML(
copula = cyl_rect_combine(
copula = frankCopula(param = 2),
low_rect = c(0, 0.5),
up_rect = "symmetric",
background = cyl_quadsec()
),
theta = theta,
x = x,
parameters = c("alpha", "low_rect2", "bg_a"),
start = c(1.415, 0.357, 0.113),
lower = c(0.2, 0, 0),
upper = c(10, 0.45, 1 / (2 * pi)),
traceOpt = T
)
Instead, we arbitrarily set the rectangles to R_1=[0.0,0.3]x[0,1] and R_2=[0.7,1.0]x[0,1]
copula_rect_frank_quadsec <- cylcop::optML(
copula = cyl_rect_combine(
copula = frankCopula(param = 2),
low_rect = c(0, 0.3),
up_rect = "symmetric",
background = cyl_quadsec()
),
theta = theta,
x = x,
parameters = c("alpha", "bg_a"),
start = c(1.415, 0.113),
lower = c(0.0001, 0),
traceOpt = T
)
The copula giving the lowest AIC score was the cubic sections copula.
copula_cubsec
## $copula
## Cub. sect. copula
## a = 0.06694751
## b = -0.1591549
##
## $logL
## [1] 278.4446
##
## $AIC
## [1] -552.8892
sessionInfo()
## R version 4.0.2 (2020-06-22)
## Platform: x86_64-w64-mingw32/x64 (64-bit)
## Running under: Windows 10 x64 (build 19042)
##
## Matrix products: default
##
## locale:
## [1] LC_COLLATE=English_United States.1252
## [2] LC_CTYPE=English_United States.1252
## [3] LC_MONETARY=English_United States.1252
## [4] LC_NUMERIC=C
## [5] LC_TIME=English_United States.1252
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] cylcop_0.0.0.9000 copula_1.0-0 amt_0.1.4 forcats_0.5.0
## [5] stringr_1.4.0 dplyr_1.0.1 purrr_0.3.4 readr_1.3.1
## [9] tidyr_1.1.1 tibble_3.0.3 ggplot2_3.3.2 tidyverse_1.3.0
##
## loaded via a namespace (and not attached):
## [1] colorspace_1.4-1 ellipsis_0.3.1 rgdal_1.5-16
## [4] fs_1.5.0 rstudioapi_0.11 circular_0.4-93
## [7] gsl_2.1-6 fansi_0.5.0 mvtnorm_1.1-1
## [10] lubridate_1.7.9 xml2_1.3.2 codetools_0.2-16
## [13] splines_4.0.2 knitr_1.33 jsonlite_1.7.0
## [16] broom_0.7.6 dbplyr_1.4.4 GoFKernel_2.1-1
## [19] stabledist_0.7-1 shiny_1.5.0 compiler_4.0.2
## [22] httr_1.4.2 backports_1.1.7 assertthat_0.2.1
## [25] Matrix_1.2-18 fastmap_1.0.1 lazyeval_0.2.2
## [28] cli_2.0.2 later_1.2.0 htmltools_0.5.1.1
## [31] tools_4.0.2 gtable_0.3.0 glue_1.4.2
## [34] movMF_0.2-4 Rcpp_1.0.6 slam_0.1-48
## [37] cellranger_1.1.0 jquerylib_0.1.4 raster_3.3-13
## [40] vctrs_0.3.2 crosstalk_1.1.0.1 gbRd_0.4-11
## [43] xfun_0.23 rvest_1.0.0 mime_0.9
## [46] miniUI_0.1.1.1 lifecycle_1.0.0 MASS_7.3-51.6
## [49] scales_1.1.1 hms_0.5.3 promises_1.1.1
## [52] yaml_2.2.1 gridExtra_2.3 sass_0.4.0
## [55] stringi_1.4.6 highr_0.8 pcaPP_1.9-73
## [58] boot_1.3-25 bibtex_0.4.2.2 manipulateWidget_0.10.1
## [61] Rdpack_1.0.0 rlang_0.4.11 pkgconfig_2.0.3
## [64] rgl_0.100.54 sound_1.4.5 evaluate_0.14
## [67] lattice_0.20-41 htmlwidgets_1.5.1 tidyselect_1.1.0
## [70] magrittr_2.0.1 R6_2.5.0 generics_0.0.2
## [73] ADGofTest_0.3 DBI_1.1.0 pillar_1.4.6
## [76] haven_2.3.1 withr_2.2.0 survival_3.1-12
## [79] sp_1.4-2 pspline_1.0-18 modelr_0.1.8
## [82] crayon_1.3.4 KernSmooth_2.23-17 utf8_1.2.1
## [85] plotly_4.9.2.1 rmarkdown_2.8 viridis_0.5.1
## [88] grid_4.0.2 readxl_1.3.1 data.table_1.13.0
## [91] blob_1.2.1 infotheo_1.2.0 reprex_0.3.0
## [94] digest_0.6.25 webshot_0.5.2 xtable_1.8-4
## [97] httpuv_1.5.4 extraDistr_1.8.11 numDeriv_2016.8-1.1
## [100] stats4_4.0.2 munsell_0.5.0 viridisLite_0.3.0
## [103] bslib_0.2.5.1