【问题标题】:Fastest way to extract hour from time (HH:MM)从时间中提取小时的最快方法 (HH:MM)
【发布时间】:2014-05-13 05:59:49
【问题描述】:

希望 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.tableas.ITime 中的 hour 方法解决了这个问题,但在大倍数数组上看起来很慢。

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

想知道是否有更快的方法(就像fastPOSIXct,但无需日期即可工作)。

fastPOSIXct 确实像 snap 一样工作,但只是错了。

【问题讨论】:

    标签: r time data.table hour


    【解决方案1】:

    这是一个选项吗?这是base 解决方案。

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

    【讨论】:

    • 感谢您提供可扩展的解决方案(即使时间格式发生变化也能正常工作)。
    【解决方案2】:

    您也可以使用 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! as.numeric(sub(pattern = ":.*", replacement = "", x = vals) 在这里可能更快。
    • @Henrik 不错!我已经更新了那个——它确实加快了速度。
    【解决方案3】:

    要真正加快速度,您还可以从字符串中剪掉 lsat 3 个字符。它比使用regex 更快。

    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】:

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


      在包含 10e6 个元素的向量的基准测试中,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
      

      【讨论】:

      • 感谢 Henrik 将所有方法合二为一。对理解选项很有帮助。
      • substr 确实不错,我能够重现相同的基准测试结果。被接受为答案(对所有其他人都 +1,这同样有价值)。
      【解决方案5】:

      您可以使用 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 参数为负数,则从字符串末尾开始计数。所以在这个例子中,子字符串是从第一个字符到第四个字符,但从字符串的末尾开始计数。

      【讨论】:

      • 它真的很快,因为它是用纯 C++ 编写的,所以它必须很快 :) 作为这个包中的每个函数。随意检查! :)
      猜你喜欢
      • 1970-01-01
      • 2018-07-14
      • 1970-01-01
      • 2023-01-26
      • 2019-05-29
      • 2017-03-06
      • 1970-01-01
      • 2018-11-25
      • 2013-09-15
      相关资源
      最近更新 更多