R中使用data.table与.SD循环修改单行的技术问询
先明确示例场景的基础数据:
library(data.table) # 构建示例数据表 temp <- data.table( a = c(10, 0), b = c(30, 10), worksA = NA_real_, worksB = NA_real_ ) # 初始化第一行的worksA、worksB temp[1, `:=`(worksA = a, worksB = b)]
我们的目标是给第2行的worksA、worksB分别赋值为a、b列前两行的均值(即5和20)。
你尝试的代码出错原因分析
temp[2,:=(tryA=mean(a[1:2]),tryB=mean(b[1:2]))]生成NA
当你用temp[2, ...]筛选行后,括号内的表达式是在第2行的局部环境中执行的。此时a[1:2]等效于temp[2, a][1:2]——也就是只取第2行的a值(0),再取前两个元素,第二个元素自然是NA,mean()计算包含NA的向量就会返回NA。temp[2, c("tryA", "tryB"):=lapply(.SD[1:2], mean), .SDcols=c("a", "b")]生成NA
和上面的问题本质一样:.SD在这里是筛选后的子集(仅第2行的a、b列),.SD[1:2]试图取这个子集的前两行,但子集只有1行,所以第二行是NA,均值计算结果还是NA。添加
na.rm=TRUE后仅计算当前行均值
此时.SD[1:2]里只有当前行的有效值和一个NA,na.rm=TRUE会忽略NA,最终计算的就是当前行的数值,而非前两行的均值。不指定行选择时所有行都被设为前两行均值
没有行筛选条件时,.SD是整个数据表的a、b列,.SD[1:2]就是这两列的前两行数据,lapply计算出的均值会被赋值给所有行,所以所有行的tryA、tryB都会变成前两行的均值。
用.SD结合自定义函数实现单行修改的正确方式
要在单行修改时引用整个表的前两行数据,必须明确从完整数据表中取数,而非筛选后的子集。以下是几种可行方案:
方案1:通过.SDcols传递列名,自定义函数调用完整数据表
# 定义计算指定列目标行均值的函数 calc_row_mean <- function(col_name, dt, target_rows) { mean(dt[[col_name]][target_rows]) } # 给第2行赋值 temp[2, c("worksA", "worksB") := lapply(.SDcols, calc_row_mean, dt=temp, target_rows=1:2), .SDcols=c("a", "b")]
方案2:直接在赋值表达式中引用完整数据表的.SD
temp[2, c("worksA", "worksB") := lapply(temp[, .SD, .SDcols=c("a", "b")][1:2], mean)]
验证结果
执行后查看数据表:
print(temp) # a b worksA worksB # 1:10 30 10 30 # 2: 0 10 5 20
完全符合预期。
内容的提问来源于stack exchange,提问作者Yohan Jaget

