7

I have a sequence of 'endpoints', e.g.:

c(7,10,5,11,15)     

that I want to expand to a sequence of 'elapsed time' between the endpoints, e.g.

c(7,1,2,3,4,5,6,7,8,9,10,1,2,3,4,5,1,2,3,4,5,6,7,8,9,10,11,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)

Whats the most efficient way to do this in R? I'm imagining some creative use of the embed function, but I can't quite get there without using a ugly for loop.

Here's the naive way to do this:

expandSequence <- function(x) {
    out <- x[1]
    for (y in (x[-1])) {
        out <- c(out,seq(1,y))
    }
    return(out)
}

expandSequence(c(7,10,5,11,15))
Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453
Zach
  • 29,791
  • 35
  • 142
  • 201

3 Answers3

8

There is a base function to do this, called, wait for it, sequence:

sequence(c(7,10,5,11,15))

 [1]  1  2  3  4  5  6  7  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  1  2  3
[26]  4  5  6  7  8  9 10 11  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

In your case it seems your first endpoint is in fact not part of the sequence, so it becomes:

c(7, sequence(c(10,5,11,15)))
 [1]  7  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  1  2  3  4  5  6  7  8  9
[26] 10 11  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
Andrie
  • 176,377
  • 47
  • 447
  • 496
  • 1
    +1 Nice catch. It is implemented effectively as @gsk and I reinvented. I'd come across `sequence` before but presumed it was an alias for `seq`. Great to find out about these less well-used functions! – Gavin Simpson Aug 03 '11 at 22:41
  • 3
    @Andrie: Thanks! For my next trick, I will reverse engineer the `embed` function. – Zach Aug 03 '11 at 22:56
4

How about this:

> unlist(sapply(x,seq))
 [1]  1  2  3  4  5  6  7  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  1  2
[25]  3  4  5  6  7  8  9 10 11  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15

With the first element added on at the end:

c( x[1], unlist( sapply( x[seq(2,length(x))], seq ) ) )

And a slightly more readable version:

library(taRifx)
c( x[1], unlist( sapply( shift(x,wrap=FALSE), seq ) ) )
Ari B. Friedman
  • 71,271
  • 35
  • 175
  • 235
  • 1
    Sadly my solution doesn't involve bolded numbers. ;-) – Ari B. Friedman Aug 03 '11 at 22:30
  • Perfect! I just got to `do.call(c,mapply(seq,1,x))` but your solution is better. – Zach Aug 03 '11 at 22:33
  • Except that isn't the output shown in the Q. The first element should be `7` according to the Q. You have `1:7`. – Gavin Simpson Aug 03 '11 at 22:35
  • So to be complete about this you'd need to omit the first element of `x` from the `sapply` call and then add it back on to the front afterwards, to get the OP's desired output, right? – joran Aug 03 '11 at 22:36
  • If that *is* what the OP wants. He has commented here that the output is "perfect", so perhaps the Q is not exactly what was wanted? – Gavin Simpson Aug 03 '11 at 22:42
  • @Gavin Simpson I added the "omit first element of x and add it back at the end" myself – Zach Aug 03 '11 at 22:54
3

A combination of lapply() and seq_len() is useful here:

expandSequence <- function(x) {
    out <- lapply(x[-1], seq_len)
    do.call(c, c(x[1], out))
}

Which gives for

pts <- c(7,10,5,11,15)

> expandSequence(pts)
 [1]  7  1  2  3  4  5  6  7  8  9 10  1  2  3  4  5  1  2  3  4
[21]  5  6  7  8  9 10 11  1  2  3  4  5  6  7  8  9 10 11 12 13
[41] 14 15

(An alternative is:

expandSequence <- function(x) {
    out <- lapply(x[-1], seq_len)
    unlist(c(x[1], out), use.names = FALSE)
}

)

Gavin Simpson
  • 170,508
  • 25
  • 396
  • 453