【问题标题】:Rounding time to nearest quarter hour将时间四舍五入到最接近的一刻钟
【发布时间】:2012-06-02 11:47:23
【问题描述】:

我有一个 POSIXct 值向量,我想将它们四舍五入到最接近的一刻钟。我不在乎这一天。如何将值转换为小时和分钟?

例如,我想要值

"2012-05-30 20:41:21 UTC"

成为

"20:45"

【问题讨论】:

    标签: r datetime


    【解决方案1】:

    的确,这是一个老问题,到目前为止有一些有用的答案。 giraffhere 的最后一个似乎是最优雅的。但是,不是 floor_date 而是 round_date 可以解决问题:

    lubridate::round_date(x, "15 minutes") 
    

    【讨论】:

      【解决方案2】:

      老问题,但要注意lubridate 包现在可以使用floor_date 轻松处理此问题。要将 POSIXct 对象的向量切割为 15 分钟间隔,请像这样使用。

      x <- lubridate::floor_date(x, "15 minutes")

      编辑:用户 @user3297928 指出,使用 lubridate::round_date(x, "15 minutes") 四舍五入到最接近的 15 分钟。以上楼层吧。

      【讨论】:

        【解决方案3】:

        您可以使用round。诀窍是在舍入前除以 900 秒(15 分钟 * 60 秒),然后乘以 900:

        a <-as.POSIXlt("2012-05-30 20:41:21 UTC")
        b <-as.POSIXlt(round(as.double(a)/(15*60))*(15*60),origin=(as.POSIXlt('1970-01-01')))
        b
        [1] "2012-05-30 20:45:00 EDT"
        

        要获取小时和分钟,只需使用格式

        format(b,"%H:%M")
        [1] "20:45"
        
        as.character(format(b,"%H:%M"))
        [1] "20:45"
        

        【讨论】:

        • 我认为我们不需要双精度,并且 origin 接受一个字符串,所以稍微简化一下:b &lt;-as.POSIXlt(round(as.numeric(a)/(15*60))*(15*60),origin='1970-01-01')
        【解决方案4】:

        可以使用xts包中的align.time函数来处理舍入,然后format返回一个字符串“HH:MM”:

        R> library(xts)
        R> p <- as.POSIXct("2012-05-30 20:41:21", tz="UTC")
        R> a <- align.time(p, n=60*15)  # n is in seconds
        R> format(a, "%H:%M")
        [1] "20:45"
        

        【讨论】:

        • 这很优雅,但似乎只是四舍五入。
        • @Dominic:你是 100% 正确的。 align.time 只四舍五入,而您想四舍五入到最接近的一刻钟。道歉。
        • 向下取整:align.time(p - lubridate::minutes(15), n=60*15)
        【解决方案5】:

        类似

        format(strptime("1970-01-01", "%Y-%m-%d", tz="UTC") + round(as.numeric(your.time)/900)*900,"%H:%M")
        

        会工作

        【讨论】:

          【解决方案6】:

          使用data.table 中的IDateITime 类以及IPeriod 类(刚刚开发),我能够获得更具可扩展性的解决方案。
          只有 shhhhimhuntingrabbitsPLapointe最近 的方式回答问题。 xts 解决方案仅使用 ceiling 进行循环,我的 IPeriod 解决方案允许指定 ceilingfloor
          要获得最佳性能,您需要将数据保存在 IDateITime 类中。正如在基准测试中看到的,从IDate/ITime/IPeriod 生成POSIXct 很便宜。低于一些 22M 时间戳的基准:

          # install only if you don't have
          install.packages(c("microbenchmarkCore","data.table"),
                           repos = c("https://olafmersmann.github.io/drat",
                                     "https://jangorecki.github.io/drat/iperiod"))
          library(microbenchmarkCore)
          library(data.table) # iunit branch
          library(xts)
          Sys.setenv(TZ="UTC")
          
          ## some source data: download and unzip csv
          # "http://api.bitcoincharts.com/v1/csv/btceUSD.csv.gz"
          # below benchmark on btceUSD.csv.gz 11-Oct-2015 11:35 133664801
          
          system.nanotime(dt <- fread(".btceUSD.csv"))
          # Read 21931266 rows and 3 (of 3) columns from 0.878 GB file in 00:00:10
          #     user   system  elapsed 
          #       NA       NA 9.048991
          
          # take the timestamp only
          x = as.POSIXct(dt[[1L]], tz="UTC", origin="1970-01-01")
          
          # functions
          shhhhi <- function(your.time){
              strptime("1970-01-01", "%Y-%m-%d", tz="UTC") + round(as.numeric(your.time)/900)*900
          }
          
          PLapointe <- function(a){
              as.POSIXlt(round(as.double(a)/(15*60))*(15*60),origin=(as.POSIXlt('1970-01-01')))
          }
          
          # myRound - not vectorized
          
          # compare results
          all.equal(
              format(shhhhi(x),"%H:%M"),
              format(PLapointe(x),"%H:%M")
          )
          # [1] TRUE
          all.equal(
              format(align.time(x, n = 60*15),"%H:%M"),
              format(periodize(x, "mins", 15),"%H:%M")
          )
          # [1] TRUE
          
          # IPeriod native input are IDate and ITime - will be tested too
          idt <- IDateTime(x)
          idate <- idt$idate
          itime <- idt$itime
          microbenchmark(times = 10L,
                         shhhhi(x),
                         PLapointe(x),
                         xts = align.time(x, 15*60),
                         posix_ip_posix = as.POSIXct(periodize(x, "mins", 15), tz="UTC"),
                         posix_ip = periodize(x, "mins", 15),
                         ip_posix = as.POSIXct(periodize(idate, itime, "mins", 15), tz="UTC"),
                         ip = periodize(idate, itime, "mins", 15))
          # Unit: microseconds
          #            expr         min          lq         mean       median          uq         max neval
          #       shhhhi(x)  960819.810  984970.363 1127272.6812 1167512.2765 1201770.895 1243706.235    10
          #    PLapointe(x) 2322929.313 2440263.122 2617210.4264 2597772.9825 2792936.774 2981499.356    10
          #             xts  453409.222  525738.163  581139.6768  546300.9395  677077.650  767609.155    10
          #  posix_ip_posix 3314609.993 3499220.920 3641219.0876 3586822.9150 3654548.885 4457614.174    10
          #        posix_ip 3010316.462 3066736.299 3157777.2361 3133693.0655 3234307.549 3401388.800    10
          #        ip_posix     335.741     380.696     513.7420     543.3425     630.020     663.385    10
          #              ip      98.031     151.471     207.7404     231.8200     262.037     278.789    10
          

          IDateITime 不仅在此特定任务中成功扩展。与IPeriod 相同的这两种类型都是基于整数的。我认为它们在加入或按 datetime 字段分组时也会很好地扩展。
          在线手册:https://jangorecki.github.io/drat/iperiod/

          【讨论】:

          • 感谢您的帖子,但您能解释一下如何实际安装这个包吗?文档中的任何地方都不清楚。
          • @ssdecontrol 查看代码块中的第一个命令以从已发布的 repo 安装。否则最可靠的方法是获取iunit 分支,您可以将其添加到远程并签出到分支。它基于 2015 年 10 月的 data.table。
          • 啊,我错过了那行。我曾假设你会有一个单独的包,称为“periodize”或“IPeriod”或其他东西,而不是 data.table 的一个分支。我认为 IDateTime 的东西被捆绑到 data.table 而不是单独的包中有点不幸
          • @ssdecontrol 但是 IPeriod 类只是一个数字,任何包都可以使用 %/% 运算符处理它,具有硬编码的句点,甚至不需要存储任何属性,只是一个数字,没有黑魔法.
          • 抽象是有价值的 imo
          【解决方案7】:

          试试这个,它结合了两个请求,并基于查看 round.POSIXt()trunc.POSIXt() 所做的事情。

          myRound <- function (x, convert = TRUE)  {
              x <- as.POSIXlt(x)
              mins <- x$min
              mult <- mins %/% 15
              remain <- mins %% 15
              if(remain > 7L || (remain == 7L && x$sec > 29))
                  mult <- mult + 1
              if(mult > 3) {
                  x$min <- 0
                  x <- x + 3600
              } else {
                  x$min <- 15 * mult
              }
              x <- trunc.POSIXt(x, units = "mins")
              if(convert) {
                  x <- format(x, format = "%H:%M")
              }
              x
          }
          

          这给出了:

          > tmp <- as.POSIXct("2012-05-30 20:41:21 UTC")
          > myRound(tmp)
          [1] "20:45"
          > myRound(tmp, convert = FALSE)
          [1] "2012-05-30 20:45:00 BST"
          > tmp2 <- as.POSIXct("2012-05-30 20:55:21 UTC")
          > myRound(tmp2)
          [1] "21:00"
          > myRound(tmp2, convert = FALSE)
          [1] "2012-05-30 21:00:00 BST"
          

          【讨论】:

          • 这个好像矢量化不好,试试structure(c(1313331280, 1313334917, 1313334917, 1313340309, 1313340309, 1313340895, 1313340895, 1313341133, 1313341218, 1313341475), class = c("POSIXct", "POSIXt"), tzone = "UTC")
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2020-02-07
          • 2011-06-25
          • 2015-11-27
          • 2011-04-03
          • 2011-01-22
          • 2022-01-02
          • 1970-01-01
          相关资源
          最近更新 更多