R data.table分组赋值新变量时的赋值逻辑疑问
理解data.table分组赋值的两种行为差异
这是个非常典型的data.table细节问题,核心在于已有列的修改赋值和新列的创建赋值遵循不同的规则,我来一步步拆解:
1. 第一次赋值(创建新列)的逻辑
执行data[,var_to_impute:=value_to_see[t==3],by=c("id")]时,var_to_impute是全新的列:
- 对于
id=1组,组内t==3的行只有1行,返回的value_to_see是长度为1的向量[3]。data.table会自动将这个向量循环补齐到组内的行数(2行),所以该组两行的var_to_impute都被设为3。 - 对于
id=2组,t==3的行同样返回长度1的向量[6],循环补齐到3行,所以该组三行的var_to_impute都为6。
此时的data表是:
id t value_to_see var_to_impute 1: 1 1 1 3 2: 1 3 3 3 3: 2 1 4 6 4: 2 2 5 6 5: 2 3 6 6
2. 直接修改已有列的逻辑
当执行data[,var_to_impute:=value_to_see[t==2],by=c("id")]时,var_to_impute已经存在,这时候data.table的行为是按位置替换,不主动填充NA:
- 对于
id=1组,组内没有t==2的行,value_to_see[t==2]返回的是长度为0的空向量。因为没有可替换的值,data.table会保留该组原有var_to_impute的所有值(也就是之前的3)。 - 对于
id=2组,t==2的行返回长度1的向量[5],data.table会将这个向量循环补齐,替换该组所有行的var_to_impute值为5。
此时的结果就是你看到的:id=1的var_to_impute仍保留旧值3,而id=2的全部变为5。
3. 删除后重新创建新列的逻辑
当你先执行data[,var_to_impute:=NULL]删除列,再执行赋值时,var_to_impute变回全新列,data.table会按组内行数匹配,不足补NA的规则处理:
- 对于
id=1组,value_to_see[t==2]返回空向量,由于需要生成和组内行数(2行)匹配的新列,data.table会自动用NA填充所有行。 - 对于
id=2组,同样将[5]循环补齐到3行,所以该组三行的var_to_impute都为5。
最终结果符合你的预期:id=1的var_to_impute为NA,id=2的全部为5。
如何避免这种差异?
如果你希望无论是修改已有列还是创建新列,都能得到相同的结果(即没有匹配项时设为NA),可以明确用ifelse或者更高效的data.table方式控制逻辑:
# 用ifelse明确指定NA填充 data[,var_to_impute:=ifelse(t==2, value_to_see, NA),by=c("id")] # 用match更高效地定位匹配项 data[,var_to_impute:=value_to_see[match(2, t)],by=c("id")]
match(2, t)会返回组内t=2的位置,没有匹配项时返回NA,这样就能统一返回和组内行数匹配的向量,无论列是否存在,结果都会一致。
内容的提问来源于stack exchange,提问作者Vitalijs
相关产品推荐
相关产品推荐

