Tea Tree
Tea Tree

Reputation: 984

How to relocate several columns in one step using dplyr::relocate?

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   

Upvotes: 12

Views: 10984

Answers (3)

petzi
petzi

Reputation: 1636

With dplyr 1.0.0 the following code is possible:

    mytib %>% 
        dplyr::relocate(a,c,b,d)

As there are few columns, I have used the "most common need", namely "to move variables to the front, the default behaviour" of relocate() But I think that this approach is also valuable if you are going to restructure a more complex dataset. It is easier than working with individual columns and using .before resp. .after.

Column groups with shared features can also be moved by applying the where() function or other selection helpers from the tidyselect package to a specific position with .before or .after.

Upvotes: 0

mirirai
mirirai

Reputation: 1409

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
  )

Upvotes: 1

alex_jwb90
alex_jwb90

Reputation: 1713

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

Upvotes: 6

Related Questions