Example

library("aof")
library("bcpa")
#> Loading required package: Rcpp
#> Loading required package: plyr

A breakpoint-based method to detect ontogenetic shifts in univariate time-activity budget series of central-place foraging insects. The method finds a single changepoint in time series where parameters change at some unknown timepoints t* is done by simply sweeping all possible breaks, and finding the most-likely changepoint according to the likelihood. The method was developed with honey bees in order to detect the Age at Onset of Foraging (AOF), but can be used for the detection of other ontogenetic shifts in other central-place foraging insects. For more details, see Requier et al. (2020) Measuring ontogenetic shifts in central-place foraging insects: a case study with honey bees. Journal of Animal Ecology.

# mu1 and mu2: behavioural values at stage 1 and stage 2. 
# Both values mu1 and mu2 are equal (e.g. mu1=mu2=50) if no behavioural change is 
# simulated, or different (e.g. mu1=25 and mu2=50) if behavioural change is 
# simulated.
# rho1 and rho2 : interval frequency (default value 0.5 for both stages) 
# n.obs: no. observations randomly selected in the time series, from 5 to 45
# sigma1 and sigma2: variance around the behavioural value, from 0.1 to 3
# t.full: time series from 0 to 50
# n.obs: no. observations randomly selected in the time series, from 5 to 45
# t.break: the time of the simulated behavioural change (default value of 25)

getTimeBudget <- function(
  mu1 = 50,
  mu2 = 50,
  rho1 = 0.5,
  rho2 = 0.5,
  n.obs = 5, 
  sigma1 = 3,
  sigma2 = 3,
  t.full = 0:50,
  t.break = 25
){
  SimTS <- function(n, mu, rho, sigma){
    X.standard <- arima.sim(n, model = list(ar = rho))
    X.standard/sd(X.standard)*sigma + mu
  }
  x.full <- c(SimTS(t.break, mu1, rho1, sigma1),
    SimTS(max(t.full) - t.break + 1, mu2, rho2, sigma2))
  keep <- sort(sample(1:length(x.full), n.obs))
  TimeBudget <- data.frame(
    name = "A",
    Age = t.full[keep],
    x = x.full[keep])
  return(TimeBudget)
}
getAofPlot <- function(
  TimeBudget = TimeBudget, 
  AOF = AOF, 
  t.break = 25, 
  poly = FALSE,
  ylabX = "x"
){
  oldpar <- par(no.readonly =TRUE)
  par(mar = c(4, 4, 1, 1), mfrow = c(1, 1))
  plot(
    x = TimeBudget$Age,
    y = TimeBudget$x, 
    las = 1,
    xlab = "Age (day)", ylab = ylabX,
    pch = 21,
    col = "gray20", bg = "gray80")
  goodbreak1 = max(TimeBudget$Age[TimeBudget$Age < t.break])
  goodbreak2 = min(TimeBudget$Age[TimeBudget$Age >= t.break])
  if(poly == TRUE){
    polygon(
      c(
        goodbreak1 - 0.5, 
        goodbreak2 + 0.5, 
        goodbreak2 + 0.5, 
        goodbreak1 - 0.5),
      c(0, 0, 100, 100), 
      col = "gray50", border = NA)
  }
  abline(v = AOF$AOF, col = "black", lwd = 2, lty = 3)
  lines(
    x = c(min(TimeBudget$Age), AOF$AOF), 
    y = c(AOF$behav.stage1, AOF$behav.stage1), 
    col = "black", lwd = 2)
  lines(
    x = c(AOF$AOF, max(TimeBudget$Age)),
    y = c(AOF$behav.stage2, AOF$behav.stage2),
    col = "black", lwd = 2)
  par(oldpar)
  return(c(goodbreak1, goodbreak2))
}

1. Examples with no change simulated in the time series

1.1. Low number of individuals (N, n.obs) and low variance (V, sigma)

#> [1] 15 34

1.2. Low number of individuals and high variance

#> [1] 15 32

1.3. High number of individuals and low variance

#> [1] 24 25

1.4. High number of individuals and high variance

#> [1] 24 25

2. Examples with change simulated in the time series

2.1. Low number of individuals and low variance

#> [1] 15 27

2.2. Low number of individuals and high variance

#> [1] 19 29

2.3. High number of individuals and low variance

#> [1] 24 25

2.4. High number of individuals and high variance

#> [1] 24 25

3. Real dataset

This is a subset of 5 bees randomly selected in the experimental design of Requier et al. (J. Animal Ecology).

