How do I calculate the length of an episode and it's sequential number in R

Viewed 36

I am working with a dataframe where I have sequential time data for an animal moving around in a room. I have a column denoting when the animal is immobile (isImmobile = TRUE or FALSE) and I would like to create a new column showing the duration of the immobile episodes - in the below example it would be 3 frames - so I can exclude the ones that are too short.

I would also want to include a column which can tell me the number of immobile episodes.

Here is some example data:

   time  speed  zone     animal   group  week  distance period isImmobile
     
1  5.26  73.8 interzone  ctrl5    ctrl    0      50.3   p1     FALSE     
2  5.29  70.8 interzone  ctrl5    ctrl    0      1.30   p1     FALSE     
3  5.33  70   interzone  ctrl5    ctrl    0      1.40   p1     TRUE
4  5.36  56.3 interzone  ctrl5    ctrl    0      1.98   p1     TRUE     
5  5.39  51.7 interzone  ctrl5    ctrl    0      2.70   p1     TRUE     
6  5.43  47   interzone  ctrl5    ctrl    0      1.84   p1     FALSE

What I'm looking for is something like this:

   time  speed  zone     animal   group  week  distance period isImmobile  Ep_number Ep _Duration
     
1  5.26  73.8 interzone  ctrl5    ctrl    0      50.3   p1     FALSE       NA        NA      
2  5.29  70.8 interzone  ctrl5    ctrl    0      1.30   p1     FALSE       NA        NA
3  5.33  70   interzone  ctrl5    ctrl    0      1.40   p1     TRUE        1         3
4  5.36  56.3 interzone  ctrl5    ctrl    0      1.98   p1     TRUE        1         3
5  5.39  51.7 interzone  ctrl5    ctrl    0      2.70   p1     TRUE        1         3
6  5.43  47   interzone  ctrl5    ctrl    0      1.84   p1     FALSE       NA        NA

The frame can also completely omit the episodes when isImmobile = FALSE rather than having NA.

Preferably dplyr but anything else is also good :)

Thank you so much in advance. I also hope a similar question hasn't been asked yet - I couldn't find anything.

1 Answers

Here is one way with data.table - use rleid on the 'isImmobile' column to create a run-length-encoding column, then using the 'grp' as grouping column, and specifying the logical column in i, we assign two new columns with group index (.GRP) and the number of rows (.N)

library(data.table)
setDT(df1)[, grp := rleid(isImmobile)]
df1[as.logical(isImmobile), c("Ep_number", "Ep_duration") := 
   .(.GRP, .N), grp][, grp := NULL][]

-output

> time speed      zone animal  group  week distance period isImmobile Ep_number Ep_duration
   <num> <num>    <char> <char> <char> <int>    <num> <char>     <lgcl>     <int>       <int>
1:  5.26  73.8 interzone  ctrl5   ctrl     0    50.30     p1      FALSE        NA          NA
2:  5.29  70.8 interzone  ctrl5   ctrl     0     1.30     p1      FALSE        NA          NA
3:  5.33  70.0 interzone  ctrl5   ctrl     0     1.40     p1       TRUE         1           3
4:  5.36  56.3 interzone  ctrl5   ctrl     0     1.98     p1       TRUE         1           3
5:  5.39  51.7 interzone  ctrl5   ctrl     0     2.70     p1       TRUE         1           3
6:  5.43  47.0 interzone  ctrl5   ctrl     0     1.84     p1      FALSE        NA          NA

Or using base R with rle

cbind(df1, with(rle(df1$isImmobile), 
   data.frame(Ep_number = rep((NA^!values) * seq_len(values[values]), lengths),
    Ep_duration = rep((NA^!values) * lengths, lengths))))

data

df1 <- structure(list(time = c(5.26, 5.29, 5.33, 5.36, 5.39, 5.43), 
    speed = c(73.8, 70.8, 70, 56.3, 51.7, 47), zone = c("interzone", 
    "interzone", "interzone", "interzone", "interzone", "interzone"
    ), animal = c("ctrl5", "ctrl5", "ctrl5", "ctrl5", "ctrl5", 
    "ctrl5"), group = c("ctrl", "ctrl", "ctrl", "ctrl", "ctrl", 
    "ctrl"), week = c(0L, 0L, 0L, 0L, 0L, 0L), distance = c(50.3, 
    1.3, 1.4, 1.98, 2.7, 1.84), period = c("p1", "p1", "p1", 
    "p1", "p1", "p1"), isImmobile = c(FALSE, FALSE, TRUE, TRUE, 
    TRUE, FALSE)), class = "data.frame", row.names = c("1", "2", 
"3", "4", "5", "6"))
Related