如何在R的data.table中实现分组slice_min并保留全部列
用data.table实现分组筛选最小值行并保留全部列
需求说明
拥有20万+行的大型数据集,需按hv001和hv002两列分组,筛选出每组中hv101列值最小的行,同时保留所有其他列。使用dplyr可实现需求但速度过慢,现有data.table写法仅返回分组后的最小值列,需优化写法达成目标。
样本数据
hv001 hv002 hv101 hv104 hv105 1 1 1 1 1 35 2 1 1 2 2 22 3 1 1 3 2 2 4 1 1 3 2 0 5 1 4 1 1 35 6 1 4 2 2 32 7 1 4 3 2 4 8 1 4 3 2 2 9 1 4 3 1 1 10 1 7 1 1 51
数据结构:
structure(list(hv001 = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), hv002 = c(1, 1, 1, 1, 4, 4, 4, 4, 4, 7), hv101 = c(1, 2, 3, 3, 1, 2, 3, 3, 3, 1), hv104 = c(1, 2, 2, 2, 1, 2, 2, 2, 1, 1), hv105 = c(35, 22, 2, 0, 35, 32, 4, 2, 1, 51)), row.names = c(NA, -10L), class = "data.frame")
原有实现代码
- dplyr写法(速度慢):
df |> group_by(hv001, hv002) |> slice_min(hv101)
- 现有data.table写法(仅返回最小值列):
df_dt <- as.data.table(df) df_dt[, .(min_hv101 = min(hv101)), by = c("hv001", "hv002")]
解决方案
方法1:保留所有最小值行(与dplyr默认行为一致)
利用data.table的.SD对象(代表每个分组的子数据集),筛选出hv101等于分组内最小值的所有行,自动保留全部列:
df_dt <- as.data.table(df) result <- df_dt[, .SD[hv101 == min(hv101)], by = .(hv001, hv002)]
样本数据运行输出:
hv001 hv002 hv101 hv104 hv105 1: 1 1 1 1 35 2: 1 4 1 1 35 3: 1 7 1 1 51
方法2:仅保留第一个最小值行(对应dplyr slice_min(n=1))
若每组只需保留第一行hv101最小的记录,使用which.min()定位行索引:
df_dt <- as.data.table(df) result <- df_dt[, .SD[which.min(hv101)], by = .(hv001, hv002)]
效率说明
data.table的分组操作基于底层C实现,在20万+行的大型数据集上,上述写法的运行速度远快于dplyr的slice_min,同时完美满足保留全部列的需求。
内容的提问来源于stack exchange,提问作者Victor Hartman
相关产品推荐
相关产品推荐

