使用cut()基于列表断点批量转换数值变量为因子变量遇问题
批量将数值变量按自定义断点转为因子变量的问题
我有一个包含多个数值变量的数据集,需要将它们转换为因子变量。断点由之前运行的模型生成,通过lapply存储在一个命名列表中,且每个变量的断点数量不同。模型生成的参数不包含0和变量最大值,必须手动传入cut()函数,否则输出结果异常。我尝试了dplyr的mutate(across)方法和data.table结合.SDcols与lapply的方法,但生成的结果均无效;手动逐列处理可正常运行,难道只能使用for循环解决该问题?
示例数据
V1 <- runif(100) V2 <- runif(100) V3 <- runif(100) df <- data.frame(V1, V2, V3) cutlist <- list(c(0.1), c(0.3, 0.4), c(0.1, 0.5, 0.6)) names(cutlist) <- c("V1","V2","V3")
尝试的无效代码
dplyr 版本
df %>% mutate( across(.cols = names(cutlist), .fns = ~cut(.x, breaks = c(0, cutlist[which(names(cutlist) == names(.x))], max(.x, na.rm=T))), .names = "{.col}_cuts") )
data.table 版本
out_cols = paste0(names(cutlist),".cut") df[, c(out_cols) := lapply(.SD, function(x){cut(x, breaks = c(0, cutlist[which(names(cutlist) == names(x))], max(x, na.rm=T)))}), .SDcols = names(cutlist)]
可正常运行的手动处理代码
df$V1.cut <- cut(df$V1,breaks=c(0, cutlist$V1, max(df$V1, na.rm=T)))
请问我忽略了什么?
问题根源
你忽略了一个关键细节:在across的匿名函数里,.x是当前列的数值向量,它本身没有数据框的列名(names(.x)返回的是向量元素的名字,而非列名);同理,data.table的lapply(.SD, function(x))里的x也是单个列的向量,names(x)同样不是数据框的列名。这就导致which(names(cutlist) == names(.x))永远匹配不到正确的断点,最终传入cut()的breaks参数完全错误。
修正后的解决方案
dplyr 实现
利用cur_column()函数获取当前处理的列名,直接匹配cutlist中的对应元素:
df %>% mutate( across( all_of(names(cutlist)), ~cut(.x, breaks = c(0, cutlist[[cur_column()]], max(.x, na.rm = TRUE))), .names = "{.col}_cuts" ) )
data.table 实现
用Map函数将列数据和对应的断点一一配对处理,避免依赖向量的名字:
library(data.table) setDT(df) out_cols <- paste0(names(cutlist), ".cut") df[, (out_cols) := Map( function(col_data, col_breaks) { cut(col_data, breaks = c(0, col_breaks, max(col_data, na.rm = TRUE))) }, .SD, cutlist[names(.SD)] ), .SDcols = names(cutlist)]
以上两种方法都能正确批量生成符合要求的因子列,无需使用for循环。
内容的提问来源于stack exchange,提问作者Wafa
相关产品推荐
相关产品推荐

