【问题标题】:R tsibble add support for custom indexR tsibble 添加对自定义索引的支持
【发布时间】:2021-01-22 13:32:13
【问题描述】:

问题描述
我经常使用三倍月度数据。每月三次(或大约每 10 天一次,也称为十天),这是前苏联水相关数据和世界各地更多气候/水相关数据集的典型报告间隔。下面是一个包含 2 个变量的示例数据集:

> date = unique(floor_date(seq.Date(as.Date("2019-01-01"), as.Date("2019-12-31"), 
                                    by="day"), "10days"))
> example_data <- tibble(
    date = date[day(date)!=31],  
    value = seq(1,36,1),  
    var = "A") %>%
    add_row(tibble(
      date = date[day(date)!=31],  
      value = seq(10,360,10),  
      var = "B")) 
> example_data
# A tibble: 72 x 3
# Groups:   var [2]
   date       value var  
   <ord>      <dbl> <chr>
 1 2019-01-01     1 A    
 2 2019-01-01    10 B    
 3 2019-01-11     2 A    
 4 2019-01-11    20 B    
 5 2019-01-21     3 A    
 6 2019-01-21    30 B    
 7 2019-02-01     4 A    
 8 2019-02-01    40 B    
 9 2019-02-11     5 A    
10 2019-02-11    50 B    
# … with 62 more rows

在示例中,我选择了 1.、11. 和 21. 迄今为止的几十年,但实际上以每月 1 到 3 为单位对它们进行索引更合适(类似于每年的第 1 到 12 个月)或在 dekad 1 到 36 每年(类似于一年中的一天)。最优雅的解决方案是在lubridate 中为像yearmonth 这样的dekadal 数据设置适当的日期格式。但是,lubridate 可能不打算在不久的将来支持 dekadal 数据 (github conversation)。

我有使用 tsibbletimetk 的工作流,它们可以很好地处理月度数据,但使用原始的十进制时间步骤确实更合适,我正在寻找一种能够使用 tidyverse 的方法使用 dekadal 数据的函数,使用尽可能少的繁琐变通方法。
在 tsibble 中对 dekadal 数据使用每日日期的问题在于,它将时间间隔标识为每日,并且您每月的 3 个值之间会出现很多数据差距:

> example_data_tsbl <- as_tsibble(example_data, index = date, key = var)
> count_gaps(example_data_tsbl, .full = FALSE)
# A tibble: 70 x 4
   var   .from      .to           .n
   <chr> <date>     <date>     <int>
 1 A     2019-01-02 2019-01-10     9
 2 A     2019-01-12 2019-01-20     9
 3 A     2019-01-22 2019-01-31    10
# … 

这是我到目前为止所做的:

  1. 我看到here 可以在tsibble 中将有序因子定义为索引,但timetk 不将因子识别为索引。 timetk 建议定义自定义索引(参见 2.)。
  2. add custom indices to tsibble 的可能性,但我没有找到这方面的例子,我不明白我必须如何使用这些功能(a vignette is still planned)。我已经开始阅读代码以尝试了解如何使用这些函数来获得对 dekadal 数据的支持,但我有点不知所措。

问题

  • tsibble 中的 dekadal 自定义索引的行为是否与 yearmonth 或 weekyear 类似?

  • 这里有人可以分享如何将自定义索引添加到 tsibble 的示例吗?

  • 或者有人知道在 tidyverse 中优雅地处理 dekadal 数据的另一种方法吗?

