---
title: "Getting Started with soReta"
output:
  rmarkdown::html_vignette:
    md_extensions: -smart
vignette: >
  %\VignetteIndexEntry{Getting Started with soReta}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment  = "#>"
)
```

## What soReta does

`soReta` takes your camera-trap dataset and returns datasets ready for
statistical analysis: GLMM, GAMM, occupancy, kernel density and circular
activity analysis, temporal interactions between species, capture-mark-recapture -- without
you having to write the aggregation code yourself.

To do this, it needs a recordTable featuring a station (Station), a date/time (DateTimeOriginal), 
and a species (Species) column formatted in `camtrapR`'s standard style (Niedballa et al. 2016), along with a matching 
`camOp` effort matrix. Additionally, some functions require an individual-count column; unlike 
the core variables, this column does not need to follow camtrapR formatting.

This vignette walks through the main families of functions using
`soReta`'s own bundled example data: `camOp_soReta` (the effort matrix,
already built via `camtrapR::cameraOperation()` from `camtraps_soReta`,
a station table -- see `?camtraps_soReta` for its own format),
`recordTable_soReta` (species detections), and `recordTableIndividuals_soReta`
(individually-identifiable detections, for the capture-mark-recapture
section) -- all entirely synthetic; see `?camOp_soReta`,
`?recordTable_soReta`, and `?recordTableIndividuals_soReta` for details
on how they were built. 

```{r setup}
library(soReta)

head(camOp_soReta[, 1:6])   # only the first 6 of 120 days, or the table gets too wide
head(recordTable_soReta)
head(recordTableIndividuals_soReta)
```

## 1. GLMM/GAMM-ready datasets: the `build_site_*` and `build_day_total` family

These functions aggregate independent events into counts (N events) and Relative
Abundance Index (RAI, O'Brien et al. 2003), at whatever temporal grain you need. 
Thay all use a configurable threshold, in minutes (`threshold_min`), to determine which 
events count as independent. All of them recompute event independence from the raw 
timestamps (never relying on a pre-set threshold baked into the `recordTable`).

```{r}
# 1 row = 1 site x 1 day (no RAI column here: with n_days_active always
# equal to 1, RAI would just be N x 100 -- no extra information over N)
ds_day <- build_site_day(recordTable_soReta, camOp_soReta, threshold_min = 30)
head(ds_day[ds_day$N_sp > 0, ])
```

### Choosing how events are grouped: `independence_method` and `require_uninterrupted`

Before going further, it's worth understanding exactly how `threshold_min`
turns raw timestamps into "independent events" -- because two further
arguments (`independence_method`, `require_uninterrupted`), present on `build_site_day()` 
and on every other function in this package that takes a `threshold_min`, 
let you control that precisely. `"chain"/FALSE` are set as the defaults.
This choice is usually unsettled in the published literature, though, 
I encourage you to report which `independence_method`/`require_uninterrupted` combination you used if you publish results built with soReta, the same way you'd report `threshold_min` itself.


**`independence_method`** (`"chain"`, default, or `"window"`): `"chain"`
compares each photo only to the one immediately before it -- a
close-together run of photos can, in principle, span far longer than
`threshold_min`, as long as no single gap between consecutive photos
exceeds it. `"window"` instead compares each photo to the start of the
current bout: a bout can never last longer than `threshold_min`. The two
methods only disagree on bouts long enough to contain more than one
sub-threshold gap in a row -- illustrated below on a real sequence from
`recordTable_soReta` itself (a wolf pack at station S_02, three photos
16 and 25 minutes apart -- "chain" counts this as one event, "window" as
two, because the first 16-minute gap already used up most of the
30-minute budget before the second photo arrived):

```{r}
wolf_burst <- recordTable_soReta[
  recordTable_soReta$Station == "S_02" &
    recordTable_soReta$Species == "wolf" &
    format(recordTable_soReta$DateTimeOriginal, "%Y-%m-%d") == "2026-02-14",
]
wolf_burst
wolf_burst_camOp <- camOp_soReta["S_02", "2026-02-14", drop = FALSE]

