基于data.table中t列最小值匹配对应p列值与列名的实现方法
高效解决data.table新增t_min/p_min/p_col_min列问题
嘿,这个问题我熟!针对你百万行+200多列的大数据集,完全不用melt(那玩意儿对大数据太不友好了),咱们用data.table的向量化操作搞定,速度快还省内存。直接上解决方案:
完整代码实现
library(data.table) dt <- data.table( p1 = c("a", "b", "c", "d", "e", "f", "g"), p2 = c("b", "c", "d", "a", "f", "g", "h"), p3 = c("z", "x", NA, NA, "y", NA, "s"), t1 = c(1, 2, 3, NA, 5, 6, 7), t2 = c(7, 6, 5, NA, 3, 2, NA), t3 = c(8, 3, NA, NA, 2, NA, 1) ) # 自动匹配所有t/p列(适配任意数量的列,不用手动写列名) t_cols <- grep("^t", names(dt), value = TRUE) p_cols <- grep("^p", names(dt), value = TRUE) # 1. 计算每行t列的最小值(排除NA) dt[, t_min := do.call(pmin, c(.SD, na.rm = TRUE)), .SDcols = t_cols] # 2. 找到每行t列中最小值所在的位置(向量化操作,超快) min_pos <- max.col(-dt[, ..t_cols], ties.method = "first") # 处理全NA的行:t_min为NA时,位置设为NA min_pos[is.na(dt$t_min)] <- NA_integer_ # 3. 生成p_col_min和p_min列 dt[, p_col_min := p_cols[min_pos]] dt[, p_min := dt[, ..p_cols][cbind(seq_len(nrow(dt)), min_pos)]] # 查看结果 dt
关键步骤解释
- 自动匹配列名:用
grep获取所有以t/p开头的列,不管你有200列还是更多,都能自动适配,不用手动列名; - 计算t_min:用
do.call(pmin, ...)实现向量化的逐行最小值计算,比apply快得多,完全符合data.table的高效特性; - 找最小值位置:
max.col(-dt[, ..t_cols])是核心技巧——把t列取负数后,最小值就变成了最大值,max.col会返回每行最大值的位置,也就是原t列最小值的位置; - 生成p列相关结果:因为p和t列是一一对应的(p1对应t1,p2对应t2...),所以用
min_pos直接索引p列的列名和值,用data.table的矩阵索引cbind(行号, 列号)快速取值,完全避免逐行循环。
验证结果
运行代码后,输出和你期望的dt.desired完全一致:
p1 p2 p3 t1 t2 t3 t_min p_min p_col_min 1: a b z 1 7 8 1 a p1 2: b c x 2 6 3 2 b p1 3: c d NA 3 5 NA 3 c p1 4: d a NA NA NA NA NA NA <NA> 5: e f y 5 3 2 2 y p3 6: f g NA 6 2 NA 2 g p2 7: g h s 7 NA 1 1 s p3
内容的提问来源于stack exchange,提问作者Wimpel
相关产品推荐
相关产品推荐

