Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ordering data.frame by multiple variables in mixed direction [duplicate]

Tags:

r

For this sample data.frame,

df <- data.frame(var1=c("b","a","b","a","a","b"),
                 var2=c("l","l","k","k","l","k"),
                 var3=c("t","t","x","t","x","x"),
                 var4=c(5,3,3,5,5,3),
                 stringsAsFactors=F)

Unsorted

  var1 var2 var3 var4
1    b    l    t    5
2    a    l    t    3
3    b    k    x    3
4    a    k    t    5
5    a    l    x    5
6    b    k    x    3

I would like to sort on three columns 'var2', 'var3' and 'var4' in this order simultaneously. One column ascending and another two descending. Column names to sort are stored in variables.

sort_asc <- "var2"
sort_desc <- c("var3","var4")

What's the best way to do this in base R?

Updated details

This is the output if sorted ascending by 'var2' first (step 1) and then descending by 'var3' and 'var4' (as step 2).

var1   var2 var3 var4
a      l    x    5
b      k    x    3
b      k    x    3
a      k    t    5
b      l    t    5
a      l    t    3

But what I am looking for is doing all three sort at the same time to get this:

var1 var2 var3 var4
b    k    x    3
b    k    x    3
a    k    t    5
a    l    x    5
b    l    t    5
a    l    t    3

'var2' is ascending (k,l), within k and within l, 'var3' is descending, and similarly 'var4' is descending

To clarify, how this question is different from other data.frame ordering questions...

  • ordering on multiple columns
  • column names to order on are stored in variables
  • different ordering directions (asc,desc)
  • ordering is not step-wise (one sort after another) but rather simultaneous (all selected columns at same time)
  • using base R, not dplyr
like image 496
rmf Avatar asked Oct 22 '25 05:10

rmf


1 Answers

Step-wise ordering (sort ascending first and then descending).

dplyr solution:

library(dplyr)
df %>% 
   arrange_at(sort_asc) %>%
   arrange_at(sort_desc, desc)

  var1 var2 var3 var4
1    a    l    x    5
2    b    k    x    3
3    b    k    x    3
4    a    k    t    5
5    b    l    t    5
6    a    l    t    3

base R solution:

With base R, if there are multiple columns (in general) use order within do.call. Here, we create the index for ascending order first, then sort it descnding with the second set of columns ('sort_desc')

i1 <- do.call(order, df[sort_asc]) 
df1 <- df[i1,]
i2 <-  do.call(order, c(df1[sort_desc], list(decreasing = TRUE)))
df1[i2,]

  var1 var2 var3 var4
5    a    l    x    5
3    b    k    x    3
6    b    k    x    3
4    a    k    t    5
1    b    l    t    5
2    a    l    t    3

Simultaneous/Sequential ordering (all ordering variables are used in one ordering step):

dplyr solution:

df %>% 
   arrange_(.dots  = c(sort_asc, paste0("desc(", sort_desc, ")")))

#   var1 var2 var3 var4
#1    b    k    x    3
#2    b    k    x    3
#3    a    k    t    5
#4    a    l    x    5
#5    b    l    t    5
#6    a    l    t    3

base R solution:

With base R, if we need the similar output as with arrange_

df[do.call(order, c(as.list(df[sort_asc]), lapply(df[sort_desc], 
               function(x) -xtfrm(x)))),]

#  var1 var2 var3 var4
#3    b    k    x    3
#6    b    k    x    3
#4    a    k    t    5
#5    a    l    x    5
#1    b    l    t    5
#2    a    l    t    3
like image 190
akrun Avatar answered Oct 23 '25 19:10

akrun