Get start and end ranges from a sequence containing gaps

Viewed 55

Suppose I have the following data:

c(1:4,7:15,20:23,25,30:35,40,41)

I want to get the start and end ranges from that vector. Yet, I want to skip single values, that have no natural predecessor and successor (in the example: 25). In other words, there should be at least two consecutive values to make a "range".

The start ranges in this example are: 1, 7, 20, 30, 40 Their corresponding end ranges are: 4, 15, 23, 35, 41

Is there any elegant built-in solution? I am pretty much stuck in a for loop hell.

2 Answers

Identify locations where diff(x) is > 1, lagged appropriately by adding TRUE to the beginning or end of the sequence. (diff(x) produces a vector that is one element shorter than x.) The & clauses get rid of the singleton values.

dx1 <- c(TRUE,diff(x)>1)
dx2 <- c(diff(x)>1, TRUE)
start <- x[dx1 & !dx2]
end <-   x[dx2 & !dx1]

There is probably also a solution with something like rle(diff(x)==1), but it would be a little more complicated than the solution above (it might be useful if e.g. you wanted to identify runs of a particular length).

Doesn't add much to the solution from @Ben Bolker, but another way could be:

ind <- aggregate(x ~ cumsum(c(FALSE, diff(x)) > 1), FUN = function(x) (length(x) > 1) * range(x))
ind$x[ind$x[, 1] != 0, ]

     [,1] [,2]
[1,]    1    4
[2,]    7   15
[3,]   20   23
[4,]   30   35
[5,]   40   41
Related