Generate series 1, 2,1, 3,2,1, 4,3,2,1, 5,4,3,2,1
Asked Answered
D

4

23

I am trying to generate a vector containing decreasing sequences of increasing length, such as 1, 2,1, 3,2,1, 4,3,2,1, 5,4,3,2,1, i.e.

c(1, 2:1, 3:1, 4:1, 5:1)

I tried to use a loop for this, but I don't know how to stack or concatenate the results.

for (i in 1:11)
 {
 x = rev(seq(i:1))
 print(x) 
 }
[1] 1
[1] 2 1
[1] 3 2 1
[1] 4 3 2 1
[1] 5 4 3 2 1
[1] 6 5 4 3 2 1
[1] 7 6 5 4 3 2 1
[1] 8 7 6 5 4 3 2 1
[1] 9 8 7 6 5 4 3 2 1
[1] 10  9  8  7  6  5  4  3  2  1
[1] 11 10  9  8  7  6  5  4  3  2  1

I have also been experimenting with the rep, rev and seq, which are my favourite option but did not get far.

Diaphaneity answered 28/3, 2017 at 8:29 Comment(3)
This should definitely be on Code Golf!Descombes
@RaisingAgent This is on code-golf already.Bannerman
@RaisingAgent You might not want to use the solutions given there in a production environment tho...Postman
G
24

With sequence:

rev(sequence(5:1))
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

From R 4.0.0 sequence takes arguments from and by:

sequence(1:5, from = 1:5, by = -1)
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

Far from the golf minimalism of rev... However, if you wake up one morning and want to create such a sequence with n = 1000 (like in the answer below), the latter is in fact faster (but I can hear Brian Ripley in fortunes::fortune(98))

n = 1000

microbenchmark(
  f_rev = rev(sequence(n:1)),
  f_seq4.0.0 = sequence(1:n, from = 1:n, by = -1))
# Unit: microseconds
#        expr   min     lq     mean  median     uq    max neval
#       f_rev 993.7 1040.3 1128.391 1076.95 1133.3 1904.7   100
#  f_seq4.0.0 136.4  141.5  153.778  148.25  150.1  304.7   100
Grapeshot answered 28/3, 2017 at 8:47 Comment(0)
C
8

We can do this with lapply

unlist(lapply(1:11, function(x) rev(seq(x))))

Or as @zx8754 mentioned in the comments, in place of rev(seq, : can be used

unlist(lapply(1:11, function(x) x:1))

Or as @BrodieG suggested, we can make this more compact by removing the anonymous function call

unlist(lapply(1:11, ":", 1))
Chong answered 28/3, 2017 at 8:32 Comment(1)
unlist(lapply(1:11, ":", 1))Mapp
S
8

And for fun, using matrices (and ignoring the warning ;) )

m <- matrix(c(1:5,0), ncol = 5, nrow = 5, byrow = T)
m[ upper.tri(m, diag = T) ]
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

And we can simplify the upper.tri into its component parts

m[ row(m) <= col(m)]
# [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

And if you can handle even more fun, then how about some benchmarking:

library(microbenchmark)
maxValue <- 1000

vec2 <- maxValue:1
m2 <- matrix(c(1:maxValue,0), ncol = maxValue, nrow = maxValue, byrow = T)

microbenchmark(
  
  henrik = {
    rev(sequence(maxValue:1))
  },
  henrik_4.0.0 = {
    sequence(1:maxValue, from = 1:maxValue, by = -1)
  },
  akrun = {
    unlist(lapply(1:maxValue, function(x) x:1))
  },
  symbolix1 = {
    m <- matrix(c(1:maxValue,0), ncol = maxValue, nrow = maxValue, byrow = T)
    m[ row(m) <= col(m) ]
  },
  symbolix2 = {
    m2[ row(m2) <= col(m2) ]
  },
  lmo1 = {
    unlist(lapply(1:maxValue, tail, x=maxValue:1))
  },
  lmo2 = {
    vec <- maxValue:1
    unlist(lapply(rev(vec), tail, x=vec))
  },
  lmo3 = {
    unlist(lapply(rev(vec2), tail, x=vec2))
  }
)
# Unit: microseconds
#         expr    min      lq      mean  median      uq      max neval
#       henrik 1018.7 1068.20  1176.430 1103.65 1223.20   2348.4   100
# henrik_4.0.0  139.9  147.90   166.092  151.40  162.70    379.0   100
#        akrun 3420.1 3637.75  3825.336 3729.10 3897.00   4960.6   100
#    symbolix1 6999.5 7483.20  7807.747 7618.30 7810.70  12138.7   100
#    symbolix2 4791.2 5043.00  5677.742 5190.50 5393.65  29318.7   100
#         lmo1 7530.1 7967.05 10918.201 8161.10 8566.45 132324.1   100
#         lmo2 7385.7 8017.95 12271.158 8213.90 8500.70 143798.2   100
#         lmo3 7539.5 7959.05 14355.810 8177.85 8500.85 131154.2   100

In this example, henrik_4.0.0 is the winner! (for bm with pre-R 4.0.0 sequence only, see previous edits)


But I know what you're thinking, 'why end all the fun there!'

Well, lets write our own C++ function and see how that performs

library(Rcpp)

cppFunction('NumericVector reverseSequence(int maxValue, int vectorLength){
                        
                        NumericVector out(vectorLength);
                        int counter = 0;
                        
                        for(int i = 1; i <= maxValue; i++){
                            for(int j = i; j > 0; j--){
                                out[counter] = j;
                                counter++;
                            }
                        }
                        
                        return out;
                        }')

maxValue <- 5
reverseSequence(maxValue, sum(1:maxValue))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

library(microbenchmark)
maxValue <- 1000

microbenchmark(
    akrun = {
        unlist(sapply(1:maxValue, function(x) x:1))
    },
    symbolix3 = {
        reverseSequence(maxValue, sum(1:maxValue))
    }
)

# Unit: microseconds
#        expr      min        lq     mean    median       uq       max neval
#      akrun 1522.250 1631.6030 3148.922 1829.9370 3357.493 45576.148   100
#  symbolix3  338.626  495.3825 1293.720  950.6635 2169.656  3816.091   100
Sassoon answered 28/3, 2017 at 9:18 Comment(0)
D
5

Another alternative is to use tail within lapply, to successively select the number of elements to keep from the initial vector:

unlist(lapply(1:5, tail, x=5:1))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1

Or, it may be faster to construct the base vector first and then call on it:

vec <- 5:1
unlist(lapply(rev(vec), tail, x=vec))
 [1] 1 2 1 3 2 1 4 3 2 1 5 4 3 2 1
Diacritical answered 28/3, 2017 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.