How to calculate duration from a "start - end" string more succinctly

Viewed 120

I have times stamps indicating the time an event started and the time it ended:

x <- "00:01:00.000 - 00:01:10.500"

I need to calculate the event's duration. Using hmsfrom the package lubridateas well as lapply and strsplitdoes give me the expected output:

library(lubridate)
unlist(lapply(strsplit(x, split=" - "), function(x) as.numeric(hms(x))))[2] - unlist(lapply(strsplit(x, split=" - "), function(x) as.numeric(hms(x))))[1] 
[1] 10.5

But I feel the code is utterly inelegant and anything but succinct. Is there any better way to get the duration?

EDIT:

What if, as is indeed the case, there are many more than just one value in x, such as:

x <- c("00:01:00.000 - 00:01:10.500", "00:12:12.000 - 00:13:10.500")

I've come up with this solution:

timepoints <- lapply(strsplit(x, split=" - "), function(x) as.numeric(hms(x)))
duration <- lapply(timepoints, function(x) x[2]-x[1])

duration
[[1]]
[1] 10.5

[[2]]
[1] 58.5

But, again, there's surely a nicer and shorter one.

2 Answers

Here is a way :

as.numeric(diff(lubridate::hms(strsplit(x, split=" - ")[[1]])))
#[1] 10.5

Keeping it in base R :

as.numeric(diff(as.POSIXct(strsplit(x, split=" - ")[[1]], format = '%H:%M:%OS')))
#[1] 10.5

For multiple values, we can use sapply :

library(lubridate)
sapply(strsplit(x, " - "), function(y) diff(period_to_seconds(hms(y))))

#[1] 10.5 80.5

and in base R :

sapply(strsplit(x, " - "), function(y) {
   x1 <- as.POSIXct(y, format = '%H:%M:%OS')
   difftime(x1[2], x1[1], units = "secs")
})

Assuming that x can be a character vector, read it into a data frame using read.table and then convert the relevant columns to hms, take their difference and convert to numeric giving the vector shown. You might need the as.is=TRUE argument to read,table if you are using a version of R prior to 4.0.

library(lubridate)

# test input
x <- c("00:01:00.000 - 00:01:10.500", "00:01:00.000 - 00:01:10.500")

with(read.table(text = x), as.numeric(hms(V3) - hms(V1)))
## [1] 10.5 10.5

or using magrittr and the same input x as above:

library(lubridate)
library(magrittr)

x %>%
  read.table(text = .) %$% 
  as.numeric(hms(V3) - hms(V1))
## [1] 10.5 10.5
Related