我是 R 新手,这是我关于 stackoverflow 的第一个问题。
我在尝试
示例数据:
id code date_down date_up
1: 1 p 2019-01-01 2019-01-02
2: 1 f 2019-01-02 2019-01-03
3: 2 f 2019-01-02 2019-01-02
4: 2 p 2019-01-03 <NA>
5: 3 p 2019-01-04 <NA>
6: 4 <NA> 2019-01-05 2019-01-05
7: 5 f 2019-01-07 2019-01-08
8: 5 p 2019-01-07 2019-01-08
9: 5 p 2019-01-09 2019-01-09
10: 6 f 2019-01-10 2019-01-10
11: 6 p 2019-01-10 2019-01-10
12: 6 p 2019-01-10 2019-01-11
我想做的是
id
date_up
对于第一行进一步向下,code = 'p'
和 date-up
(找到的行)大于 date-down
对于我正在更新的行。 我的预期结果是:
id code date_down date_up founddate
1: 1 p 2019-01-01 2019-01-02 <NA>
2: 1 f 2019-01-02 2019-01-03 <NA>
3: 2 f 2019-01-02 2019-01-02 <NA>
4: 2 p 2019-01-03 <NA> <NA>
5: 3 p 2019-01-04 <NA> <NA>
6: 4 <NA> 2019-01-05 2019-01-05 <NA>
7: 5 f 2019-01-07 2019-01-08 2019-01-08
8: 5 p 2019-01-07 2019-01-08 2019-01-09
9: 5 p 2019-01-09 2019-01-09 <NA>
10: 6 f 2019-01-10 2019-01-10 2019-01-11
11: 6 p 2019-01-10 2019-01-10 2019-01-11
12: 6 p 2019-01-10 2019-01-11 <NA>
我尝试了很多变体,使用
.SD
, .N
,创建一个新列DT[, idcount:= seq_leg(.N),by=id]
,但并没有真正到任何地方。非常感谢任何帮助。还有对 data.table 的任何好的引用:) 非常感谢
编辑:
我编辑了提供的原始数据以提供一个更微妙的示例,其中第 10 行使用第 12 行的数据进行更新,因为第 12 行位于 id 子集中并符合资格条件。第 11 行不符合资格条件,因此数据不用于更新第 10 行。还包括我第一次使用
dput
!示例数据为
dput
代码:dt <- structure(list(
id = c(1L, 1L, 2L, 2L, 3L, 4L, 5L, 5L, 5L, 6L, 6L, 6L),
code = c("p", "f", "f", "p", "p", "<NA>", "f", "p", "p", "f", "p", "p"),
date_down = structure(c(17897, 17898, 17898, 17899, 17900, 17901, 17903, 17903, 17905, 17906, 17906, 17906), class = "Date"),
date_up = structure(c(17898, 17899, 17898, NA, NA, 17901, 17904, 17904, 17905, 17906, 17906, 17907), class = "Date")),
class = c("data.table", "data.frame"),
row.names = c(NA, -12L))
setDT(dt) # to reinit the internal self ref pointer (known issue)
最佳答案
按组将 data.table 连接到其自身的子集,以从匹配非相等条件的行中获取值。
概括:
data.table
候选解决方案针对 OP 的实际数据集(1.4M 记录)的性能测试。
用于连接的列)在
on
中条款。 易于遵循以比较不同
data.table
选项和语法选择。 方法
工作通过
data.table
为此,我将其分解为 OP 问题的以下步骤:解决方案1:
# Add row numbers to all records in dt (only because you
# have criteria based on comparing sequential rows):
dt[, row := .I]
# Compute result columns ( then standard assignment into dt using <- )
dt$found_date <-
dt[code=='p'][dt, # join dt to the data.table matching your criteria, in this case dt[code=='p']
.( x.date_up ), # columns to select, x. prefix means columns from dt[code=='p']
on = .(id==id, row > row, date_up > date_down), # join criteria: dt[code=='p'] fields on LHS, main dt fields on RHS
mult = "first"] # get only the first match if multiple matches
请注意上面的连接表达式:
i
在这种情况下是您的主要 dt。通过这种方式,您可以从主 data.table 中获取所有记录。 x
是您要从中查找匹配值的子集(或任何其他 data.table)。 结果匹配请求的输出:
dt
id code date_down date_up row found_date
1: 1 p 2019-01-01 2019-01-02 1 <NA>
2: 1 f 2019-01-02 2019-01-03 2 <NA>
3: 2 f 2019-01-02 2019-01-02 3 <NA>
4: 2 p 2019-01-03 <NA> 4 <NA>
5: 3 p 2019-01-04 <NA> 5 <NA>
6: 4 <NA> 2019-01-05 2019-01-05 6 <NA>
7: 5 f 2019-01-07 2019-01-08 7 2019-01-08
8: 5 p 2019-01-07 2019-01-08 8 2019-01-09
9: 5 p 2019-01-09 2019-01-09 9 <NA>
10: 6 f 2019-01-10 2019-01-10 10 2019-01-11
11: 6 p 2019-01-10 2019-01-10 11 2019-01-11
12: 6 p 2019-01-10 2019-01-11 12 <NA>
注:您可以删除
row
做专栏dt[, row := NULL]
如果你喜欢。解决方案2:
连接和查找结果列的逻辑与上述相同,但 现在使用“通过引用分配”
:=
创建 found_date
在 dt
:dt[, row := .I] # add row numbers (as in all the solutions)
# Compute result columns ( then assign by reference into dt using :=
# dt$found_date <-
dt[, found_date := # assign by reference to dt$found_date
dt[code=='p'][dt,
.( x.date_up ),
on = .(id==id, row > row, date_up > date_down),
mult = "first"]]
在解决方案 2 中,将我们的结果“通过引用”分配到 dt 的细微变化应该比解决方案 1 更有效。解决方案 1 以完全相同的方式计算结果 - 唯一的区别是解决方案 1 使用标准分配
<-
创建 dt$found_date
(效率较低)。解决方案3:
喜欢解决方案 2 但 正在使用
.(.SD)
代替 dt
引用原始 dt 而不直接命名。dt[, row := .I] # add row numbers (as in all the solutions)
setkey(dt, id, row, date_down) #set key for dt
# For all rows of dt, create found_date by reference :=
dt[, found_date :=
# dt[code=='p'][dt,
dt[code=='p'][.(.SD), # our subset (or another data.table), joined to .SD (referring to original dt)
.( x.date_up ),
on = .(id==id, row > row, date_up > date_down),
mult = "first"] ]
上面的 .SD 引用回我们正在分配回的原始 dt。它对应于包含在第一个
dt[,
中选择的行的 data.table 的子集。这是所有行,因为我们没有过滤它。注意:在解决方案 3 中,我使用了
setkey()
设置 key 。我应该在解决方案 1 和解决方案 2 中这样做 - 但是,在@OllieB 成功测试它们之后,我不想更改这些解决方案。解决方案4:
喜欢解决方案 3 但 比以前多一次使用 .SD。我们的主要数据表名称
dt
现在在我们的整个表达式中只出现一次! # add row column and setkey() as previous solutions
dt[, found_date :=
# dt[code=='p'][.(.SD),
.SD[code=='p'][.SD, # .SD in place of dt at left! Also, removed .() at right (not sure on this second change)
.(found_date = x.date_up),
on = .(id==id, row > row, date_up > date_down),
mult = "first"]]
随着我们的 data.table 名称上方的更改
dt
只出现一次。我非常喜欢它,因为它可以轻松地在其他地方复制、改编和重用。另请注意:我以前使用过的地方
.(SD)
我现在已经删除了 .SD
周围的 .()因为它似乎不需要它。但是,对于该更改,我不确定它是否具有任何性能优势,或者它是否是 data.table 首选语法。如果有人可以添加评论以就此提出建议,我将不胜感激。解决方案5:
像以前的解决方案,但 利用
by
在加入 时显式地对操作的子集进行分组# add row column and setkey() as previous solutions
dt[, found_date :=
.SD[code=='p'][.SD,
.(found_date = x.date_up),
# on = .(id==id, row > row, date_up > date_down),
on = .(row > row, date_up > date_down), # removed the id column from here
mult = "first"]
, by = id] # added by = id to group the .SD subsets
在最后一个解决方案中,我将其更改为使用
by
子句在 id
上显式分组 .SD 子集.注:与解决方案 1 - 4 相比,解决方案 5 针对 OllieB 的实际数据表现不佳。但是,测试我自己的模拟数据时,我发现解决方案 5 在来自
id
的唯一组的数量上表现良好。列低:- 在 150 万条记录中只有 6 个组,此解决方案的运行速度与其他解决方案一样快。
- 在 150 万条记录中有 4 万个组,我看到了与 OllieB 报告的类似的糟糕表现。
结果
解决方案 1 - 4 表现良好:
解决方案5
by
子句)表现不佳,OllieB 对其真实数据的测试耗时 577 秒。 使用的版本
数据表版本:1.12.0
R 版本 3.5.3 (2019-03-11)
可能的进一步改进:
on
调用 [经常] 更有效的二级索引和自动索引。 对 data.table 的引用
作为您问题的一部分,您要求提供“对 data.table 的任何良好引用”。我发现以下内容很有帮助:
重要提示this answer by @Arun这解释了“实现 on= 参数的原因”表明可能不再需要设置键:
It is therefore essential to figure out if the time spent on reordering the entire data.table is worth the time to do a cache-efficient join/aggregation. Usually, unless there are repetitive grouping / join operations being performed on the same keyed data.table, there should not be a noticeable difference.
In most cases therefore, there shouldn't be a need to set keys any more. We recommend using on= wherever possible, unless setting key has a dramatic improvement in performance that you'd like to exploit.
这个 SO 问题似乎是有关不同 data.table
的信息中心加入:How to join (merge) data frames (inner, outer, left, right)?最后,data.table cheat sheet是一个很好的引用(来自 GitHub 上 data.table Getting started Wiki 上的链接)。
与往常一样,如果有人提出建议,我将不胜感激,因为也许这可以进一步改进。
如果您可以添加任何内容,请随时发表评论、更正或发布其他解决方案。
关于r - 使用满足条件的同一组中的第一个下一行设置列值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55070842/