Get a seq() in R with alternating steps
Asked Answered
T

6

10

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).

Toothpick answered 11/1, 2017 at 9:0 Comment(2)
In the Example, where is the cutting point where 2 changes to 4 to make it more dynamicBaneberry
@Baneberry what do you mean by cutting point? The result is c(1, 1+2, 3+4, 7+2, 9+4, 13+2, 15+4). Alternating the step between 2 and 4.Toothpick
A
6

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])
 }
Apartment answered 11/1, 2017 at 9:29 Comment(1)
I suggest to repeat only times = ceiling((to-from)/sum(by)) otherwise your temporary sequency may grow unncessarily large (e.g. by = c(0.0001,1))Prognathous
N
3

First n terms of this sequence you can generate with

x = 1; m1 = 2; m2 = 4

n <- 0:10 # first 11 terms
x + ceiling(n/2)*m1 + ceiling((n-1)/2)*m2
# [1] 1  3  7  9 13 15 19 21 25 27 31
Nether answered 11/1, 2017 at 9:12 Comment(0)
C
3

Here is another idea,

fun1 <- function(x, y, j, z){
if(j >= y) {return(x)}else{
  s1 <- seq(x, y, j+z)
  s2 <- seq(x+j, y, j+z)
  return(sort(c(s1, s2)))
  }
}

fun1(1, 19, 2, 4)
#[1]  1  3  7  9 13 15 19

fun1(1, 40, 4, 3)
#[1]  1  5  8 12 15 19 22 26 29 33 36 40

fun1(3, 56, 7, 10)
#[1]  3 10 20 27 37 44 54

fun1(1, 2, 2, 4)
#[1] 1
Chronicles answered 11/1, 2017 at 9:30 Comment(3)
fun1(1,2,2,4) chucks a slightly cryptic error, and this doesn't generalise very well for more m.Larva
seq(1,2,4) returns 1. OP might need something like seq as he mentioned in the question.Apartment
ok @Apartment edited to account for that as well, though I doubt it will ever be the case for the OPChronicles
O
2

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
Opt answered 11/1, 2017 at 12:52 Comment(0)
H
2

perfect example of recycling vectors in R

# 1. 
x = 1; y = 19; m1 = 2; m2 = 4
(x:y)[c(TRUE, rep(FALSE, m1-1), TRUE, rep(FALSE,m2-1))]
# [1]  1  3  7  9 13 15 19
# 2.
x = 3; y = 56; m1 = 7; m2 = 10
(x:y)[c(TRUE, rep(FALSE, m1-1), TRUE, rep(FALSE,m2-1))]
# [1]  3 10 20 27 37 44 54
Holzman answered 25/1, 2017 at 4:42 Comment(0)
S
2

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
Spathe answered 25/1, 2017 at 4:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.