2

我有一个关于动态子集数据表的问题。我知道 stackoverflow 上有许多线程,它们的命名方式相似,但不幸的是它们并没有引导我找到想要的解决方案。

示例数据集:

require(data.table)
dt <- data.table(date=c(rep(1,5),rep(2,5)),id=rep(1:5,2),var=c(1:10))

对于每个 ID,我想找到之前所有时期的所有其他ID的子集。在示例数据集中,有 5 个 ID 和两个句点。如果在周期 2 中查看 ID=5,则相应的子集将是 ID={1,2,3,4) 和 date=1 的子集。在这个简单的数据集中,我当然可以手动编写代码:

dt[,dt[-.I][date<2],by=id]

但是,我想自动执行此操作。我尝试了类似的东西

dt[,dt[-.I][date < unique(dt$date[.I])],by=id] 

但不幸的是,这不起作用。

任何有用的意见表示赞赏!谢谢!

4

3 回答 3

3

您必须意识到,这些组合会随着唯一日期/ID 数量的增加而爆炸式增长。即使对于 date=1:10 和 id=1:10,答案是 4050 行(需要 0.7 秒),对于 date=1:50 和 id=1:50,它已经是 3001250 行(需要 6.2 秒)。话虽如此,这应该按预期工作:

setkey(dt, date, id)
ans <- dt[!J(1), {d.tmp = date-1; id.tmp = id; dt[CJ(1:d.tmp, 
        setdiff(id, id.tmp))]}, by=list(date, id)]
setnames(ans, make.unique(names(ans)))
setkey(ans, date, id, date.1)

    date id date.1 id.1 var
 1:    2  1      1    2   2
 2:    2  1      1    3   3
 3:    2  1      1    4   4
 4:    2  1      1    5   5
 5:    2  2      1    1   1
 6:    2  2      1    3   3
 7:    2  2      1    4   4
 8:    2  2      1    5   5
 9:    2  3      1    1   1
10:    2  3      1    2   2
11:    2  3      1    4   4
12:    2  3      1    5   5
13:    2  4      1    1   1
14:    2  4      1    2   2
15:    2  4      1    3   3
16:    2  4      1    5   5
17:    2  5      1    1   1
18:    2  5      1    2   2
19:    2  5      1    3   3
20:    2  5      1    4   4
于 2013-10-26T20:58:18.877 回答
2

我认为这是更快的解决方案:

dta <- data.table(date=c(rep(1,5),rep(2,5)),id=rep(1:5,2),var=c(1:10))
dta[,dta[dta[.I]$id!=dta$id & dta[.I]$date>dta$date],by=list(id,date)]

非常感谢任何有关如何使此代码更快的评论。

于 2013-11-08T17:15:47.460 回答
0

data.table从1.9.8版(CRAN 2016 年 11 月 25 日)开始,可以进行non-equi-joins

非设备自连接

dta[dta, on = .(date > date), allow.cartesian = TRUE, nomatch = 0L, 
    .(id, x.date, i.date, i.id, i.var)][
      id != i.id][order(id)]
    id x.date i.date i.id i.var
 1:  1      2      1    2     2
 2:  1      2      1    3     3
 3:  1      2      1    4     4
 4:  1      2      1    5     5
 5:  2      2      1    1     1
 6:  2      2      1    3     3
 7:  2      2      1    4     4
 8:  2      2      1    5     5
 9:  3      2      1    1     1
10:  3      2      1    2     2
11:  3      2      1    4     4
12:  3      2      1    5     5
13:  4      2      1    1     1
14:  4      2      1    2     2
15:  4      2      1    3     3
16:  4      2      1    5     5
17:  5      2      1    1     1
18:  5      2      1    2     2
19:  5      2      1    3     3
20:  5      2      1    4     4

正如 Arun 指出的那样,组合随着唯一日期/ID 数量的增加而爆炸式增长。因此,allow.cartesian = TRUE不得不设置。

不幸的是,只有>=, >, <=, < 和==二元运算符可以在非等值连接中使用,但不能!=。因此,连接的结果必须在id之后被过滤为相等。

基准

OP 发布了他自己的答案,要求进一步加快代码速度。Arun 的回答包括不同问题大小的时间安排。

因此,下面的基准测试尝试重复 Arun 的测量并比较迄今为止发布的三种不同方法。

library(bench)
library(magrittr)

bm <- press(
  n_date = c(2, 10, 50),
  n_id = c(5, 10, 50),
  {
    dt0 <- CJ(date = seq_len(n_date), id = seq_len(n_id))
    dt0[, var := .I]
    mark(
      arun = {
        dt <- copy(dt0)
        setkey(dt, date, id)
        dt[!J(1), {
          d.tmp = date-1
          id.tmp = id 
          dt[CJ(1:d.tmp, setdiff(id, id.tmp))]
        }, by=list(id, date)] -> arun
      },
      chameau13 = {
        dta <- copy(dt0)
        dta[,dta[dta[.I]$id!=dta$id & dta[.I]$date>dta$date],by=list(id,date)]
      },
      uwe = {
        dta <- copy(dt0)
        dta[dta, on = .(date > date), allow = TRUE, nomatch = 0L, 
            .(id = x.id, date = x.date, date.1 = i.date, id.1 = i.id, var = i.var)][
              id != id.1]
      },
      check = my_check
    )
  }
)

由于 Arun 的解决方案通过引用修改数据集,因此所有运行都从一个新副本开始。这三种解决方案在列名和行顺序上有所不同。因此,使用自定义检查函数来确保结果相同:

my_check <- function(x, y) {
  setnames(x, make.unique(names(x)))
  setorder(x, id, date, date.1, id.1)
  setnames(y, make.unique(names(y)))
  setorder(y, id, date, date.1, id.1)
  all.equal(x, y, check.attributes = FALSE) %T>% 
    {if (!isTRUE(.)) print(.)}
}

基准时间可以通过以下方式可视化

ggplot2::autoplot(bm)

在此处输入图像描述

迄今为止,非等连接是所有问题规模最快的方法,而 OP 自己的解决方案几乎总是最慢,尽管 OP 有预期。

于 2018-08-13T05:56:05.980 回答