The seq
function in R would give me a sequence from x
to y
with a constant step m
:
seq(x, y, m)
E.g. seq(1,9,2) = c(1,3,5,7,9)
.
What would be the most elegant way to get a sequence from x
to y
with alternating steps m1
and m2
, such that something like "seq(x, y, c(m1, m2))"
would give me c(x, x + m1, (x + m1) + m2, (x + m1 + m2) + m1, ..., y)
, each time adding one of the steps (not necessarily reaching up to y
, of course, as in seq
)?
Example: x = 1; y = 19; m1 = 2; m2 = 4
and I get c(1,3,7,9,13,15,19)
.
You could use Reduce
with accumulate = TRUE
to iteratively add either 2 or 4:
Reduce(`+`, rep(c(2,4), 10), init = 1, accumulate = TRUE)
# [1] 1 3 7 9 13 15 19 21 25 27 31 33 37 39 43 45 49 51 55 57 61
The number of times you repeat c(2,4)
will determine sequence length; since it is 10 above, the sequence is length 20.
The purrr
package has an accumulate
wrapper, if you prefer the syntax:
purrr::accumulate(rep(c(2,4), 10), `+`, .init = 1)
## [1] 1 3 7 9 13 15 19 21 25 27 31 33 37 39 43 45 49 51 55 57 61
Here is an alternative that uses diffinv
This method over allocates the values, so as a stopping rule, I get the elements that are less than or equal to the stopping value.
seqAlt <- function(start, stop, by1, by2) {
out <- diffinv(rep(c(by1, by2), ceiling(stop / (by1 + by2))), xi=start)
return(out[out <= stop])
}
seqAlt(1, 19, 2, 4)
[1] 1 3 7 9 13 15 19
I arrived the solution by:
1. Use cumsum
with a vector c(from,rep(by,times),...)
, with by
repeated times = ceiling((to-from)/sum(by))
times.
2. Truncate the sequence by !(seq > to)
.
seq_alt <- function(from, to, by) {
seq <- cumsum(c(from,rep(by,ceiling((to-from)/sum(by)))))
return(seq[! seq > to])
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With