data.table中shift函数在i与j部分按by分组行为差异的原因咨询
i与by的执行顺序——为什么shift在i中不分组 这确实是个容易踩坑的点,咱们从data.table的核心执行逻辑入手,就能搞明白为啥会出现你看到的情况了。
首先得明确DT[i,j,by]的执行顺序:先执行i进行全局行筛选,再按by分组,最后在每个分组里执行j的操作。这个顺序是关键!
你的代码里发生了什么?
咱们一步步拆解:
i部分的shift(a,1)是全局操作:
当你写DT[!is.na(shift(a, 1)), ...]时,shift(a,1)是对整个DT的a列进行位移,完全不考虑后面的by = b——因为by是在i筛选完成后才会生效的。所以这一步的shift结果是NA,1,2,3,4,5,6,!is.na()筛选后,你保留了第2到7行(去掉了第一行)。j部分的shift(a,1)是分组操作:
筛选完成后,data.table才会按b分组。此时b="A"的组只剩下第2、3行(原始a值为2、3),对这个组做shift(a,1)得到的就是NA,2;b="B"的组是第4到7行(a值4、5、6、7),shift结果是NA,4,5,6。这就导致第2行的xy值是NA,也就是你看到的问题。
为什么i里的shift不识别by?
这是data.table的设计逻辑决定的:i的作用是先从整个数据集里选出要处理的行,这个阶段还没有进行分组,所以任何在i中调用的函数(包括shift)都是作用于全局的,而非分组后的子集。by参数的作用范围只限于j阶段的分组计算,不会回溯影响i的筛选逻辑。
怎么实现“分组内位移后筛选非NA”的需求?
如果你的目标是对每个分组内的a做位移,同时去掉每组的第一行(也就是位移后为NA的行),可以换个顺序:先分组计算xy,再筛选,或者直接在分组内处理:
# 方法1:先分组计算位移,再筛选 DT[, xy := shift(a, 1), by = b] DT <- DT[!is.na(xy)] # 方法2:直接在分组内保留需要的行(更高效) DT <- DT[, if (.N > 1) .SD[, xy := shift(a, 1)], by = b]
这样就能得到每个分组内位移后非NA的结果啦。
内容的提问来源于stack exchange,提问作者User878239