build_site_day(wolf_burst, wolf_burst_camOp, threshold_min = 30, independence_method = "chain")$wolf_N
build_site_day(wolf_burst, wolf_burst_camOp, threshold_min = 30, independence_method = "window")$wolf_N
```


**`require_uninterrupted`** (`FALSE`, default, or `TRUE`): implements the
third independence criterion of O'Brien et al. (2003) --
the paper most commonly cited for the whole convention. Two photos of
the same species are only subject to the time threshold at all if they
are literally consecutive in the station's raw, all-species timeline:
if a photo of any other species falls chronologically between them, the
two are independent regardless of elapsed time. Another real sequence
from `recordTable_soReta` shows this directly (station S_04: a wolf,
then a red fox 10 minutes later, then a wolf again 10 minutes after
that):

```{r}
interrupted_burst <- recordTable_soReta[
  recordTable_soReta$Station == "S_04" &
    format(recordTable_soReta$DateTimeOriginal, "%Y-%m-%d") == "2026-03-10",
]
interrupted_burst

interrupted_burst_camOp <- camOp_soReta["S_04", "2026-03-10", drop = FALSE]

build_site_day(interrupted_burst, interrupted_burst_camOp, threshold_min = 30, require_uninterrupted = FALSE)$wolf_N
build_site_day(interrupted_burst, interrupted_burst_camOp, threshold_min = 30, require_uninterrupted = TRUE)$wolf_N
```

With `require_uninterrupted = FALSE` the two wolf photos are 20 minutes
apart (below the 30-minute threshold) and collapse into one event. With
`require_uninterrupted = TRUE` the red fox in between breaks the run, so
both wolf photos count as independent regardless of the 20-minute gap.

`independence_method` and `require_uninterrupted` combine freely, giving
four possible conventions in total. Every function below that takes a
`threshold_min` supports both -- from here on, this vignette just points
back to this section rather than re-explaining them each time.

O'Brien et al. (2003) actually define three criteria for independence: (1) consecutive photos of different individuals of the same or different species, (2) consecutive photos of the same species more than 0.5 hours apart, and (3) non-consecutive photos of the same species. `independence_method` implements criterion (2) (as "chain" or "window"), and `require_uninterrupted` implements criterion (3). Criterion (1) is deliberately not implemented: individual recognition is only feasible for some species (e.g. those with strong coat-pattern variation or sexual dimorphism) and not others, and applying it selectively -- crediting extra independent events only where individuals happen to be distinguishable -- would bias N and RAI inconsistently across species within the same study. Since most studies now monitor several species at once rather than a single one, this package does not implement criterion (1) for any species.

### The rest of the `build_site_*` family


The other functions in the `build_site_*` family perform the same task
as `build_site_day()`, but aggregate events at a different temporal
grain:

- `build_site_block()` -- a fixed-size window, in days, set via
  `block_days`
- `build_site_month()` -- calendar months
- `build_site_period()` -- named periods you define yourself:
  `period_names` sets their labels, `period_starts` their start dates

All three also take `min_days` (default `NULL`, no filtering): set it to
drop any site x interval combination where the station was active for
fewer than that many days -- useful for excluding a short, unreliable
block or month from the dataset rather than keeping it in with an
unreliably low sample size.

```{r}
# 1 row = 1 site x 1 fixed N-day block
ds_week <- build_site_block(recordTable_soReta, camOp_soReta, block_days = 7, threshold_min = 30)
head(ds_week)

# 1 row = 1 site x 1 calendar month
ds_month <- build_site_month(recordTable_soReta, camOp_soReta, threshold_min = 30)
head(ds_month)

# 1 row = 1 site x 1 named period, recurring every year -- our example
# data only spans January to April 2026, so a two-period split fits
# better here than a full four-season year
ds_period <- build_site_period(
  recordTable_soReta, camOp_soReta,
  period_names  = c("early", "late"),
  period_starts = c("01/01/2026", "01/03/2026"),
  threshold_min = 30
)
head(ds_period)
```

`build_site_block()`, `build_site_month()`, and `build_site_period()` all
include also a `mid_day` column: the day roughly halfway between the start and
end of each block/month/period, rounded down when the span is even so it
always lands on a real day. It's there to make joining an external daily
covariate straightforward -- lunar illumination, average temperature,
whatever your analysis needs -- without you having to compute a
representative date yourself:

```{r, eval = FALSE}
# example: joining a per-day covariate you already have, e.g. lunar fraction
ds_month |> dplyr::left_join(my_lunar_fraction, by = c("mid_day" = "Date"))
```

Station-level covariates (elevation, habitat type, distance to the
nearest road...) join just as directly -- every function in this
package returns a Station column, so a single left_join() is all it
takes:

```{r, eval = FALSE}
ds_month |> dplyr::left_join(my_station_covariates, by = "Station")
```

To exclude species you're not interested in (domestic animals, unclear identification, etc.) 
from every downstream calculation, filter recordTable
before calling any build_*() function -- RAI and richness are recomputed
correctly from whatever species remain:

```{r, eval = FALSE}
recordTable_soReta |> dplyr::filter(!Species %in% c("...")) |> build_site_month(camOp_soReta, threshold_min = 30)
```

Finally, two related functions collapse a different dimension:
`build_site_total()` gives one row per site over the whole survey --
handy for data exploration, community analyses (e.g. `vegan::vegdist()`),
and modelling -- while `build_day_total()` gives one row per calendar
day, summed across all stations. `build_day_total()` also returns the
number of active stations that day and RAI values, calculated as the
sum of events for each species divided by the number of active
stations that day, times 100, analogous to the classic RAI proposed by
O'Brien et al. (2003). This shape is specifically suited to GLMM/GAMM
analyses of behaviour in response to day-to-day environmental
variation, such as the lunar cycle, and can be readily further
aggregated for tests such as the chi-square test. Both functions also
take `independence_method`/`require_uninterrupted`, as explained above.

```{r}
# 1 row = 1 site
ds_site_tot <- build_site_total(recordTable_soReta, camOp_soReta, threshold_min = 30)
head(ds_site_tot)

