Group column by regular interval [duplicate]

Solution 1:

base R

df$id <- rep(letters, each = 5, length = nrow(df))

dplyr

library(dplyr)
df %>% 
  mutate(id = rep(letters, each = 5, length = n()))

output

df
   A id
1  1  a
2  2  a
3  3  a
4  4  a
5  1  a
6  2  b
7  3  b
8  2  b
9  2  b
10 2  b
11 2  c
12 1  c
13 1  c
14 1  c
15 1  c
16 6  d
17 7  d

Solution 2:

df <- data.frame(A = c(1, 2, 3, 4, 1, 2, 3, 2, 2, 2, 2, 1, 1, 1, 1, 6, 7))

library(dplyr)

df %>% 
  mutate(grp = (row_number() - 1) %/% 5)
#>    A grp
#> 1  1   0
#> 2  2   0
#> 3  3   0
#> 4  4   0
#> 5  1   0
#> 6  2   1
#> 7  3   1
#> 8  2   1
#> 9  2   1
#> 10 2   1
#> 11 2   2
#> 12 1   2
#> 13 1   2
#> 14 1   2
#> 15 1   2
#> 16 6   3
#> 17 7   3

Created on 2022-01-14 by the reprex package (v2.0.1)

or

df %>% 
  mutate(grp = letters[(row_number() - 1) %/% 5 + 1])

   A grp
1  1   a
2  2   a
3  3   a
4  4   a
5  1   a
6  2   b
7  3   b
8  2   b
9  2   b
10 2   b
11 2   c
12 1   c
13 1   c
14 1   c
15 1   c
16 6   d
17 7   d

Solution 3:

A data.table solution:

library(data.table)

setDT(df)[, grp := rep(letters, each = 5, length = .N)]

Output

    A grp
 1: 1   a
 2: 2   a
 3: 3   a
 4: 4   a
 5: 1   a
 6: 2   b
 7: 3   b
 8: 2   b
 9: 2   b
10: 2   b
11: 2   c
12: 1   c
13: 1   c
14: 1   c
15: 1   c
16: 6   d
17: 7   d

Benchmark

It looks like base R is the quickest for doing this:

enter image description here

bm <- microbenchmark::microbenchmark(
  andrew.data.table = setDT(df)[, grp := rep(letters, each = 5, length = .N)],
  yuriy = df %>%
    mutate(grp = letters[(row_number() - 1) %/% 5 + 1]),
  paul = df %>%
    mutate(id = rep(letters, each=5)[1:n()]),
  Maël_baseR = df$id <- rep(letters, each = 5, length = nrow(df)),
  Maël_dplyr = df %>%
    mutate(id = rep(letters, each = 5, length = n())),
  times=1000L
)

autoplot(bm)