TimeBudget <- dataExample
head(TimeBudget, n = 25)
#>                name Age Number  Duration    Time
#> 1  A00103C00020FD24  15      2   63.5000 55166.0
#> 2  A00103C00020FD24  20      1  176.0000 71609.0
#> 3  A00103C00020FD24  22      4 1708.5000 41340.0
#> 4  A00103C00020FD24  23      5 2762.2000 42941.0
#> 5  A00103C00020FD24  24      1  562.0000 67521.0
#> 6  A00103C00020FD24  26      2  695.0000 40153.0
#> 7  A00103C00020FD24  27      1  700.0000 48959.0
#> 8  A00103C000402590  14      1   22.0000 64725.0
#> 9  A00103C000402590  16      1  227.0000 45238.0
#> 10 A00103C000402590  23      7  205.5714 62908.0
#> 11 A00103C000402590  24     14  360.1429 54300.0
#> 12 A00103C000402590  25      3  357.3333 64024.0
#> 13 A00103C000402590  26     10  680.4000 56767.5
#> 14 A00103C000402590  27      7  781.1429 56320.0
#> 15 A00103C000402590  28      3 2786.3333 51801.0
#> 16 A00103C000402590  29      1    8.0000 42236.0
#> 17 A00103C000406D60  13      1   14.0000 56166.0
#> 18 A00103C000406D60  14      2   89.0000 60514.5
#> 19 A00103C000406D60  15      4  150.5000 46005.5
#> 20 A00103C000406D60  16      1    4.0000 68591.0
#> 21 A00103C000406D60  17      2  330.5000 58752.5
#> 22 A00103C000406D60  18      3  781.0000 54998.0
#> 23 A00103C000406D60  20      4 1981.0000 57991.0
#> 24 A00103C000406D60  22      2  735.0000 52296.5
#> 25 A00103C000406D60  23      7 1908.2857 44964.0
# working on Number of trips
varX <- "Number"
AOF_number <- aof(
  name = TimeBudget[,1], 
  Age = TimeBudget[,2], 
  x = TimeBudget[varX])
#> 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |======================================================================| 100%
print(AOF_number)
#>               name parameter        AOF behav.total behav.stage1 behav.stage2
#> 1 A00103C00020FD24    Number undetected    2.285714           NA           NA
#> 2 A00103C000402590    Number         23    5.222222     3.000000     6.333333
#> 3 A00103C000406D60    Number         20    3.300000     2.428571     5.333333
#> 4 A00103C00040A5D5    Number undetected    1.636364           NA           NA
#> 5 A00103C00040A5F4    Number         23   11.125000     1.333333    17.000000
# working on Duration of trips
varX <- "Duration"
AOF_duration <- aof(
  name = TimeBudget[,1], 
  Age = TimeBudget[,2], 
  x = TimeBudget[varX])
#> 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |======================================================================| 100%
print(AOF_duration)
#>               name parameter AOF behav.total behav.stage1 behav.stage2
#> 1 A00103C00020FD24  Duration  20    952.4571    119.75000    1285.5400
#> 2 A00103C000402590  Duration  25    603.1026    234.40952    1063.9690
#> 3 A00103C000406D60  Duration  17    757.0571    117.60000    1396.5143
#> 4 A00103C00040A5D5  Duration  25    798.9091     81.64286    2054.1250
#> 5 A00103C00040A5F4  Duration  24    228.5120    211.82143     245.2026
# working on Time of trips
varX <- "Time"
AOF_time <- aof(
  name = TimeBudget[,1], 
  Age = TimeBudget[,2], 
  x = TimeBudget[varX])
#> 
  |                                                                            
  |                                                                      |   0%
  |                                                                            
  |==============                                                        |  20%
  |                                                                            
  |============================                                          |  40%
  |                                                                            
  |==========================================                            |  60%
  |                                                                            
  |========================================================              |  80%
  |                                                                            
  |======================================================================| 100%
print(AOF_time)
#>               name parameter AOF behav.total behav.stage1 behav.stage2
#> 1 A00103C00020FD24      Time  20    52527.00     63387.50     48182.80
#> 2 A00103C000402590      Time  23    55368.83     57623.67     54241.42
#> 3 A00103C000406D60      Time  18    54637.80     57504.58     50337.62
#> 4 A00103C00040A5D5      Time  25    56072.86     59164.14     50663.12
#> 5 A00103C00040A5F4      Time  25    59626.88     62104.80     55497.00
goodbreak <- lapply(seq_along(unique(TimeBudget[,1])), function(i){
  print(as.character(unique(TimeBudget[,1])[i]))
  oldpar <- par(no.readonly =TRUE)
  par(mfrow = c(1, 3))
  varXList <- list("Number", "Duration", "Time")
  varAOFList <- list(AOF_number, AOF_duration, AOF_time)
  varYlabList <- list("Trip number (per day)", "Trip duration (seconds)", "Trip time (seconds)")
  varXRes <- sapply(1:3, function(j){
    TimeBx <- TimeBudget[
      TimeBudget[,1] == unique(TimeBudget[,1])[i], 
      c("name", "Age", varXList[[j]])]
    names(TimeBx)[3] <- "x"
    getAofPlot(
      TimeBudget = TimeBx, 
      AOF = varAOFList[[j]][i,], 
      ylabX = varYlabList[[j]])
  })
  par(oldpar)
  return(varXRes)
})
#> [1] "A00103C00020FD24"

#> [1] "A00103C000402590"

#> [1] "A00103C000406D60"

#> [1] "A00103C00040A5D5"

#> [1] "A00103C00040A5F4"