# 1 row = 1 day
ds_day_tot <- build_day_total(recordTable_soReta, camOp_soReta, threshold_min = 30)
head(ds_day_tot)
```


## 2. Group size

For species where the number of animals per photo matters -- herd size,
pack size, flock size -- the `build_group_size_*` family mirrors the
`build_site_*` family, but tracks group size instead of just counting
events. All seven functions take a `countCol` argument: the name of the
column in `recordTable` holding the number of animals per photo. In
`recordTable_soReta` that column is called `"N_individuals"`. They also
support `independence_method` and `require_uninterrupted`, exactly as
explained in Section 1: the boundary of a "bout" is defined identically
here and in `build_site_*()`, so the event counts and the group sizes
always agree on where one passage ends and the next begins. `countCol`
is only needed for this family -- every other function in the package
works without it.

`build_group_size_events()` is the base of the family: one row per
independent event, with no temporal aggregation at all, not even
daily. It has no counterpart in `build_site_*()` -- there, an event is
always worth exactly 1 towards the count, so a raw, unaggregated
listing of events wouldn't add any information beyond `build_site_day()`
itself. Here, the raw group size per event is itself the finest-grained
information there is, before any averaging or summing collapses it.

Within a single bout, `group_size` is always the maximum count across
the photos in that bout, never the sum: summing would treat repeated
photos of the same passing group as if they were separate individuals.

```{r}
gr_size_ev <- build_group_size_events(recordTable_soReta, camOp_soReta, countCol = "N_individuals",
                         independence_method = "window", threshold_min = 30)
head(gr_size_ev)

```

`build_group_size_day()`, `build_group_size_block()`,
`build_group_size_month()`, `build_group_size_period()`,
`build_group_size_total()`, and `build_group_day_total()` aggregate
`build_group_size_events()` at the same temporal grains as the
`build_site_*` family, each producing three columns per species:
`<species>_mean_group_size`, `<species>_max_group_size`, and
`<species>_sum_group_size` -- plus `n_days_active`/`n_stations_active`,
`N_sp`, and `<species>_RAI_individuals` (= sum_group_size /
n_days_active * 100, the individual-based counterpart of the
event-based RAI in `build_site_*()`) on every level except the daily
one, where they would be redundant. Unlike `build_site_*()`, a species
with no events in a given day/period gets `NA` in the group-size
columns, not `0`: a group size of zero never happens, and using `0`
would silently distort any later averaging across days or periods.


```{r}
# most site x day x species combinations are genuinely empty
# at this grain, so filter for the informative rows
ds_group_day <- build_group_size_day(recordTable_soReta, camOp_soReta,
                                       countCol = "N_individuals",
                                       threshold_min = 30)
ds_group_day[!is.na(ds_group_day$red_deer_mean_group_size), ] |>
  head() |>
  print(width = 90)
```

```{r}
ds_group_total <- build_group_size_total(recordTable_soReta, camOp_soReta,
                                           countCol = "N_individuals",
                                           threshold_min = 30)
