如何基于多列使用data.table的dcast实现有序移位列转换?
问题描述
现有一个data.table格式的数据集DT,结构如下:
DT = data.table( year=c('1981', '1981', '1981', '2005', '2005', '2005'), value=c(2, 8, 16, 3, 9, 27), order =c(1,2,3,1,2,3))
对应的表格:
| year | value | order |
|---|---|---|
| '1981' | 2 | 1 |
| '1981' | 8 | 2 |
| '1981' | 16 | 3 |
| '2005' | 3 | 1 |
| '2005' | 9 | 2 |
| '2005' | 27 | 3 |
需要基于每个year内的order顺序,生成有序移位后的新列,最终结果如下:
| year | order1 | order2 | order3 |
|---|---|---|---|
| '1981' | 2 | 8 | 16 |
| '1981' | 8 | 16 | NA |
| '1981' | 16 | NA | NA |
| '2005' | 3 | 9 | 27 |
| '2005' | 9 | 27 | NA |
| '2005' | 27 | NA | NA |
已知仅按order转换可以用dcast(DT, year ~ order, value.var = c('value')),但需要结合移位逻辑实现需求,且不想手动创建移位后的order列,也不想多次dcast后合并,求更优雅的方法。
优雅解决方案
直接利用data.table的分组+shift函数就能实现,不用手动构造额外列,也不需要多次dcast合并:
固定最大order值的写法
如果已知最大的order是3,可以直接生成对应的移位列:
# 按year分组,生成order1到order3 DT[, c(paste0("order", 1:3)) := .( value, # order1就是原value shift(value, type = "lead"), # order2是下一个value shift(value, n = 2, type = "lead") # order3是下两个value ), by = year] # 删掉不需要的原列(可选) DT[, c("value", "order") := NULL]
动态适配最大order值的写法
如果不确定最大的order值,可以先计算最大值,再批量生成移位列:
max_order = max(DT$order) DT[, c(paste0("order", 1:max_order)) := lapply(0:(max_order-1), function(n) { shift(value, n = n, type = "lead") }), by = year] # 删掉原列(可选) DT[, c("value", "order") := NULL]
执行后就能得到目标表格,这种方法直接在原数据集上操作,代码简洁高效,完全贴合data.table的使用风格。
内容的提问来源于stack exchange,提问作者Hal
相关产品推荐
相关产品推荐

