I can think of using lubridate::hms
to convert those strings to numbers, but I haven't found the right way to format(.., format="%H:%M:%S")
back again, so here are two functions I have used for various related purposes:
## simply convert "01:23:45" to 5025 (seconds) and "00:17:14.842" to 1034.842
time2num <- function(x) {
vapply(strsplit(x, ':'), function(y) sum(as.numeric(y) * c(60*60, 60, 1)),
numeric(1), USE.NAMES=FALSE)
}
## and back again
num2time <- function(x, digits.secs = getOption("digits.secs", 3)) {
hr <- as.integer(x %/% 3600)
min <- as.integer((x - 3600*hr) %/% 60)
sec <- (x - 3600*hr - 60*min)
if (anyNA(digits.secs)) {
# a mostly-arbitrary determination of significant digits,
# motivated by @Roland https://stackoverflow.com/a/27767973
for (digits.secs in 1:6) {
if (any(abs(signif(sec, digits.secs) - sec) > (10^(-3 - digits.secs)))) next
digits.secs <- digits.secs - 1L
break
}
}
sec <- sprintf(paste0("%02.", digits.secs[[1]], "f"), sec)
sec <- paste0(ifelse(grepl("^[0-9]\\.", sec), "0", ""), sec)
out <- sprintf("%02i:%02i:%s", hr, min, sec)
out[is.na(x)] <- NA_character_
out
}
With these,
library(dplyr)
df %>%
group_by(ID) %>%
mutate(Freq = num2time(sum(time2num(Time)), digits = 0)) %>%
ungroup()
# # A tibble: 6 x 3
# ID Time Freq
# <int> <chr> <chr>
# 1 456 0:00:01 00:02:06
# 2 456 0:02:05 00:02:06
# 3 123 0:00:14 00:00:14
# 4 756 0:03:47 00:05:44
# 5 756 0:01:56 00:05:44
# 6 756 0:00:01 00:05:44
Data
dat <- structure(list(ID = c(456L, 456L, 123L, 756L, 756L, 756L), Time = c("0:00:01", "0:02:05", "0:00:14", "0:03:47", "0:01:56", "0:00:01")), class = "data.frame", row.names = c(NA, -6L))