print(ds_group_total, width = 100)
```

## 3. Occupancy detection histories

`build_occupancy_day()` and `build_occupancy_block()` return, for each
species, a site x occasion matrix of 0/1/NA -- ready for `unmarked`,
`ubms`, or `spOccupancy`. `NA` marks an occasion with no active camera
effort at all; `0` means the station was active but the species was not
detected. `build_occupancy_day()` creates a daily matrix; `build_occupancy_block()`
aggregates days over a specified interval (`block_days`), as several
functions in the `build_site_*` and `build_group_size_*` families also
do. Only `build_occupancy_block()` takes `min_days` -- and, unlike
elsewhere in the package, it defaults to `1` here rather than `NA`:
a block needs at least one active day to be trusted as a real `0`
(absence) rather than left as `NA` (effort too low to draw any
conclusion). These two functions have no independence threshold at
all (presence/absence doesn't need one), so
`independence_method`/`require_uninterrupted` don't apply here.

```{r}
occ_day <- build_occupancy_day(recordTable_soReta, camOp_soReta)
head(occ_day[["wolf"]])

occ_week <- build_occupancy_block(recordTable_soReta, camOp_soReta, block_days = 7, min_days = 4)
head(occ_week[["wolf"]])
```

## 4. Kernel density and circular activity analysis

`extract_radians()` converts detection times into radians (clock time,
0 to 2*pi) -- the format `overlap::densityPlot()`, `overlap::overlapPlot()`,
and `activity::fitact()` expect, one vector per species. It computes
independence over the entire detection history before any subsetting,
so a threshold-based cut never splits one continuous visit into two.
Like the functions above, it also supports
`independence_method`/`require_uninterrupted` (Section 1).

```{r}
rad <- extract_radians(recordTable_soReta, threshold_min = 30)
names(rad)
# overlap::densityPlot(rad[["wolf"]], xcenter = "midnight")
```

With no further arguments, each species' result is one flat vector,
covering the whole dataset. The `group_col` argument splits it further
-- one nested list level per column you name, in that order -- useful
if you want a separate activity-pattern estimate per station, per
season, or both, without calling the function once per subgroup
yourself:

```{r}
recordTable_soReta$bimonth <- paste0("bim", ceiling(lubridate::month(recordTable_soReta$DateTimeOriginal) / 2))

# one level: split by station
rad_by_station <- extract_radians(recordTable_soReta, threshold_min = 30, group_col = "Station")
rad_by_station[["wolf"]][["S_01"]]

# two levels: station, then two-month period within station
rad_by_station_bimonth <- extract_radians(recordTable_soReta, threshold_min = 30,
                                            group_col = c("Station", "bimonth"))
names(rad_by_station_bimonth[["wolf"]][["S_01"]])   # check which periods actually
                                                       # exist for this species/station
                                                       # before indexing further
rad_by_station_bimonth[["wolf"]][["S_01"]][["bim1"]]
```

Don't confuse `group_col` with the separate `group_cols` argument.
`group_cols` decides which events get compared against each other when
checking independence -- by default, only events at the same station
and of the same species are compared, which is the right choice for
almost every study. `group_col`, instead, only decides how the
*already-computed* result gets organised into nested lists for you to
browse -- it never changes which photos count as one event.

One situation where you might genuinely want to change `group_cols`:
two camera traps placed a few meters apart, effectively watching the
same spot (a narrow trail, a den entrance). An animal walking past
could trigger both cameras within seconds of each other -- two
separate "events" at two different `Station` values, even though it
was really one single passage. Merging that pair into one combined
station name before calling `extract_radians()`, and passing that
combined name via `group_cols`, treats the two cameras as one:

```{r}
recordTable_soReta$Cluster <- ifelse(
  recordTable_soReta$Station %in% c("S_01", "S_02"),
  "S_01_S_02_cluster",
  recordTable_soReta$Station
)

# group_cols changes what counts as independent: the two real stations
# are now merged for this purpose
rad_clustered <- extract_radians(recordTable_soReta, threshold_min = 30,
                                   group_cols = c("Cluster", "Species"))

# group_col, unchanged in meaning: still just splits the finished
# result -- here, by the same clustered column, just to display it
rad_clustered_split <- extract_radians(recordTable_soReta, threshold_min = 30,
                                         group_cols = c("Cluster", "Species"),
                                         group_col  = "Cluster")
