2

I am curious if there is a functional way to break up a vector of contiguous groups in R.

For example if I have the following

# start with this
vec <- c(1,2,3,4,6,7,8,30,31,32)

# end with this
vec_vec <- list(c(1, 2, 3, 4),c(6,7,8),c(30,31,32))
print(vec_vec[1])

I can only imaging this is possible with a for loop where I calculate differences of previous values, but maybe there are better ways to solve this.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
Alex
  • 2,603
  • 4
  • 40
  • 73

2 Answers2

7
split(vec, vec - seq_along(vec)) |> unname()
# [[1]]
# [1] 1 2 3 4
# 
# [[2]]
# [1] 6 7 8
# 
# [[3]]
# [1] 30 31 32

Subtratcing seq_along(vec) turns contiguous sequences into single values, which we can split by.

Gregor Thomas
  • 136,190
  • 20
  • 167
  • 294
  • 1
    Clever, I was going to try diff but this is more elegant and efficient – Ottie Aug 10 '22 at 15:35
  • Thanks @Ottie, I've given more-or-less this answer before, but I couldn't find it to mark it as a duplicate. While I do like this version and find it elegant, G. Grothendieck's suggestions are probably both more efficient :) – Gregor Thomas Aug 10 '22 at 15:37
5

1) The collapse package has seqid for this:

library(collapse)
split(vec, seqid(vec))

giving:

$`1`
[1] 1 2 3 4

$`2`
[1] 6 7 8

$`3`
[1] 30 31 32

2) A base approach giving the same answer is

split(vec, cumsum(c(TRUE, diff(vec) != 1)))

3) In the comments seqle in cgwtools is suggested.

library(cgwtools)
lens <- seqle(vec)$lengths
split(vec, rep(seq_along(lens), lens))
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341