Reputation: 562
I am working with a list in R that looks like this
[1] 0 0 4 4 0 4 0
now suppose that I want to generate a list of numbers corresponding to this list like this
[[1]]
[1] 1 1
[[2]]
[1] 2 2
[[3]]
[1] 3 6
[[4]]
[1] 7 10
[[5]]
[1] 11 11
[[6]]
[1] 12 15
[[7]]
[1] 16 16
So in other words, the first list gives the groups of a list of a sequence of numbers from 1 to 16 and the second list gives the start and end of each group.
This is probably easier to see if you consider the sequence
1 2 3-6 7-10 11 12-15 16
Is there an easy way to do this? I think I could do it using some sort of global index and lapply but I wanted to see if there were an easier way.
Upvotes: 0
Views: 137
Reputation: 162451
Here's a slightly different approach:
x <- c(0,0,3,3,0,3,0)
f <- function(x) {
ee <- split(seq_len(sum(x+1)), rep.int(seq_along(x), x+1))
lapply(ee, range)
}
f(x)
Upvotes: 3
Reputation: 66819
Here's one way
# alternate input suggested by @MichaelChirico
d = c(0,0,3,3,0,3,0)
# my preferred approach
library(data.table) # version 1.9.5+
Map(c,
seq_along(d)+shift(cumsum(d), type="lag", fill=0),
seq_along(d)+cumsum(d)
)
A similar variation by @akrun:
# alternate input, starting from OP's
d2 = c(0, 0, 4, 4, 0, 4, 0)
d2 = replace( d2, !d2, 1)
# @akrun's answer
Map(c, cumsum(d2)-d2+1, cumsum(d2))
And some more:
# my original answer
start = c(1,head(cumsum(d+1)+1,-1))
Map(c, start, start + d)
# another way
s = sequence(d+1)
Map(c, seq_along(s)[s==1], seq_along(s)[c(diff(s) < 1, TRUE)] )
Upvotes: 4
Reputation: 24945
Here's a function that'll do it, no way near as elegant as @Frank's answer:
mygenerator <- function(vec){
counter <- 1
outlist <- list()
for(i in 1:length(vec)){
if(vec[i] == 0){
outlist[[i]] <- c(counter, counter)
counter <- counter + 1
} else {
outlist[[i]] <- c(counter, counter + vec[i] - 1)
counter <- counter + vec[i]
}
}
outlist
}
mygenerator(c(0, 0, 4, 4, 0, 4, 0))
[[1]]
[1] 1 1
[[2]]
[1] 2 2
[[3]]
[1] 3 6
[[4]]
[1] 7 10
[[5]]
[1] 11 11
[[6]]
[1] 12 15
[[7]]
[1] 16 16
Upvotes: 1