Home > OS >  How to calculate length of time before a variable switches?
How to calculate length of time before a variable switches?

Time:10-15

I have a data from an activity monitor where I need to calculate the length of time asleep vs awake. For each 5 minute time point, I have marked whether or not the subject was asleep vs. awake during that period. I cannot figure out how to calculate how long the animal is continuously asleep before the "state" column switches to awake (aka, if there were two "awake" rows in a row and then the state switched backed to "sleep", I would want R to tell me that the subject was awake for the 8:00-8:05 and 8:05-8:10 period).

Finally, I'd like to then be able to say, in an entire day, what is the average length of time of an awake period vs a sleeping period?

Many thanks!! Example of my df below.

timeofday<-c("8:00","8:05","8:10","8:20","8:25")
activity<-c(1250,1650,200,100,40)
state<-c("awake","awake","sleep","sleep","sleep")
data_frame(timeofday,state,activity

enter image description here

CodePudding user response:

I am not sure what kind of output you are looking for but you can try this approach -

library(dplyr)
library(lubridate)

df %>%
  mutate(timeperiod = hm(timeofday), 
         group = data.table::rleid(state)) %>%
  group_by(group) %>%
  summarise(timeofday = paste(first(timeofday), last(timeofday), sep = '-'), 
            state = first(state), 
            timeperiod = last(timeperiod) - first(timeperiod))

#  group timeofday state timeperiod
#  <int> <chr>     <chr> <Period>  
#1     1 8:00-8:05 awake 5M 0S     
#2     2 8:10-8:25 sleep 15M 0S    

CodePudding user response:

Here is a data.tableapproach

library(data.table)
setDT(mydata)
mydata[, timeofday := as.ITime(timeofday)]
# create end times, or else you will get gaps
mydata[, timeofday_2 := shift(timeofday, type = "lead")]
mydata[is.na(timeofday_2), timeofday_2 := timeofday]
# summarise
mydata[, .(state = state[1], 
           from = min(timeofday), 
           to = max(timeofday_2)), 
       by = .(period = rleid(state))]
#    period state     from       to
# 1:      1 awake 08:00:00 08:10:00
# 2:      2 sleep 08:10:00 08:25:00

CodePudding user response:

Base R solution:

# Coerce the time vector to appropriate type: timeofday => POSIXlt vector
df$timeofday <- strptime(
  df$timeofday, 
  "%H:%M"
)

# Numericise the state of the animal: state_no => integer vector
df$state_no <- as.integer(
  factor(
    df$state,
    levels = c("awake", "rest", "sleep"),
    ordered = TRUE
  )
)

# Group the patient state: grp => integer vector
df$grp <- with(
  df[order(timeofday),],
  cumsum(c(0, abs(diff(state_no)) > 0))
)

# Split-apply-combine to calculate the start and end times of each 
# event: res => data.frame 
res <- data.frame(do.call(rbind, lapply(with(df, split(df, grp)), function(x){
        data.frame(
          event = unique(x$grp),
          state = unique(x$state),
          start_time = format(min(x$timeofday, na.rm = TRUE), "%H:%M"),
          end_time = format(max(x$timeofday, na.rm = TRUE), "%H:%M"),
          duration_in_approx_5_mins = difftime(
            max(x$timeofday, na.rm = TRUE),
            min(x$timeofday, na.rm = TRUE),
            units = "mins"
          )
        )  
      }
    )
  ),
  stringsAsFactors = FALSE,
  row.names = NULL
)

# Print the result: data.frame => stdout(console)
res
  •  Tags:  
  • r
  • Related