【问题标题】:efficiently subsetting data.table multiple times多次有效地对 data.table 进行子集化
【发布时间】:2016-10-29 22:33:29
【问题描述】:

我有这种格式的数据

> data = data.table(id = 1:10, date = seq(as.Date("2016-01-01"), by = 1, length = 10))
> data
    id       date
 1:  1 2016-01-01
 2:  2 2016-01-02
 3:  3 2016-01-03
 4:  4 2016-01-04
 5:  5 2016-01-05
 6:  6 2016-01-06
 7:  7 2016-01-07
 8:  8 2016-01-08
 9:  9 2016-01-09
10: 10 2016-01-10

我有另一个矩阵,它是我希望执行的查询/子集。

> query = data.table(id = c(1,4,7), date_start = c("2016-01-01", "2016-01-01", "2016-01-01"), date_end = c("2016-01-04", "2016-01-02", "2016-01-03"))
> query
   id date_start   date_end
1:  1 2016-01-01 2016-01-04
2:  4 2016-01-01 2016-01-02
3:  7 2016-01-01 2016-01-03

我希望做这样的事情:

subset(data, (id == query[1] & date > date_start[1] & date < date_end[1]) | 
       (id == query[2] & date > date_start[2] & date < date_end[2]) |
       (id == query[3] & date > date_start[3] & date < date_end[3]))

是否有自动生成子集查询而不使用 for 循环和 rbinding 结果。

谢谢

【问题讨论】:

  • 您可以在正确格式化数据后使用foverlaps(data, query, nomatch=0)(使用IDate 列,请参阅?foverlaps 和?IDate)。此问答可能会有所帮助:stackoverflow.com/q/27487949
  • 我认为 foverlaps 是我正在寻找的,如果您作为答案回复,我将能够接受。

标签: r data.table subset date-range


【解决方案1】:

如果我们稍微转换一下 OP 的数据来得到

library(data.table)
data = setDT(structure(list(id = 1:10, date = structure(16801:16810, class = c("IDate", 
"Date")), date2 = structure(16801:16810, class = c("IDate", "Date"
))), .Names = c("id", "date", "date2"), row.names = c(NA, -10L
), class = c("data.table", "data.frame"), sorted = c("id", 
"date", "date2")))

query = setDT(structure(list(id = c(1, 4, 7), date_start = 
structure(c(16801L, 
16801L, 16801L), class = c("IDate", "Date")), date_end = structure(c(16804L, 
16802L, 16803L), class = c("IDate", "Date"))), .Names = c("id", 
"date_start", "date_end"), row.names = c(NA, -3L), class = c("data.table", 
"data.frame"), sorted = c("id", 
"date_start", "date_end")))

...那么我们可以使用foverlapslike

foverlaps(data, query, nomatch=0)
#    id date_start   date_end       date      date2
# 1:  1 2016-01-01 2016-01-04 2016-01-01 2016-01-01

对于这种方法,我认为在合并之前需要采取以下步骤:

  • 将所有日期设为IDates
  • 在主数据中创建一个额外的日期列
  • 在每个表上设置键

【讨论】:

  • 欢迎任何更正。我实际上很少使用foverlaps。
【解决方案2】:

在current development version中,可以直接进行non-equi的join,如下:

# data.table v1.9.7+
data[query, .(id, x.date), on=.(id, date>=date_start, date<=date_end)]

如有必要,添加nomatch=0L 以删除结果中的不匹配行。

.(id, x.date) 目前是必需的,直到我确定非 equi 连接的默认输出应该是什么样子。

【讨论】:

  • 在相关说明中,有没有办法进行查询-查询连接并删除对角线(与自身完全匹配?)
【解决方案3】:
require(data.table)
data = data.table(id = 1:10, date = seq(as.Date("2016-01-01"), by = 1, length = 10))
query = data.table(id = c(1,4,7), date_start = c("2016-01-01", "2016-01-01", 
"2016-01-01"), date_end = c("2016-01-04", "2016-01-02", "2016-01-03"))

首先你可以加入他们:

data.full <- merge(data,query,by="id", all.x=T)

接下来,如果您想排除 query 中未引​​用的观察结果,并保留那些在日期范围内引用的观察结果,那么您可以这样做:

data.final <- data.full[date >= date_start & date <= date_end,]

data.final
   id       date date_start   date_end
1:  1 2016-01-01 2016-01-01 2016-01-04

或者,如果您想保留未在 query 中引用的记录,并保留在日期范围内引用的记录:

data.final <- data.full[is.na(date_start) | (date >= date_start & date <= date_end),]
data.final
   id       date date_start   date_end
1:  1 2016-01-01 2016-01-01 2016-01-04
2:  2 2016-01-02         NA         NA
3:  3 2016-01-03         NA         NA
4:  5 2016-01-05         NA         NA
5:  6 2016-01-06         NA         NA
6:  8 2016-01-08         NA         NA
7:  9 2016-01-09         NA         NA
8: 10 2016-01-10         NA         NA

【讨论】:

  • 在[.data.table 内你不需要data.full$
  • @docendodiscimus 哦,那是真的。当我写这篇文章时,我什至没有真正想到它是一个 data.table。幸运的是它仍然以这种方式工作,但您的评论是正确的。我继续更新它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
  • 1970-01-01
  • 2020-07-24
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 2019-09-07
相关资源
最近更新 更多