将日期差异作为时间单位感知数字向量进行分区

时间:2018-05-25 13:35:43

标签: r date posix

我需要计算“多少x单位” POSIX日期向量中的每个元素都来自给定的参考日期,其中

  • x是“典型”时间单位,例如季度等。
  • 日期向量可以跨越多年
  • 结果必须是numeric向量

我有一些东西,但它不是一种可以推广的一致方法(的两种不同方法)。

可能一文不值:我一般都在寻找符合ISO 8601

的解决方案

修改

“一致”在某种意义上说,我理想地说,一个解决方案总是在as.numeric(dates)之后利用一些聪明的“时间单位分箱”。但是对于我不会看到如何实现这一点,因为每个月包含不同的天数(工作数周,因为我们总能安全地说“一周包含7天”)。

换句话说:对于我想使用(as.numeric(.x) / (<something>))之类的内容,就像我使用(as.numeric(.x) / (60 * 60 * 24 * 7)) 一样。这就是 <something> ,我正在寻找一种通用的方法来区分日期差异。

解决方案草案

功能定义:

library(magrittr)
library(purrr)

normalize_time_distance_month <- function(dates) {
  dates %>%
    as.POSIXct() %>%
    purrr::map_dbl(function(.x)
      as.numeric(format(.x, "%y")) * 12 + as.numeric(format(.x, "%m")))
}

normalize_time_distance_week <- function(dates) {
  dates %>%
    as.POSIXct() %>%
    purrr::map_dbl(function(.x)
      (as.numeric(.x) / (60 * 60 * 24 * 7)) %>%
        round())
}

个月:

# Months ------------------------------------------------------------------

dates <- seq(as.POSIXct("2018-03-01"), length.out = 24, by = "month")
origin <- as.POSIXct("2018-05-01")

dates_norm <- normalize_time_distance_month(dates)
origin_norm <- normalize_time_distance_month(origin)

(time_diffs <- dates_norm - origin_norm)
#>  [1] -2 -1  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
#> [24] 21

周:

# Weeks -------------------------------------------------------------------

dates <- seq(as.POSIXct("2018-05-07"), length.out = 104, by = "week")
origin <- as.POSIXct("2018-05-21")

dates_norm <- normalize_time_distance_week(dates)
origin_norm <- normalize_time_distance_week(origin)

(time_diffs <- dates_norm - origin_norm)
#>   [1]  -2  -1   0   1   2   3   4   5   6   7   8   9  10  11  12  13  14
#>  [18]  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31
#>  [35]  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48
#>  [52]  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65
#>  [69]  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82
#>  [86]  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99
#> [103] 100 101

reprex package(v0.2.0)创建于2018-05-25。

2 个答案:

答案 0 :(得分:1)

一种选择是将表达式作为参数传递,然后解析它

library(tidyverse)
library(rlang)
normalize_time_distance <- function(dates, expr) {
 dates %>%
    as_tibble %>% 
    mutate(value = as.POSIXct(value)) %>%
    mutate(value = !! parse_expr(expr)) %>%
    pull(value)

 }

expr1 <- 'as.numeric(format(value, "%y")) * 12 + as.numeric(format(value, "%m"))'
normalize_time_distance(dates, expr1)
#[1] 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
#[20] 238 239 240 241 242



expr2 <-  'round((as.numeric(value) / (60 * 60 * 24 * 7)))'
normalize_time_distance(dates, expr2)
#[1] 2513 2517 2522 2526 2530 2535 2539 2544 2548 2552 2557 2561 2565 2570 2574
#[16] 2578 2583 2587 2591 2596 2600 2604 2609 2613

答案 1 :(得分:1)

如果您对一天的倍数间隔感兴趣,那么使用POSIXt类是没有意义的。它只会产生时区错误的可能性,你可以通过使用Date类完全阻止它,所以从这里我们将假设Date类。 as.Date可用于将POSIXct对象转换为Date对象。

您的问题中有两个不同的案例。间隔为一天(天,周)的倍数,间隔是一个月的倍数(月,季,年)。这些必须单独处理,因为一个月内没有固定的天数。

案例1 - 间隔是天数的倍数

如果间隔长度是d天,那么如果x和y是Date类对象的话 间隔数是

# x and y are Date class
(as.numeric(y) - as.numeric(x)) / d

其中d为1天,7周为。

案例2 - 间隔是几个月

如果间隔长度是m个月,那么如果x和y是Date类对象:

library(zoo)

date2ym <- function(x) {
   ym <- as.yearmon(x)
   b <- as.numeric(as.Date(ym))
   e <- as.numeric(as.Date(ym, frac = 1))
   12 * as.numeric(ym) + (as.numeric(x) - b) / (e - b + 1)
}

# x and y are Date class
(date2ym(y) - date2ym(x)) / m 

其中m为1个月,3个为季度,12个为年。

修改

修复(2)。