【问题讨论】:

    标签: r date customization tsibble


    【解决方案1】:

    这里没有讨论 tsibbles,但评论太长了,确实提供了替代方案。

    zoo 可以通过 (1) 下面的代码不需要创建新类或 (2) 通过创建新类和方法来做到这一点。对于遵循yearmon 类具有的方法的替代方案就足够了。见here。动物园本身不需要修改。

    正如我们在下面看到的,对于第一种方法,日期将显示为 year(cycle),其中 cycle 为 1、2、...、36。在内部,日期存储为 year + (cycle-1)/36 。

    如果日期是连续的第三个月,也可以使用 ts 类(或者如果您不介意插入 NA 以使其如此)。为此使用as.ts(z)

    开始一个不加载任何包的新会话,然后复制并粘贴最后注释中显示的输入 DF,然后是此代码。 Date2dek 会将表示标准 yyyy-mm-dd 格式的日期的日期向量或字符向量转换为上述的 dek 格式。 dek2Date 执行逆变换。它实际上并未在下面使用,但可能有用。

    library(zoo)
    
    # convert Date or yyyy-mm-dd char vector
    Date2dek <- function(x, ...) with(as.POSIXlt(x, tz="GMT"), 
      1900 + year + (mon + ((mday >= 11) + (mday >= 21)) / 3) / 12)
    
    dek2Date <- function(x, ...) {     # not used below but shows inverse
      cyc <- round(36 * (as.numeric(x) %% 1)) + 1
      if(all(is.na(x))) return(as.Date(x))
      month <- (cyc - 1) %/% 3 + 1
      day <- 10 * ((cyc - 1) %% 3) + 1
      year <- floor(x + .001)
      ix <- !is.na(year)
      as.Date(paste(year[ix], month[ix], day[ix], sep = "-")) 
    }
    
    # DF given in Note below
    z <- read.zoo(DF, split = "var", FUN = Date2dek, regular = TRUE, freq = 36)
    z
    

    结果是以下 zooreg 对象:

            A  B
    2019(1) 1 10
    2019(2) 2 20
    2019(3) 3 30
    2019(4) 4 40
    2019(5) 5 50
    

    注意

    DF <- data.frame(
      date = as.Date(ISOdate(2019, rep(1:2, 3:2), c(1, 11, 21))), 
      value = c(1:5, 10*(1:5)), 
      var = rep(c("A", "B"), each = 5))
    

    【讨论】:

    • 非常感谢您的回答!实际上,我之前一直在使用 ts 和 zoo 处理我的十年数据,并且运行良好(应该在问题中提到它)。
    【解决方案2】:

    扩展 tsibble 以支持新索引需要为这些泛型定义方法:

    • index_valid() - 如果类可以作为索引,则此方法应返回 TRUE
    • interval_pull() - 此方法接受您的索引值并计算数据的间隔。可以使用tsibble:::new_interval() 创建间隔。您可能会发现 tsibble::gcd_interval() 可用于计算最小间隔。
    • seq()+ - 这些方法用于使用 new_data() 函数生成未来时间值。

    'year' 的新 tsibble 索引类的最小示例如下:

    library(tsibble)
    #> 
    #> Attaching package: 'tsibble'
    #> The following objects are masked from 'package:base':
    #> 
    #>     intersect, setdiff, union
    library(vctrs)
    
    # Object creation function
    my_year <- function(x = integer()) {
      x <- vec_cast(x, integer())
      vctrs::new_vctr(x, class = "year")
    }
    
    # Declare this class as a valid index
    index_valid.year <- function(x) TRUE
    
    # Compute the interval of a year input
    interval_pull.year <- function(x) {
      tsibble::new_interval(
        year = tsibble::gcd_interval(vec_data(x))
      )
    }
    
    # Specify how sequences are generated from years
    seq.year <- function(from, to, by, length.out = NULL, along.with = NULL, ...) {
      from <- vec_data(from)
      if (!rlang::is_missing(to)) {
        vec_assert(to, my_year())
        to <- vec_data(to)
      }
      my_year(NextMethod())
    }
    
    # Define `+` operation as needed for `new_data()`
    vec_arith.year <- function(op, x, y, ...) {
      my_year(vec_arith(op, vec_data(x), vec_data(y), ...))
    }
    
    # Use the new index class
    x <- tsibble::tsibble(
      year = my_year(c(2018, 2020, 2024)),
      y = rnorm(3), 
      index = "year"
    )
    x
    #> # A tsibble: 3 x 2 [2Y]
    #>     year      y
    #>   <year>  <dbl>
    #> 1   2018  0.211
    #> 2   2020 -0.410
    #> 3   2024  0.333
    interval(x)
    #> <interval[1]>
    #> [1] 2Y
    new_data(x, 3)
    #> # A tsibble: 3 x 1 [2Y]
    #>     year
    #>   <year>
    #> 1   2026
    #> 2   2028
    #> 3   2030
    

    reprex package (v0.3.0) 于 2021-02-08 创建

    【讨论】:

    • 非常感谢您花时间添加您的答案。我会尝试一下并及时发布更新。
    猜你喜欢
    • 2023-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-12-24
    • 2013-03-12
    • 2023-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多