如何用data.table语法替代循环,按country拼接对应survey字符串?
问题描述
我有一个data.table,其中country列存在重复值,survey列的每个观测都是唯一字符串。我希望创建一个新变量,将单个country对应的所有survey字符串按行累积拼接起来。目前我已经通过索引和for循环实现了该功能,但想知道有没有更简便的data.table原生语法实现方式。
我的示例代码如下:
library(data.table) dt <- data.table(country = c("Belgium","Belgium","Bolivia","Brazil","Brazil","Brazil"), survey = c("BE01, BE03, BE04","BE05, BE07, BE11", "BO11, BO13", "BR01, BR02, BR03", "BR05","BR12, BR13")) dt[, index := seq(1, .N), by = "country"] for(cntry in unique(dt$country)){ tmp_ind <- max(dt[country == eval(cntry)]$index) dt[country == eval(cntry) & index ==1, all_surveys := survey] if(tmp_ind > 1) { for(i in 2:tmp_ind){ dt[country == eval(cntry) & index ==i, all_surveys := paste0(dt[country == eval(cntry) & index == i-1, all_surveys], survey)] } } }
运行后得到的预期结果:
> dt country survey index all_surveys 1: Belgium BE01, BE03, BE04 1 BE01, BE03, BE04 2: Belgium BE05, BE07, BE11 2 BE01, BE03, BE04BE05, BE07, BE11 3: Bolivia BO11, BO13 1 BO11, BO13 4: Brazil BR01, BR02, BR03 1 BR01, BR02, BR03 5: Brazil BR05 2 BR01, BR02, BR03BR05 6: Brazil BR12, BR13 3 BR01, BR02, BR03BR05BR12, BR13
简化实现方式
在data.table里可以直接利用分组结合累积拼接的方式,不需要额外创建索引和嵌套循环,一行代码就能达成需求:
library(data.table) dt <- data.table(country = c("Belgium","Belgium","Bolivia","Brazil","Brazil","Brazil"), survey = c("BE01, BE03, BE04","BE05, BE07, BE11", "BO11, BO13", "BR01, BR02, BR03", "BR05","BR12, BR13")) # 核心逻辑:按country分组,生成累积拼接的字符串 dt[, all_surveys := Reduce(paste0, survey, accumulate = TRUE), by = country]
运行后得到的结果和你用for循环实现的完全一致:
> dt country survey all_surveys 1: Belgium BE01, BE03, BE04 BE01, BE03, BE04 2: Belgium BE05, BE07, BE11 BE01, BE03, BE04BE05, BE07, BE11 3: Bolivia BO11, BO13 BO11, BO13 4: Brazil BR01, BR02, BR03 BR01, BR02, BR03 5: Brazil BR05 BR01, BR02, BR03BR05 6: Brazil BR12, BR13 BR01, BR02, BR03BR05BR12, BR13
代码说明
Reduce(paste0, survey, accumulate = TRUE):Reduce会依次对当前分组内的survey元素执行paste0拼接,accumulate=TRUE会保留每一步的拼接结果,正好实现按行累积拼接的效果。by = country:确保每个国家的拼接独立进行,不会跨组混合内容。
如果需要在拼接的字符串之间添加分隔符(比如逗号加空格),只需把paste0替换为带sep参数的paste即可:
dt[, all_surveys := Reduce(function(x, y) paste(x, y, sep = ", "), survey, accumulate = TRUE), by = country]
内容的提问来源于stack exchange,提问作者BLP92
相关产品推荐
相关产品推荐

