Selecting observations within a data frame and reversing their order

Using data.table:

library(data.table)
setDT(dat)
ids.to.reverse <- c('b', 'e', 'g')

dat[, if(ID %in% ids.to.reverse) .SD[.N:1] else .SD, by='ID']

One option is to group_split by ID and do the arrange by looping over the list with map based on whether any of the values 'b', 'e', 'g' are %n% the 'ID'

library(dplyr)
library(purrr)
out <- dat %>% 
        group_split(ID) %>%
        map_dfr(~ if(any(c('b', 'e', 'g') %in% first(.x$ID)))
         .x %>%
             arrange(desc(time)) else .x)   

out %>% 
   filter(ID %in% c('a', 'b'))
# A tibble: 20 x 3
#   ID     time    var1
#   <fct> <int>   <dbl>
# 1 a         1 -0.560 
# 2 a         2 -0.230 
# 3 a         3  1.56  
# 4 a         4  0.0705
# 5 a         5  0.129 
# 6 a         6  1.72  
# 7 a         7  0.461 
# 8 a         8 -1.27  
# 9 a         9 -0.687 
#10 a        10 -0.446 
#11 b        10 -0.473 
#12 b         9  0.701 
#13 b         8 -1.97  
#14 b         7  0.498 
#15 b         6  1.79  
#16 b         5 -0.556 
#17 b         4  0.111 
#18 b         3  0.401 
#19 b         2  0.360 
#20 b         1  1.22  

Or we can make use of arrange in a hacky way i.e. change the time to negative based on the ID 'b', 'e', 'g' while the rest is positive

out1 <- dat %>%
     arrange(ID,  time * c(1, -1)[c(1 + (ID %in% c('b', 'e', 'g')))])

-checking

all.equal(out, out1, check.attributes = FALSE)
#[1] TRUE

Here is an approach with base R using split, order and rev:

rev.ids <- c("b", "e", "g")
split <- split(dat, dat$ID)
dat <- do.call(rbind,lapply(split,function(x){
  if(x[1,1] %in% rev.ids)
    x[order(rev(x$time)),] 
  else 
    x
  }))
dat
    ID time         var1
1    a    1 -0.560475647
2    a    2 -0.230177489
...
8    a    8 -1.265061235
9    a    9 -0.686852852
10   a   10 -0.445661970
11   b   10 -0.472791408
12   b    9  0.701355902
...
18   b    3  0.400771451
19   b    2  0.359813827
20   b    1  1.224081797
21   c    1 -1.067823706

Edit

I think this data.table approach will be faster:

library(data.table)
rev.ids <- c("b", "e", "g")
setDT(dat)[,.SD[order(time,decreasing = (unlist(.BY) %in% rev.ids))],by = ID]
    ID time         var1
  1:  a    1 -0.560475647
  2:  a    2 -0.230177489
...
  8:  a    8 -1.265061235
  9:  a    9 -0.686852852
 10:  a   10 -0.445661970
 11:  b   10 -0.472791408
 12:  b    9  0.701355902
...
 19:  b    2  0.359813827
 20:  b    1  1.224081797
 21:  c    1 -1.067823706
 22:  c    2 -0.217974915

Tags:

R

Dataframe