如何在R data.table中让.I函数仅作用于数据表特定元素
data.table 按条件跳过分组赋值实现方法
初始场景
先看初始代码和目标需求:
初始运行的代码会生成带全局序号Nbr、同ID下按顺序标注字母编号Class的数据表:
> DT ID Nbr Class 1: A 1 A 2: A 2 B 3: B 3 A 4: C 4 A 5: D 5 A DT <- data.table(ID = c("A","A","B","C","D")) DT[, c("Nbr", "Class") := .(.I, LETTERS[seq_len(.N)]), by = ID]
需求是用纯data.table原生语法修改代码,跳过ID为B的分组的Class编号逻辑,把所有ID为B的行的Class值统一设为NA,最终得到如下结果:
> DT ID Nbr Class 1: A 1 A 2: A 2 B 3: B 3 NA 4: C 4 A 5: D 5 A
实现方案
方案1:分组内直接做条件判断(单次遍历,效率更高)
利用data.table分组操作内置的.BY对象获取当前分组的ID值,直接在j的赋值逻辑里加分支判断,一次分组操作完成两列的赋值,不需要额外子集筛选:
DT <- data.table(ID = c("A","A","B","C","D")) DT[, c("Nbr", "Class") := .( .I, if (.BY$ID == "B") NA_character_ else LETTERS[seq_len(.N)] ), by = ID]
关键说明:
.BY是分组计算时的内置环境变量,存储当前分组所有by字段的取值,因为这里按ID分组,每个分组ID唯一,直接判断.BY$ID即可- 用
NA_character_而非通用NA是为了保持Class列的字符类型一致,避免类型转换警告
方案2:分两次按行条件赋值(逻辑直观,易理解)
利用data.table的i行筛选机制,先完成通用逻辑赋值,再单独处理ID为B的行,逻辑更直白,适合新手熟悉i筛选+j赋值的基础语法:
DT <- data.table(ID = c("A","A","B","C","D")) # Nbr是全局行号,不需要分组判断,直接赋值 DT[, Nbr := .I] # 筛选非B的ID,按分组规则生成Class DT[ID != "B", Class := LETTERS[seq_len(.N)], by = ID] # 筛选ID为B的行,统一赋值NA DT[ID == "B", Class := NA_character_]
两种写法运行后都能得到目标结果,数据量较大时优先选方案1,减少遍历次数提升性能。
内容的提问来源于stack exchange,提问作者Curious Jorge - user9788072
相关产品推荐
相关产品推荐