rad_clustered_split[["wolf"]][["S_01_S_02_cluster"]]
```

One more detail worth knowing before plotting: the radians this
function returns are in **clock time**, not solar time. For
comparisons across seasons or across sites at different latitudes,
convert the result with `activity::solartime()` or `overlap::sunTime()`
before handing it to `densityPlot()`/`fitact()` -- `extract_radians()`
itself does not do this conversion.

If you specifically need one separate object per species/group
combination in your environment -- to reuse an older script that was
already written that way -- `radians_to_env()` takes the (possibly
nested) list `extract_radians()` returns and creates one object per
vector, named by prefix + species + group levels:

```{r}
Rad_obj <- radians_to_env(rad_by_station, prefix = "Rad_", sep = "-")
Rad_obj
str(get(Rad_obj[1]))
```


## 5. Temporal interactions between species

`build_species_pair_intervals()` computes the AB/BA time intervals
between two species (Parsons et al. 2016, Niedballa et al. 2019): the time from a detection
of species A to the next detection of species B at the same station,
and vice versa. Intervals with no follow-up detection before the end
of a station's monitoring period are not discarded -- they are flagged
`censored = TRUE`, so you can hand them to a proper survival model
(`survival::survreg()`) instead of losing that information. It also
supports `independence_method`/`require_uninterrupted` (Section 1).

```{r}
pair_data <- build_species_pair_intervals(
  recordTable_soReta, camOp_soReta,
  speciesA = "wolf", speciesB = "wild boar",
  threshold_min = 30
)
head(pair_data)
```

Its output is exactly what all three of Niedballa et al. (2019)'s
AB/BA-level methods need, unchanged:

```{r}
# 1. linear model (log-transformed, as Niedballa et al. 2019 did to meet
#    linear model assumptions)
mod <- lm(log(delta_hours) ~ direction, data = pair_data[!pair_data$censored, ])
summary(mod)

# 2. Mann-Whitney U-test 
wilcox.test(delta_hours ~ direction, data = pair_data[!pair_data$censored, ])

# 3. permutation test (shuffle species labels, keeping real timestamps
#    and each species' total count fixed, recompute the AB/BA ratio
#    each time)
set.seed(1)
n_perm <- 999
rt_pair <- recordTable_soReta[recordTable_soReta$Species %in% c("wolf", "wild boar"), ]
obs_ratio <- median(pair_data$delta_hours[pair_data$direction == "AB" & !pair_data$censored]) /
  median(pair_data$delta_hours[pair_data$direction == "BA" & !pair_data$censored])
null_ratio <- replicate(n_perm, {
  rt_perm <- rt_pair
  rt_perm$Species <- sample(rt_perm$Species)
  out_perm <- build_species_pair_intervals(rt_perm, camOp_soReta, speciesA = "wolf", speciesB = "wild boar",
                                            threshold_min = 30)
  median(out_perm$delta_hours[out_perm$direction == "AB" & !out_perm$censored]) /
    median(out_perm$delta_hours[out_perm$direction == "BA" & !out_perm$censored])
})
mean(null_ratio >= obs_ratio, na.rm = TRUE)  # empirical p-value
```

**A methodological caution before interpreting any of these results**:
with two species detected at very different rates (a common situation),
a strong AB/BA asymmetry can appear even with no real behavioural
avoidance -- it's largely an artefact of how often each species is
detected at all (Dymit & Levi 2025). Before drawing conclusions, compare the two species'
overall detection rates (`build_site_total()` is the quickest way); the
permutation test above already accounts for this by holding each
species' total count fixed under the null.

`build_species_pair_interruptions()` covers the complementary AA/BB/ABA/BAB
intervals from the same framework (also supports
`independence_method`/`require_uninterrupted`). Unlike AB/BA,
**Niedballa et al. (2019) only applied the permutation test to these four
interval types** -- not the linear model or Mann-Whitney U-test used
above, which in their paper were reserved for AB/BA specifically:

```{r}
interruptions <- build_species_pair_interruptions(
  recordTable_soReta,
  speciesA = "wolf", speciesB = "wild boar",
  threshold_min = 30
)
table(interruptions$type)

# permutation test on AA vs BB (same logic as above, applied to the
# interruption-type intervals instead of AB/BA)
obs_ratio_aabb <- median(interruptions$delta_hours[interruptions$type == "AA"]) /
  median(interruptions$delta_hours[interruptions$type == "BB"])
