如何使用data.table高效创建新变量并指定列名?
批量生成data.table等级列的简化方案
问题背景
现有如下结构的data.table数据:
structure(list(english_score = c(3L, 4L, 3L, 3L, 4L, 3L, 4L, 2L, 4L, 2L, 3L, 3L, 2L, 2L, 3L, 4L, 3L, 3L, 4L, 3L, 4L, 3L, 2L ), math_score = c(4L, 4L, 3L, 4L, 4L, 4L, 3L, 2L, 3L, 3L, 4L, 2L, 4L, 2L, 4L, 2L, 3L, 3L, 2L, 2L, 2L, 4L, 2L), science_score = c(3L, 4L, 4L, 4L, 3L, 4L, 4L, 3L, 3L, 2L, 3L, 4L, 4L, 4L, 4L, 4L, 4L, 2L, 3L, 2L, 3L, 3L, 4L)), row.names = c(NA, -23L), class = c("data.table", "data.frame"), .internal.selfref = <pointer: 0x000002478ee34d50>)
需要为每科分数生成对应的等级列(如english_level),等级规则通过自定义函数实现,原方法是逐个列处理,希望用data.table语法简化流程。
原自定义函数:
myfunction<-function(x){case_when(x<2~"Level A", x>1 & x<3~"Level B", x>2 & x<4~"Level C", x>3~"Level D")}
原逐个处理代码:
DT[, english_level:=lapply(.SD, myfunction), .SDcols='english_score'] DT[, math_level:=lapply(.SD, myfunction), .SDcols='math_score'] DT[, science_level:=lapply(.SD, myfunction), .SDcols='science_score']
简化方案
方法1:批量处理多列,一步生成所有等级列
利用data.table的.SD(Subset of Data)和批量赋值特性,一次性处理所有分数列并生成对应等级列:
- 定义分数列列表和目标等级列名:
# 指定需要处理的分数列 score_cols <- c("english_score", "math_score", "science_score") # 生成对应的等级列名(比如english_score → english_level) level_cols <- paste0(sub("_score", "", score_cols), "_level")
- 批量赋值生成等级列:
DT[, (level_cols) := lapply(.SD, myfunction), .SDcols = score_cols]
这里(level_cols)用于将变量中的列名作为目标列,lapply(.SD, myfunction)对.SDcols指定的每一列应用自定义函数,直接生成所有等级列。
方法2:替换为data.table原生的fcase优化函数
原函数使用了dplyr::case_when,可以替换为data.table原生的fcase函数,更贴合data.table生态,执行效率也更高:
myfunction <- function(x){ fcase( x < 2, "Level A", x > 1 & x < 3, "Level B", x > 2 & x < 4, "Level C", x > 3, "Level D" ) }
替换后再执行方法1的批量赋值代码即可,效果完全一致。
方法3:melt+dcast转置处理(适合更复杂的多列场景)
如果后续有更多类似列需要处理,也可以通过转长再转宽的方式实现:
# 转长格式 DT_long <- melt(DT, measure.vars = score_cols, variable.name = "subject", value.name = "score") # 添加等级列 DT_long[, level := myfunction(score)] # 转宽格式合并回原表 DT <- dcast(DT_long, ... ~ sub("_score", "", subject), value.var = c("score", "level")) # 调整列名(可选,和目标结构对齐) setnames(DT, gsub("_level", "", names(DT))[grepl("level", names(DT))], level_cols)
这种方法适合列数极多的场景,但对于当前3列的情况,方法1更简洁高效。
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

