Вопрос

I am trying to make a function that converts time (in character form) to decimal format such that 1 corresponds to 1 am and 23 corresponds to 11 pm and 24 means the end of the day.

Here are the two function that does this. Here one function vectorizes while other do

time2dec <- function(time0)
{
time.dec <-as.numeric(substr(time0,1,2))+as.numeric(substr(time0,4,5))/60+(as.numeric(substr(time0,7,8)))/3600
return(time.dec)
}

time2dec1 <- function(time0)
{
time.dec <-as.numeric(strsplit(time0,':')[[1]][1])+as.numeric(strsplit(time0,':')[[1]][2])/60+as.numeric(strsplit(time0,':')[[1]][3])/3600
return(time.dec)
}

This is what I get...

times <- c('12:23:12','10:23:45','9:08:10')

#>time2dec(times)
[1] 12.38667 10.39583       NA
Warning messages:
1: In time2dec(times) : NAs introduced by coercion
2: In time2dec(times) : NAs introduced by coercion

#>time2dec1(times)
[1] 12.38667

I know time2dec which is vectorized, gives NA for the last element because it extracts 9: instead of 9 as hour. That is why I created time2dec1 but I do not know why it is not getting vectorized.

I will also be interested in getting a better function for doing what I am trying to do. I saw this which explain a part of my question but does not provide a clue to do what I am trying.

Это было полезно?

Решение

Don't try to reinvent the wheel:

times1 <- difftime(as.POSIXct(times, "%H:%M:%S", tz="GMT"),
                   as.POSIXct("0:0:0", "%H:%M:%S", tz="GMT"), 
                   units="hours")
#Time differences in hours
#[1] 12.386667 10.395833  9.136111

as.numeric(times1)
#[1] 12.386667 10.395833  9.136111

Другие советы

In the following we shall use this test vector:

ch <- c('12:23:12','10:23:45','9:08:10')

1) To fix up the solution in the question we prepend a 0 and then replace any string of 3 digits with the last two:

num.substr <- function(...) as.numeric(substr(...))
time2dec <- function(time0) {
    t0 <- sub("\\d(\\d\\d)", "\\1", paste0(0, time0))
    num.substr(t0, 1, 2) + num.substr(t0, 4, 5) / 60 + num.substr(t0, 7, 8) / 3600
}

time2dec(ch)
## [1] 12.386667 10.395833  9.136111

2) Parsing the string is slightly easier with strapply in the gsubfn package:

strapply(ch, "^(.?.):(..):(..)", 
   ~ as.numeric(h) + as.numeric(m)/60 + as.numeric(s)/36000,
   simplify = c)
## [1] 12.383667 10.384583  9.133611

3) We can reduce the string manipulation to just removing the colons and then convert the resulting character string to numeric so we can manipulate it numerically:

num <- as.numeric(gsub(":", "", ch))
num %/% 10000 + num %% 10000 %/% 100 / 60 + num %% 100 / 3600
## [1] 12.386667 10.395833  9.136111

4) The chron package has a "times" class that internally represents times as fractions of a day. Converting that to hours gives an easy solution:

library(chron)

24 * as.numeric(times(ch))
## [1] 12.386667 10.395833  9.136111

ADDED Added more solutions.

 as.numeric( strptime(times, "%H:%M:%S")-strptime(Sys.Date(), "%Y-%m-%d" ))
[1] 12.386667 10.395833  9.136111

Basically the same as Roland's but bypassing some steps, and I try to avoid using difftime if I can. Had too many bugs arise because I don't really understand the function or the class ... or something. And when I timed it versus Roland's his was faster. Oh, well.

Emulating @G.Grothendieck's efforts (and essentially working similarly to his elegant strapply solution:

num <- apply( matrix(scan(text=gsub(":", " ", ch), what=numeric(0)),nrow=3), 2, 
                 function(x) x[1]+x[2]/60 +x[3]/3600 )
#Read 9 items
num
#[1] 12.386667 10.395833  9.136111

And this actually answers the original question:

 num <- sapply( strsplit(ch, ":"), function(x){ x2 <- as.numeric(x);
                                                x2[1]+x2[2]/60 +x2[3]/3600})
 num
#[1] 12.386667 10.395833  9.136111

The following does what you want

sapply(strsplit(times, ":"), function(d) {
  sum(as.numeric(d)*c(1,1/60,1/3600))
})

Step by step:

strsplit(times, ":")

returns a list with character vectors. Each character vector contains the three part of the time (hour, minutes, seconds). We now want to convert each of the elements in the list to a numeric values. For this we need to apply a function to each element and put the results of the back into a vector which is what sapply does.

sapply(strsplit(times, ":", function(d) {
})

As for the function. We first need to convert the character values to numeris values using as.numeric. The we multiply the first element with 1, the second with 1/60 and the third with 1/3600 and add the results (for which we use sum). Resulting in

sapply(strsplit(times, ":"), function(d) {
  sum(as.numeric(d)*c(1,1/60,1/3600))
})
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top