null_ratio_aabb <- replicate(n_perm, {
  rt_perm <- rt_pair
  rt_perm$Species <- sample(rt_perm$Species)
  out_perm <- build_species_pair_interruptions(rt_perm, speciesA = "wolf", speciesB = "wild boar",
                                                threshold_min = 30)
  median(out_perm$delta_hours[out_perm$type == "AA"]) /
    median(out_perm$delta_hours[out_perm$type == "BB"])
})
mean(null_ratio_aabb >= obs_ratio_aabb, na.rm = TRUE)
```


## 6. Hierarchical diel activity models

`build_diel_binomial_block()`, `build_diel_binomial_month()`, and
`build_diel_binomial_period()` turn detection times into a binomial
success/failure dataset -- how many active days a species was detected
in each time-bin (`bin_hours` sets the width of each bin, e.g. 1 for
hourly bins), at whatever temporal grain you choose -- ready for the
trigonometric GLMMs and cyclic cubic spline HGAMs described in
Iannarilli et al. (2024)'s tutorial for hierarchical diel activity
models. There is no independence threshold here by design (see the
caution in Section 4): `detected` is already a per-day, per-bin binary
indicator, so more photos in the same bin don't change the result.

```{r, eval = requireNamespace("GLMMadaptive", quietly = TRUE) && requireNamespace("mgcv", quietly = TRUE)}
diel_month <- build_diel_binomial_month(recordTable_soReta, camOp_soReta, bin_hours = 1, min_days = 10)
diel_wolf  <- diel_month[diel_month$Species == "wolf", ]
diel_wolf$Station <- factor(diel_wolf$Station)

# trigonometric GLMM (Iannarilli et al. 2024, section 3.3)
trig_model <- GLMMadaptive::mixed_model(
  fixed  = cbind(success, failure) ~ cos(2 * pi * Time / 24) + sin(2 * pi * Time / 24) +
                                      cos(2 * pi * Time / 12) + sin(2 * pi * Time / 12),
  random = ~ 1 | Station,
  data   = diel_wolf,
  family = binomial()
)
summary(trig_model)
```

## 7. Classic capture-mark-recapture

For individually identifiable species, `build_cmr_day()` and
`build_cmr_block()` build an individual x occasion capture history from
a recordTable with an individual-ID column (the same shape as
camtrapR's own `recordTableIndividual()` output). Station plays no role
in the result. `as_capture_strings()`
converts that into the capture-history string format used by RMark,
MARK, and `marked`. Neither of these two functions has an independence
threshold: distinct individual sightings on the same day are already
unambiguous.

`recordTableIndividuals_soReta` provides this directly: 8
individually-recognizable red deer (`"RD_01"` to `"RD_08"`), built by
tagging the "red deer" detections already present in
`recordTable_soReta` -- same stations, same `camOp_soReta` used throughout
this vignette, no separate dataset or dependency needed.

```{r}
ch <- build_cmr_block(recordTableIndividuals_soReta, camOp_soReta, block_days = 7, min_days = 4)
as_capture_strings(ch)
```

## Where to go from here

Every function's help page (`?build_site_month`, `?build_species_pair_intervals`,
...) documents its parameters and return value in full. This vignette
only shows the default behaviour -- most functions have a `threshold_min`,
`min_days`, or similar argument worth tuning to your own study design.

## References

- Dymit, E., Garcia‐Anleu, R., Levi, T., 2025. Avoidance–attraction ratios incorrectly characterize behavioral interactions with camera trap data. Ecology 106, e70134. https://doi.org/10.1002/ecy.70134
- Iannarilli, F., Gerber, B.D., Erb, J., Fieberg, J.R., 2024. A ’how-to’ guide for estimating animal diel activity using hierarchical models. The Journal of animal ecology.
- Niedballa, J., Sollmann, R., Courtiol, A., Wilting, A., 2016. camtrapR: an R package for efficient camera trap data management. Methods Ecol Evol 7, 1457–1462. https://doi.org/10.1111/2041-210X.12600
- Niedballa, J., Wilting, A., Sollmann, R., Hofer, H., Courtiol, A., 2019. Assessing analytical methods for detecting spatiotemporal interactions between species from camera trapping data. Remote Sens Ecol Conserv 5, 272–285. https://doi.org/10.1002/rse2.107
- O’Brien, T.G., Kinnaird, M.F., Wibisono, H.T., 2003. Crouching tigers, hidden prey: Sumatran tiger and prey populations in a tropical forest landscape, in: Animal Conservation Forum. Cambridge University Press, pp. 131–139.
- Parsons, A.W., Bland, C., Forrester, T., Baker-Whatton, M.C., Schuttler, S.G., McShea, W.J., Costello, R., Kays, R., 2016. The ecological impact of humans and dogs on wildlife in protected areas in eastern North America. Biological Conservation 203, 75–88.
