我正在尝试逐组遍历data.table,以便在每个子组内有条件地为每行分配值。我可以选择所需的行,但是无法更新所选行中的目标变量。
我认为这可能是由于我必须将data.table切片两次。我正在使用一个名为data.table
的{{1}},该列具有dt
,group
,center
和date
列。此处的目标是使非中心记录(也称为var
)的var
的值与其最近的中心记录(按日期差)center==0
相匹配。假设center==1
是该行的位置索引,我想基于条件过滤来更新记录,然后在子组i
中搜索date
。
gp
但是当我跑步
dt[group == gp][i, var:= "new value"]
变量dt[group == gp][i, var]
似乎没有变化,也就是返回了var
。
上面的命令在for循环中,也许我没有在这里使用最佳实践。如果有人对以下for循环分享他/她的意见,我将不胜感激。谢谢。
"old value"
我知道data.table中有for( gp in unique(dt$group)){
tmp = dt[group==gp]
for( i in 1:nrow(tmp)){
new_val = tmp[center==1][which.min(abs(tmp[i, date]-tmp[center==1, date]),var]
dt[group == gp][i, var:= new_val]
}
}
和set
。但是我不知道如何使用.by
语法轻松地将条件搜索功能应用于每个子组。也许我可以在dt[, j=somefunction ,by=group]
上加一个小号,但是它比for循环快得多吗?性能的提高值得丧失可读性吗?
在下面的评论部分中,我找到了在data.table中同时结合逻辑索引和位置索引的技巧:
.SD
有关使用for循环是否是一个好主意的问题仍未得到解答。任何输入将不胜感激!
假设原始dt(按组和日期排序)如下所示:
dt[which(group == gp)[i], var := new_val]
我希望更新的dt为:
group center date var
1 0 10-01 NA
1 1 10-02 val1
1 0 10-03 NA
1 1 11-05 val2
2 1 10-02 val3
比方说,我们这里大约有10,000个组,每个组最多可以有1000行。
答案 0 :(得分:1)
对于此问题,您可能需要考虑使用滚动联接,如下所示:
dt[center==0L, var := dt[center!=0L][.SD, var, on=.(group, date), roll="nearest"]]
说明:
dt[center==0L
过滤要更新的行。
var :=
告诉data.table
,这是要更新的列。
dt[center!=0L]
过滤中心不为0的行。
dt[center!=0L][.SD, on=.(group, date)]
将步骤1(.SD
)中的行与步骤3中的行连接起来,, var,
选择此列作为输出。
有关?data.table
参数的帮助,请参见roll
。当roll='nearest'
时,它将查找联接中最接近的date
。请注意,滚动键应始终是on
参数中的最后一个元素。
输出:
group center date var
1: 1 0 2018-10-01 val1
2: 1 1 2018-10-02 val1
3: 1 0 2018-10-03 val1
4: 1 1 2018-11-05 val2
5: 2 1 2018-10-02 val3
数据:
library(data.table)
dt <- fread("group center date var
1 0 2018-10-01 NA
1 1 2018-10-02 val1
1 0 2018-10-03 NA
1 1 2018-11-05 val2
2 1 2018-10-02 val3")
dt[, date := as.Date(date, format="%Y-%m-%d")]