【问题标题】:Find first date of two consecutive weeks with records in R (data.table)使用 R 中的记录查找连续两周的第一个日期(data.table)
【发布时间】:2014-01-23 04:21:44
【问题描述】:

我正在尝试查找第一个日期(每组)在一周内以及下一周有记录。周不是从星期一开始,而是定义为 7 天。

假设日期是第一周的第一天,我正在尝试测试第二个“周”中的日期记录数是否大于一个。

library(data.table)

dt=data.table(date=c(1,9,10,15,18,3,4,7,7,19,21,27),
              group=c(rep("a", 5), rep("b",7)))

> dt
    date group
 1:    1     a
 2:    9     a
 3:   10     a
 4:   15     a
 5:   18     a
 6:    3     b
 7:    4     b
 8:    7     b
 9:    7     b
10:   19     b
11:   21     b
12:   27     b

适用于 data.frame 的 for 循环如下所示:

df <- data.frame(dt)

for(i in 1:length(df$date)){
  df$count[i] <- sum(df$date >= df$date[i] + 7 &
  df$date < df$date[i] + 14 &
  df$group == df$group[i])
}

> df
   date group  count
1     1     a      2
2     9     a      1
3    10     a      1
4    15     a      0
5    18     a      0
6     3     b      0
7     4     b      0
8     7     b      1
9     7     b      1
10   19     b      1
11   21     b      0
12   27     b      0

计数大于 0 的每个组的第一个日期会给我第一周的开始日期,即“a”组中的 1 和“b”组中的 7。

我的真实data.table有超过一千万行,所以理想情况下我想要一个类似于上面for循环的函数,所以我可以这样做:

dt[, date/sum(date), by=group]

问题是我不明白如何创建一个适用于 data.table 的带有索引的函数。非常感谢任何帮助。

【问题讨论】:

  • 您能详细解释一下dt[, date/sum(date), by=group]吗?你期待什么样的输出?计数有什么用?

标签: r date indexing data.table


【解决方案1】:

我认为这可行:

# set the key for the rolling merges
setkey(dt, group, date)

# find start and end point of the intervals you want
start = dt[J(group, date + 7 ), .I, roll = -Inf, by = .EACHI]$I
end   = dt[J(group, date + 13), .I, roll =  Inf, by = .EACHI]$I

# if start is 0, the first condition is not satisfied, so set count to 0
dt[, count := (start != 0) * (end - start + 1)]

dt
#    date group count
# 1:    1     a     2
# 2:    9     a     1
# 3:   10     a     1
# 4:   15     a     0
# 5:   18     a     0
# 6:    3     b     0
# 7:    4     b     0
# 8:    7     b     1
# 9:    7     b     1
#10:   19     b     1
#11:   21     b     0
#12:   27     b     0

【讨论】:

  • 回收去年的代码,不幸的是这个解决方案不再有效。它会引发错误:Error in dt[J(group, date + 13), .I, roll = Inf]$.I : $ operator is invalid for atomic vectors。我使用了一种(丑陋的)方法来解决foverlaps 的问题(请参阅下面的答案)。一定有办法避免无效运算符错误,但仍然使用滚动连接?
  • @BramVisser 已修复 - 错误(主要)是由于 1.9.4 使 by-without-by 显式
  • 哇,真快。谢谢。我知道这很简单,并且似乎记得在导入 data.table 时启动消息说有关 .EACHI 的内容......猜猜实际上关注这些消息是一件好事。
【解决方案2】:

不幸的是,@eddi 建议的解决方案不再适用于 R 3.1.2data.table 1.9.4。失败并出现此错误:

Error in dt[J(group, date + 13), .I, roll = Inf]$.I : 
  $ operator is invalid for atomic vectors

以下代码有效,但使用新的foverlaps 函数是一种快速而肮脏的解决方法。我确定一定有办法修复滚动连接解决方​​案?

# Find start and end point of the intervals you want
dt[, start := date + 7]
dt[, end := date + 13]

# Make two data tables for overlapping dates.
dt2 <- dt[, c("group", "start", "end")]
dt[, date2 := date] # copy date (foverlaps need an interval).

# Sort by date and overlap-merge with week ranges.
setkey(dt, group, date, date2)
dt3 <- foverlaps(dt2, dt, by.x=c("group", "start", "end"))

# Count unique values to get number of records in following week.
setkey(dt, group, start, end)
setkey(dt3, group, i.start, i.end)
dt4 <- unique(dt)[dt3]
dt4[, count := ifelse(is.na(i.start), 0L, length(unique(i.start))), by=date]

# Cleaning up.
dt5 <- dt[unique(dt4)]
dt5 <- dt5[, c("date", "group", "count")]

# > dt5
#    date group count
# 1:    1     a     2
# 2:    9     a     1
# 3:   10     a     1
# 4:   15     a     0
# 5:   18     a     0
# 6:    3     b     0
# 7:    4     b     0
# 8:    7     b     1
# 9:    7     b     1
#10:   19     b     1
#11:   21     b     0
#12:   27     b     0

对简单的修复非常好奇,如果有的话。

【讨论】:

  • 只需删除该命令末尾的$.I。它已经返回一个向量。查看来自 v1.9.4 README.md 文件 here 的 NEWS 以了解更改。
【解决方案3】:

为什么不直接使用你创建的循环呢?

dt[,count:=date]
for(i in 1:length(dt$date)){
    set(dt,i,3L, sum(dt$date >= dt$date[i] + 7 &
                           dt$date < dt$date[i] + 14 &
                           dt$group == dt$group[i]))
}
dt
#    date group count
# 1:    1     a     2
# 2:    9     a     1
# 3:   10     a     1
# 4:   15     a     0
# 5:   18     a     0
# 6:    3     b     0
# 7:    4     b     0
# 8:    7     b     1
# 9:    7     b     1
#10:   19     b     1
#11:   21     b     0
#12:   27     b     0

by 的工作方式类似于tapply。您通过列(例如组)中的变量将data.table 溢出到 mini data.tables 中,在整个 mini data.table 上执行一个函数,为每个 mini data.table 返回一些内容,然后组合返回的内容以生成您的输出。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-29
    相关资源
    最近更新 更多