9

I would like to reorder some columns to come after a particular other column using dplyr::relocate. Here is a MWE:

a <- letters[1:3]
b <- letters[4:6]
c <- letters[7:9]
d <- letters[10:12]

mytib <- tibble::tibble(a,b,c,d)

#  A tibble: 3 x 4
#  a     b     c     d    
#  <chr> <chr> <chr> <chr>
# 1 a     d     g     j    
# 2 b     e     h     k    
# 3 c     f     i     l    

mytib %>%
     relocate(c, .after = a)

This example works but is there a way that I could, with one relocate command, move c after a and, say, d after b?

I tried the following without success:

mytib %>%
     relocate(c(c, d), .after(c(a, b)))

Edit 1: I explicitly ask about relocate because functions like select do not work for large datasets where all I know is after which column (name) I want to insert a column.

Edit 2: This is my expected output:

#  A tibble: 3 x 4
#  a     c     b     d    
#  <chr> <chr> <chr> <chr>
# 1 a     g     d     j    
# 2 b     h     e     k    
# 3 c     i     f     l   
Tea Tree
  • 882
  • 11
  • 26
  • 1
    Typo? `.after = c(a, b)` – arg0naut91 Sep 02 '20 at 20:49
  • I don't think that it could be used that way. For this task, I think that you should use `select()`: `mytib %>% select(a, c, b, d)`. – tmfmnk Sep 02 '20 at 20:54
  • 2
    Using OP's vectors, this works for me `tibble(c, d, a, b) %>% relocate(c(c, d), .after = c(a, b))` – arg0naut91 Sep 02 '20 at 21:00
  • 1
    @arg0naut91 I think it does not work: a should be after c, while d should be after b. Here it is a, b, c, d. – tmfmnk Sep 02 '20 at 21:02
  • @tmfmnk, ah OK I see - there was no expected output so I've just noticed the typo. Indeed I agree that `relocate` may not be of help here. – arg0naut91 Sep 02 '20 at 21:05
  • I fixed the typo. ```mytib %>% select(a,c,b,d)``` only works for small datasets. Is there a scalable alternative that a) does not rely on numeric column indices and b) does not require me to type out the order of all columns? – Tea Tree Sep 02 '20 at 21:08
  • I think I found a halfway elegant solution using `purrr::reduce` - posted it as an answer below – alex_jwb90 Sep 15 '20 at 21:49

2 Answers2

5

As dplyr::relocate itself apparently doesn't allow relocating in pairs, you can "hack" this behavior by preparing a list of column pairs like the ones you describe ("c after a" & "d after b") and reduce over that list, passing your df in as an .init value and in each reduce-step relocating one pair.

Like this:

library(dplyr)
library(purrr)

df_relocated <- reduce(
  .x = list(c('c','a'), c('d','b')), 
  .f = ~ relocate(.x, .y[1], .after = .y[2]),
  .init = mytib
)

This produces a tibble just as you expect it:

> df_relocated
# A tibble: 3 x 4
  a     c     b     d    
  <chr> <chr> <chr> <chr>
1 a     g     d     j    
2 b     h     e     k    
3 c     i     f     l
alex_jwb90
  • 1,663
  • 1
  • 11
  • 20
1

In case you want to work with two lists, where element 1 of list 2 should relocated after element 1 of list 1 and so forth, this would be a solution:

 reduce2(
  .x = c("a", "b"),
  .y = c("c", "d"),
  .f = ~ relocate(..1, ..3, .after = ..2),
  .init = mytib
  )
mirirai
  • 1,365
  • 9
  • 25
  • With `dplyr version 1.0.7` I got an error; `Can't subset columns that don't exist. x Column \`..2\` doesn't exist.` Putting `..2` and `..3` in curly braces helped: `.f = ~ relocate(..1, {..3}, .after = {..2}),`. – mirirai Mar 30 '22 at 18:23