如何在data.table中向左滑动填充数据消除melt后的col列间隙
解决data.table非NA左移后melt得到连续列的问题
针对你遇到的场景:原宽表存在NA值,直接melt后col列会出现间隙,想要先把每行的非NA值左移、NA值挪到右边,再执行melt得到连续的col列,这里给你两种基于data.table的解决方案:
方案1:逐行处理实现非NA左移(直观易懂)
先对原数据表的每行做整理,把非NA值全部左移,剩下的位置补NA,之后再melt就没问题了:
library(data.table) # 你的原数据表 x <- data.table(id = c(1,2,3,4), "1" = c("a", "a", "a", "a"), "2" = c("b", NA, "b", NA), "3" = c(NA, "b", NA, "b"), "4" = c("c", "c", "c", "c")) # 逐行处理:提取非NA值,补NA到原列数,重新命名列 x_processed <- x[, c(list(id = id), setNames( as.data.table(t(apply(.SD, 1, function(row) { non_na <- na.omit(row) c(non_na, rep(NA, length(row) - length(non_na))) }))), as.character(1:ncol(.SD)) )), .SDcols = -"id"]
这段代码的逻辑很直白:
- 对除
id外的所有列(.SD)逐行操作,先把该行的非NA值挑出来,再补足够的NA让每行长度和原数据一致 - 转置结果(因为
apply返回的矩阵是按列排的),转成data.table后给列重新命名为"1"到"4" - 最后把原
id列和处理后的列组合起来
处理完的x_processed长这样:
id 1 2 3 4 1: 1 a b c NA 2: 2 a b c NA 3: 3 a b c NA 4: 4 a b c NA
接下来再执行你原本的melt操作:
y <- melt(x_processed, id.vars = "id", measure.vars = as.character(1:4), variable.name = "col", value.name = "letter") %>% na.omit(letter) %>% setorder(id)
最终得到的y里col就是连续的了:
id col letter 1: 1 1 a 2: 1 2 b 3: 1 3 c 4: 2 1 a 5: 2 2 b 6: 2 3 c 7: 3 1 a 8: 3 2 b 9: 3 3 c 10: 4 1 a 11: 4 2 b 12: 4 3 c
方案2:纯data.table高效操作(适合大数据量)
如果你的数据表行数很多,apply逐行操作效率可能不够高,那可以用melt+dcast的组合来实现,全程用data.table的向量化操作:
# 先把原表melt,去掉NA值 x_melt <- melt(x, id.vars = "id", measure.vars = as.character(1:4), variable.name = "col", value.name = "letter") %>% na.omit(letter) %>% setorder(id) # 按id分组,给每个非NA值分配连续的序号作为新的col x_melt[, col := as.character(seq_len(.N)), by = id] # 转成宽表,补NA到原列数,之后再melt回来 x_processed2 <- dcast(x_melt, id ~ col, value.var = "letter") # 补NA到原来的4列数(如果有的id的非NA值少于4个) x_processed2[, paste0(seq(ncol(x_processed2), 4)) := NA] y2 <- melt(x_processed2, id.vars = "id", measure.vars = setdiff(colnames(x_processed2), "id"), variable.name = "col", value.name = "letter") %>% na.omit(letter) %>% setorder(id)
这种方法完全不用逐行循环,性能更优,最终得到的y2和方案1的结果完全一致。
内容的提问来源于stack exchange,提问作者Dylan Russell
相关产品推荐
相关产品推荐

