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

如何用tidyverse为数据框同类别连续行块生成分组编号

问题描述

现有如下数据集:

set.seed(10) 
df <- data.frame(row=c(1:15), cat = c(rep("a", 3), rep("b", 3), rep("c", 3), rep("b", 3), rep("a", 3)), val = runif(15)) 
df

输出结果:

row cat        val
1    1   a 0.53559704
2    2   a 0.09308813
3    3   a 0.16980304
4    4   b 0.89983245
5    5   b 0.42263761
6    6   b 0.74774647
7    7   c 0.82265258
8    8   c 0.95465365
9    9   c 0.68544451
10  10   b 0.50050323
11  11   b 0.27548386
12  12   b 0.22890394
13  13   a 0.01443391
14  14   a 0.72896456
15  15   a 0.24988047

可以看到cat列存在连续的同类别行块,例如类别a出现在1-3行和13-15行。需要新增cat2列,按每个类别出现的行块顺序编号,格式如a1、a2,b1、b2,预期输出:

row cat        val  cat2
1    1   a 0.53559704    a1
2    2   a 0.09308813    a1
3    3   a 0.16980304    a1
4    4   b 0.89983245    b1
5    5   b 0.42263761    b1
6    6   b 0.74774647    b1
7    7   c 0.82265258    c1
8    8   c 0.95465365    c1
9    9   c 0.68544451    c1
10  10   b 0.50050323    b2
11  11   b 0.27548386    b2
12  12   b 0.22890394    b2
13  13   a 0.01443391    a2
14  14   a 0.72896456    a2
15  15   a 0.24988047    a2

仅使用dplyr::group_by(cat)无法实现该需求。尝试了以下方法:

df %>%
  mutate(run = with(rle(cat), rep(seq_along(lengths), lengths))) %>%
  group_by(cat, run) %>%
  mutate(cat2 = paste0(cat, run)) %>%
  ungroup() %>%
  select(-run)

但得到的是全局递增的编号,不符合预期:

row cat      val cat2 
   <int> <chr>  <dbl> <chr>
 1     1 a     0.536  a1   
 2     2 a     0.0931 a1   
 3     3 a     0.170  a1   
 4     4 b     0.900  b2   
 5     5 b     0.423  b2   
 6     6 b     0.748  b2   
 7     7 c     0.823  c3   
 8     8 c     0.955  c3   
 9     9 c     0.685  c3   
10    10 b     0.501  b4   
11    11 b     0.275  b4   
12    12 b     0.229  b4   
13    13 a     0.0144 a5   
14    14 a     0.729  a5   
15    15 a     0.250  a5  

请问如何使用tidyverse实现需求的分组编号?


解决方案

核心思路是先识别每个连续的行块,再针对每个cat类别,对其出现的行块进行独立编号。具体实现代码如下:

library(tidyverse)

set.seed(10) 
df <- data.frame(row=c(1:15), cat = c(rep("a", 3), rep("b", 3), rep("c", 3), rep("b", 3), rep("a", 3)), val = runif(15)) 

df %>%
  # 生成每个连续行块的全局唯一标识
  mutate(block_id = with(rle(cat), rep(seq_along(lengths), lengths))) %>%
  # 按cat分组,对每个类别下的行块进行顺序编号
  group_by(cat) %>%
  mutate(block_num = dense_rank(block_id)) %>%
  # 拼接得到目标格式的cat2列
  mutate(cat2 = paste0(cat, block_num)) %>%
  ungroup() %>%
  # 移除中间辅助列
  select(-block_id, -block_num)

运行后得到的结果完全符合预期:

row cat        val cat2
1    1   a 0.53559704   a1
2    2   a 0.09308813   a1
3    3   a 0.16980304   a1
4    4   b 0.89983245   b1
5    5   b 0.42263761   b1
6    6   b 0.74774647   b1
7    7   c 0.82265258   c1
8    8   c 0.95465365   c1
9    9   c 0.68544451   c1
10  10   b 0.50050323   b2
11  11   b 0.27548386   b2
12  12   b 0.22890394   b2
13  13   a 0.01443391   a2
14  14   a 0.72896456   a2
15  15   a 0.24988047   a2

代码解释

  • block_id:通过rle()函数识别连续的同类别行块,为每个行块分配全局唯一的编号;
  • block_num:按cat分组后,用dense_rank()对block_id重新排序,确保每个类别下的行块从1开始递增编号;
  • cat2:将cat类别与对应的行块编号拼接,得到需求的格式。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 23:30:42