【问题标题】:Interval class in data.tabledata.table 中的区间类
【发布时间】:2018-02-01 20:44:34
【问题描述】:

我的问题将在以下可重现的示例中得到解释。

首先,让我们加载所需的包并创建一个POSIXct 和一个data.table 对象。

library(data.table)
library(lubridate)

target_date <- ymd(20180601, tz='America/Montreal')

test <- data.table(
  V1 = seq(1:3),
  V2 = c(ymd(20170421, tz='America/Montreal'),
         ymd(20170702, tz='America/Montreal'),
         ymd(20180113, tz='America/Montreal'))
)

如下所示,一切正常。

test[]

##     V1         V2
##  1:  1 2017-04-21
##  2:  2 2017-07-02
##  3:  3 2018-01-13

然后,我创建一个包含 Interval 对象的新列。

test[, V3:=interval(V2, target_date)]

还在那里,一切都很好。

test[]

##     V1         V2                             V3
##  1:  1 2017-04-21 2017-04-21 EDT--2018-06-01 EDT
##  2:  2 2017-07-02 2017-07-02 EDT--2018-06-01 EDT
##  3:  3 2018-01-13 2018-01-13 EST--2018-06-01 EDT

然后,我想创建两个仅在前两行定义的新列:第一个是POSIXct 列的函数,第二个是Interval 列的函数。

test[c(1, 3), V4 := V2 + days(20)]
test[]

##     V1         V2                             V3         V4
##  1:  1 2017-04-21 2017-04-21 EDT--2018-06-01 EDT 2017-05-11
##  2:  2 2017-07-02 2017-07-02 EDT--2018-06-01 EDT       <NA>
##  3:  3 2018-01-13 2018-01-13 EST--2018-06-01 EDT 2018-02-02

此时仍然正确。

然后,当我使用Interval 列的列函数尝试相同的操作时,我得到以下warning

test[c(1, 3), V5 := ymd(20180101, tz='America/Montreal') %within% V3]

##  Warning messages:
##  1: In as.numeric(a) - as.numeric(b@start) <= b@.Data :
##    longer object length is not a multiple of shorter object length
##  2: In `[.data.table`(test, c(1, 3), `:=`(V5, ymd(20180101, tz = "America/Montreal") %within%  :
##  Supplied 3 items to be assigned to 2 items of column 'V5' (1 unused)

这个warning 告诉我,事实上,该命令产生了 3 个值,我尝试将这些值输入 2 行 data.table(过滤后的 data.table 有两行)。

我试图找出问题所在,我想我找到了提示。考虑以下两个命令:

test[, V3][2]
##  [1] 2017-07-02 EDT--2018-06-01 EDT

test[2, V3]
##  [1] 2017-04-21 EDT--2018-03-21 EDT 2017-07-02 EDT--2018-06-01 EDT 2018-01-13 EST--2018-12-13 EST

事实上,我认为这两个命令会产生相同的结果,但事实并非如此。更令人惊讶的是,它们的 length 均为 1(查看上面的输出时,我认为第二个命令的 length 为 3)。

length(test[, V3][2])
##  [1] 1

length(test[2, V3])
##  [1] 1

问题是,这两个命令在.Data 槽中产生了一个向量为1 的对象(我猜Interval 的长度以秒为单位),

test[, V3][2]@.Data
##  [1] 28857600

test[2, V3]@.Data
##  [1] 28857600

但第一个在start 槽中的向量为1,而第二个在start 槽中的向量为3。

test[, V3][2]@start
##  [1] "2017-07-02 EDT"

test[2, V3]@start
##  [1] "2017-04-21 EDT" "2017-07-02 EDT" "2018-01-13 EST"

我知道我可以通过将每个 Interval 对象封装在一个列表中并在每次需要时提取它们来解决这个问题,但是还有其他方法可以解决这个问题吗?

【问题讨论】:

    标签: r data.table lubridate


    【解决方案1】:

    可能有点脏,但你能走吗:

    test[c(1, 3), V5 := ymd(20180101, tz='America/Montreal') %within% V3[.I]]
    
    test[, dat := (V3[.I]@.Data)]
    

    【讨论】:

    • 如果我这样做,我将为每个条目提供良好的@start,但如果我很好地理解包的行为,两个条目的第一个 @.Data...!
    • 似乎接近为我工作(见编辑)。如果您有大量数据,我可能会放弃 lubridate。
    • 我也在考虑放弃 lubridate,我的数据库有 500M 行,我使用 lubridate 获得的更精确一点不值得我失去的计算时间......但是'我仍然很好奇解决这个问题。
    • 我在一个有 350k 行的表上使用 lubridate 间隔获得了糟糕的性能,如果可以的话,我肯定会避免它,只是将日期视为整数。我认为我发布的解决方案可能会为每一行获得正确的@.Data,但可能不会。
    • 感谢@Matt 的提示,我将使用data.table 处理日期的方式。
    猜你喜欢
    • 1970-01-01
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    • 2020-01-28
    • 1970-01-01
    • 2013-09-21
    • 2013-08-02
    • 2011-12-10
    相关资源
    最近更新 更多