从时间中提取小时的最快方法是什么?(HH:MM)

13

希望fastPOSIXct能够发挥作用,但在这种情况下没有起作用。

这是我的时间数据(不含日期)- 我需要从中获取小时部分。

times <- c("9:46","11:06", "14:17", "19:53", "0:03", "3:56")

这里是fastPOSIXct的错误输出:

fastPOSIXct(times, "GMT")
[1] "1970-01-01 00:00:00 GMT" "1970-01-01 00:00:00 GMT"
[3] "1970-01-01 00:00:00 GMT" "1970-01-01 00:00:00 GMT"
[5] "1970-01-01 00:00:00 GMT" "1970-01-01 00:00:00 GMT"

没有日期的情况下,它不能正确识别时间。

data.tablehour 方法与 as.ITime 可以解决这个问题,但在大型时间数组上速度较慢。

library(data.table)
hour(as.ITime(times))
# [1]  9 11 14 19  0  3

想知道是否有一种更快的方式(就像fastPOSIXct那样,但不需要日期)。

fastPOSIXct确实很快,但是有点不准确。

5个回答

11

你也可以尝试使用substr: as.integer(substr(vals, start = 1, stop = nchar(vals) - 3))


在一个包含1000万个元素的向量基准测试中,stringi::stri_sub是最快的,substr排名第二。

vals <- sample(c("9:46", "11:06", "14:17", "19:53", "0:03", "3:56"), 1e6, replace = TRUE)

fun_substr <- function(vals) as.integer(substr(vals, start = 1, stop = nchar(vals) - 3))

grab.hrs <- function(vals) as.integer(sub(pattern = ":.*", replacement = "", x = vals))

fun_strtrim <- function(vals) as.integer(strtrim(vals, nchar(vals) - 3))

library(chron)
fun_chron <- function(vals) hours(times(paste0(vals, ":00")))

fun_lt <- function(vals) as.POSIXlt(vals, format="%H:%M")$hour

library(stringi)
fun_stri_sub <- function(vals) as.integer(stri_sub(vals, from = 1, to = -4))

library(microbenchmark)
microbenchmark(fun_substr(vals),
               fun_stri_sub(vals),      
               grab.hrs(vals),
               fun_strtrim(vals),
               fun_lt(vals),
               fun_chron(vals),
               unit = "relative", times = 5)
# Unit: relative
#               expr       min        lq      mean    median        uq       max neval
#   fun_substr(vals)  2.186714  1.902074  2.015082  1.968542  1.945007  2.090236     5
# fun_stri_sub(vals)  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000     5
#     grab.hrs(vals)  2.656630  2.397918  2.687133  2.426223  2.446902  3.263962     5
#  fun_strtrim(vals) 31.177869 27.601380 26.009818 27.423562 17.902507 29.426989     5
#       fun_lt(vals) 47.296929 41.122287 42.266556 40.647465 30.539030 52.710992     5
#    fun_chron(vals)  5.594931  5.159192  5.961775  7.746242  5.286944  6.189742     5

1
感谢亨里克将所有的方法整理到一个地方。这对于理解这些选项非常有帮助。 - Gopalakrishna Palem
substr确实很好,我能够复现相同的基准测试结果。接受作为答案(并为所有其他人+1,它们同样有价值)。 - Gopalakrishna Palem

10

你也可以使用 chron 包中的 times 函数来完成此操作:

library(chron)
vals <- c("9:46","11:06", "14:17", "19:53", "0:03", "3:56")
dat <- times(paste0(vals, ":00"))
hours(dat)
# [1]  9 11 14 19  0  3

如果速度很重要,你可以通过字符串操作更快地提取小时数:

grab.hrs <- function(vals) as.numeric(sub(pattern = ":.*", replacement = "",
                                      x = vals))
grab.hrs(vals)
# [1]  9 11 14 19  0  3

timesas.POSIXlt(来自 @tonytonov 的解决方案)似乎比 as.ITime 更快,并且字符串操作速度更快:

library(microbenchmark)
library(data.table)
microbenchmark(hours(times(paste0(vals, ":00"))),
               hours(as.ITime(vals)),
               as.POSIXlt(vals, format="%H:%M")$hour,
               grab.hrs(vals))
# Unit: microseconds
#                                     expr     min       lq   median       uq      max neval
#        hours(times(paste0(vals, ":00"))) 174.544 184.9485 193.5630 204.6950 5047.195   100
#                    hours(as.ITime(vals)) 665.833 678.8790 705.6445 735.0525 3030.574   100
#  as.POSIXlt(vals, format = "%H:%M")$hour 158.264 169.8880 171.9670 180.1800  301.840   100
#                           grab.hrs(vals)  10.637  15.4540  20.0995  21.1285   55.985   100

1
+1!在这里可能会更快的是as.numeric(sub(pattern = ":.*", replacement = "", x = vals) - Henrik
@Henrik 不错!我已经更新了,确实加快了速度。 - josliber

6

您好,这是一个选项吗?这是一种基础解决方案。

as.POSIXlt(times, format="%H:%M")$hour
#[1]  9 11 14 19  0  3

感谢这个可扩展的解决方案(即使时间格式改变,也能正常工作)。 - Gopalakrishna Palem

6

为了真正提高速度,您还可以仅从字符串中剪切掉最后3个字符。这比使用正则表达式更快。

as.numeric(strtrim(times, nchar(times) - 3)) 
## [1]  9 11 14 19  0  3

以下是基准测试结果。
Unit: microseconds
                                         expr     min       lq   median       uq      max neval
            hours(times(paste0(vals, ":00"))) 200.670 212.9720 218.7960 221.8420  352.370   100
                        hours(as.ITime(vals)) 453.174 478.9680 487.3805 496.7885 1607.321   100
      as.POSIXlt(vals, format = "%H:%M")$hour  41.278  46.4945  49.7310  51.3115   56.453   100
                               grab.hrs(vals)  12.352  15.4295  18.3850  20.3390   31.349   100
  as.numeric(gsub("(.*):.*", "\\\\1", times))  14.528  17.7225  20.6390  23.4530   53.683   100
 as.numeric(strtrim(times, nchar(times) - 3))   9.621  11.6605  12.7435  13.2520  147.446   100

4
您可以使用来自 stringi 软件包的 stri_sub 函数,并如下所示截取最后3个字符:
require(stringi)
times <- c("9:46", "11:06", "14:17", "19:53", "0:03", "3:56")
stri_sub(times, from = 1, to = -4)
## [1] "9"  "11" "14" "19" "0"  "3" 

如果from和/或to参数为负数,则从字符串末尾开始计数。因此,在这个例子中,子字符串是从第一个字符到第四个字符,但是从字符串结尾开始计数。

2
它非常快,因为它是用纯C++编写的,所以它必须很快 :) 就像这个包中的每个单独的函数一样。请随意检查! :) - bartektartanus

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接