You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用vctrs::vec_chop替代filter(row_number() == 1)优化R代码?

问题:用vctrs::vec_chop替代分组取首行逻辑的实现建议

我正在优化R代码,尝试用vctrs包的vec_chop()替代以下分组取每组首行的逻辑,但没能成功实现:

df2 <- df1 %>% 
  group_by(clnt_label, term1, term2) %>% 
  filter(row_number() == 1)

测试数据:

df1 <- tibble(clnt_label = rep(LETTERS[1:3], each = 10),
              term1 = rev(rep(LETTERS[1:3], times = 10)),
              term2 = rep(LETTERS[1:3], each = 5, times = 2))

我在全量数据集上测试了多种实现方案,发现distinct()是目前最快的方式,测试结果如下:

各方案性能测试

  1. 原分组filter方案:
microbenchmark::microbenchmark(
all_pairs4_old <- all_pairs3 %>% 
  group_by(clnt_label, term1, term2) %>% 
  filter(row_number() == 1),
times = 1, unit = "millisecond")

单位:毫秒
min lq mean median neval
31938.37 31938.37 31938.37 31938.37 1

  1. slice_head方案:
microbenchmark::microbenchmark(
  all_pairs4_head <- all_pairs3 %>% 
    group_by(clnt_label, term1, term2) %>% 
    slice_head(n = 1),
  times = 1, unit = "millisecond")  

单位:毫秒
min lq mean median neval
214474.4 214474.4 214474.4 214474.4 1

  1. slice(1)方案:
microbenchmark::microbenchmark(
  all_pairs4_slice <- all_pairs3 %>% 
    group_by(clnt_label, term1, term2) %>% 
    slice(1),
  times = 1, unit = "millisecond") 

单位:毫秒
min lq mean median neval
144225.7 144225.7 144225.7 144225.7 1

  1. distinct方案:
microbenchmark::microbenchmark(
  all_pairs4_distinct <- all_pairs3 %>% 
    distinct(clnt_label, term1, term2, .keep_all = TRUE),
  times = 1, unit = "millisecond") 

单位:毫秒
min lq mean median neval
242.9775 242.9775 242.9775 242.9775 1


实现建议

  1. 保留distinct方案:你的测试已经证明distinct(clnt_label, term1, term2, .keep_all = TRUE)是当前最快的实现,它内部基于哈希表去重,比dplyr分组操作的效率高很多,尤其在大数据集上优势明显。如果业务逻辑允许(若严格要求保留每组首行,需确保数据集在分组键上的顺序与原逻辑一致),这是最优选择。

  2. 用vctrs实现的思路:如果一定要基于vctrs实现,可以结合vec_group_loc生成分组索引,再提取每组首行:

# 生成分组位置索引
groups <- vctrs::vec_group_loc(df1, c("clnt_label", "term1", "term2"))
# 提取每个组的首行位置
first_rows <- purrr::map_int(groups$loc, ~ .x[[1]])
# 获取结果
df2 <- df1[first_rows, ]

该方案性能接近distinct,vec_group_loc是vctrs的高效分组函数,避免了dplyr分组的额外开销。

  1. 备选优化方案:如果数据集已按分组键排序,data.table::unique(df1, by = c("clnt_label", "term1", "term2"))也是极快的选择,可作为额外测试项。

内容的提问来源于stack exchange,提问作者PhDavey

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 16:22:47