How to sort a tibble by column order? (first column, second column, third...)

Viewed 834

I have a tibble that contains an arbitrary number of columns. These columns were inserted in order so their index (first, second, ...) is meaningful.

I'm trying to sort the tibble by the first column, then the second column, then the third, and so on.

I'd rather keep using dplyr::arrange() to be consistent with my framework, but if it cannot be done I'd gladly accept any solution.

Also, if missing values could be considered last that would be a great plus.

Here is a reproducible example with my expected output and some failed attempts:

library(tidyverse)
set.seed(0)
x=as_tibble(mtcars)[1:4] %>% sample_n(5)
x
#> # A tibble: 5 x 4
#>     mpg   cyl  disp    hp
#>   <dbl> <dbl> <dbl> <dbl>
#> 1  15.2     8  276.   180
#> 2  19.2     8  400    175
#> 3  21.4     6  258    110
#> 4  14.3     8  360    245
#> 5  21       6  160    110

# **** EXPECTED OUTPUTS: ****

arrange(x, mpg, cyl, disp, hp)
#> # A tibble: 5 x 4
#>     mpg   cyl  disp    hp
#>   <dbl> <dbl> <dbl> <dbl>
#> 1  14.3     8  360    245
#> 2  15.2     8  276.   180
#> 3  19.2     8  400    175
#> 4  21       6  160    110
#> 5  21.4     6  258    110
arrange(x, hp, disp, cyl, mpg)
#> # A tibble: 5 x 4
#>     mpg   cyl  disp    hp
#>   <dbl> <dbl> <dbl> <dbl>
#> 1  21       6  160    110
#> 2  21.4     6  258    110
#> 3  19.2     8  400    175
#> 4  15.2     8  276.   180
#> 5  14.3     8  360    245

# **** MY FAILED ATTEMPTS: ****

arrange(x, all_of(colnames(x)))
#> Error: arrange() failed at implicit mutate() step. 
#> * Problem with `mutate()` column `..1`.
#> i `..1 = all_of(colnames(x))`.
#> i `..1` must be size 5 or 1, not 4.
arrange(x, !!all_of(colnames(x)))
#> Error: arrange() failed at implicit mutate() step. 
#> * Problem with `mutate()` column `..1`.
#> i `..1 = <chr>`.
#> i `..1` must be size 5 or 1, not 4.
arrange(x, !!!all_of(names(x)))
#> # A tibble: 5 x 4
#>     mpg   cyl  disp    hp
#>   <dbl> <dbl> <dbl> <dbl>
#> 1  15.2     8  276.   180
#> 2  19.2     8  400    175
#> 3  21.4     6  258    110
#> 4  14.3     8  360    245
#> 5  21       6  160    110
do.call(arrange, x, colnames(x))
#> Warning in if (quote) args <- lapply(args, enquote): the condition has length >
#> 1 and only the first element will be used
#> Error in if (quote) args <- lapply(args, enquote): argument is not interpretable as logical
do.call(arrange, x, list(colnames(x)))
#> Error in if (quote) args <- lapply(args, enquote): argument is not interpretable as logical

Created on 2021-08-10 by the reprex package (v2.0.0)

3 Answers

Since arrange_all has been deprecated, you can use across in arrange.

library(dplyr)
x %>% arrange(across())

# A tibble: 5 x 4
#    mpg   cyl  disp    hp
#  <dbl> <dbl> <dbl> <dbl>
#1  14.3     8  360    245
#2  15.2     8  276.   180
#3  19.2     8  400    175
#4  21       6  160    110
#5  21.4     6  258    110

For reverse you can do -

x %>% arrange(across(.cols = ncol(.):1))

# A tibble: 5 x 4
#    mpg   cyl  disp    hp
#  <dbl> <dbl> <dbl> <dbl>
#1  21       6  160    110
#2  21.4     6  258    110
#3  19.2     8  400    175
#4  15.2     8  276.   180
#5  14.3     8  360    245

In base R, you can use do.call with order -

#1.
x[do.call(order, x), ]

#2.
x[do.call(order, rev(x)), ]
# arrange by column left to right:
x %>% arrange(!!!syms(colnames(.)))

# arrange by column right to left:
x %>% arrange(!!!syms(rev(colnames(.))))

Output:

> x %>% arrange(!!!syms(colnames(.)))
# A tibble: 5 x 4
    mpg   cyl  disp    hp
  <dbl> <dbl> <dbl> <dbl>
1  14.3     8  360    245
2  15.2     8  276.   180
3  19.2     8  400    175
4  21       6  160    110
5  21.4     6  258    110
> x %>% arrange(!!!syms(rev(colnames(.))))
# A tibble: 5 x 4
    mpg   cyl  disp    hp
  <dbl> <dbl> <dbl> <dbl>
1  21       6  160    110
2  21.4     6  258    110
3  19.2     8  400    175
4  15.2     8  276.   180
5  14.3     8  360    245

We can use

library(dplyr)
x %>% 
    arrange(across(everything()))

-output

 A tibble: 5 x 4
    mpg   cyl  disp    hp
  <dbl> <dbl> <dbl> <dbl>
1  14.3     8  360    245
2  15.2     8  276.   180
3  19.2     8  400    175
4  21       6  160    110
5  21.4     6  258    110
Related