5

So, if the input is a vector such as

v <- (1, 2, 3, 7, 2, 8, 9) 

The output would be

(3, 2, 1, 7, 2, 9, 8)

I have tried using nested for and if loops with the condition as an is.sorted function, but have not had any success.

989
  • 12,579
  • 5
  • 31
  • 53
tdm
  • 131
  • 5
  • 1
    See also: [How to partition a vector into groups of regular, consecutive sequences?](http://stackoverflow.com/questions/5222061/how-to-partition-a-vector-into-groups-of-regular-consecutive-sequences) – Henrik Mar 27 '17 at 21:22
  • ...which I believe is a nice, canonical post for the `cumsum(...diff(...` idiom. – Henrik Mar 28 '17 at 11:24

2 Answers2

8

With the sample data

x <- c(1, 2, 3, 7, 2, 8, 9)

You can partition into "sequential groups" with

grp <- cumsum(!c(TRUE, diff(x)==1))
grp
# [1] 0 0 0 1 2 3 3

Basically we look at the diff from the previous element and track changes anytime that value isn't equal to 1.

You can use this group information to re-order those values with ave (this does a within-group transformation).

revsort<-function(...) sort(...,decreasing=TRUE)
ave(x, grp, FUN=revsort)
# [1] 3 2 1 7 2 9 8
MrFlick
  • 195,160
  • 17
  • 277
  • 295
5

We could also do:

x <- c(0, which(diff(vec) != 1), length(vec))
unlist(sapply(seq(length(x) - 1),  function(i) rev(vec[(x[i]+1):x[i+1]])))

#[1] 3 2 1 7 2 9 8

The idea is to first cut the vector based on the positions of consecutive numbers. We then traverse these cuts and apply rev.


Data

vec <- c(1, 2, 3, 7, 2, 8, 9)

Benchmarking

library(microbenchmark)
vec1 <- c(1, 2, 3, 7, 2, 8, 9)
vec2 <- c(1:1000, sample.int(100, 10), 5:500, sample.int(100, 10), 100:125)

f_MrFlick <- function(x){
    revsort<-function(...) sort(...,decreasing=TRUE)
    grp <- cumsum(!c(TRUE, diff(x)==1))
    ave(x, grp, FUN=revsort)
}

f_989 <- function(vec){
    x <- c(0, which(diff(vec) != 1), length(vec))
    unlist(sapply(seq(length(x) - 1),  function(i) rev(vec[(x[i]+1):x[i+1]])))
}

all(f_MrFlick(vec1)==f_989(vec1))
# [1] TRUE
all(f_MrFlick(vec2)==f_989(vec2))
# [1] TRUE

length(vec1)
# [1] 7
microbenchmark(f_MrFlick(vec1), f_989(vec1))
# Unit: microseconds
            # expr     min       lq     mean  median       uq     max neval
 # f_MrFlick(vec1) 287.006 297.0585 305.6340 302.833 312.6695 479.912   100
     # f_989(vec1) 113.348 119.7645 124.7901 121.903 127.0360 268.186   100
# --------------------------------------------------------------------------
length(vec2)
# [1] 1542 
microbenchmark(f_MrFlick(vec2), f_989(vec2))
# Unit: microseconds
            # expr      min        lq      mean   median       uq      max neval
 # f_MrFlick(vec2) 1532.553 1565.2745 1725.7540 1627.937 1698.084 3914.149   100
     # f_989(vec2)  426.874  454.6765  546.9115  466.439  489.322 2634.383   100
989
  • 12,579
  • 5
  • 31
  • 53