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
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.table
approach
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