3

I need to sort a data frame by several columns, and I have the name of the columns in a variable. My question is related to this one but in my case the columns to be used to sort are stored in a variable, like in this other question.

I borrow the data frame from the first question:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
dd
    b x y z
1  Hi A 8 1
2 Med D 3 1
3  Hi A 9 1
4 Low C 9 2

What I need to do is sort dd by z decreasing, and by b increasing. The sort answer is:

dd[with(dd, order(-z, b)), ]

What I have is:

sort_list <- c("z","b")

From the second question I know I can do:

dd[do.call(order, dd[, sort_list]),]

But that only gives me increasing order for both variables. What I can't figure out is how to do it with decreasing order. I've tried this:

dd[do.call(order, list(dd[, sort_list]), decreasing = c(TRUE,FALSE)),]

which produces an error, because it's assuming the decreasing argument is just another ordering item.

Keyur Potdar
  • 6,669
  • 6
  • 23
  • 36
eindzl
  • 103
  • 7
  • 1
    Are you looking for this? `require(dplyr) ; dd %>% group_by(b,z) %>% arrange(b,desc(z))` – count Oct 28 '16 at 09:19
  • No, sorry. As I say, my column names are stored in a variable, so I don't know them in advance. – eindzl Oct 28 '16 at 11:49

3 Answers3

2

Using data.table order function, you could do :

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
                            levels = c("Low", "Med", "Hi"), ordered = TRUE),
                 x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
                 z = c(1, 1, 1, 2))


library(data.table)
sort_list <- c("z","b")
sort_order <- c(-1, 1)
setDT(dd)
setorderv(dd, sort_list, sort_order)
dd
#>      b x y z
#> 1: Low C 9 2
#> 2: Med D 3 1
#> 3:  Hi A 8 1
#> 4:  Hi A 9 1
cderv
  • 4,972
  • 1
  • 16
  • 22
1

This works:

dd <- data.frame(b = factor(c("Hi", "Med", "Hi", "Low"), 
      levels = c("Low", "Med", "Hi"), ordered = TRUE),
      x = c("A", "D", "A", "C"), y = c(8, 3, 9, 9),
      z = c(1, 1, 1, 2))
sort_list <- c("z","b")
dd[order(-dd[,sort_list[1]], dd[,sort_list[2]]), ]
# or
dd[order(-dd[,sort_list["z"]], dd[,sort_list["b"]]), ]

if that's annoying to type, or the variable names change, or whatever, you could stick it in a function (in right order):

downup <- function(dat, sort_list){
    dat[order(-dat[,sort_list[1]], dat[,sort_list[2]]), ]
}
downup(dd)

That help?

tim riffe
  • 5,311
  • 23
  • 37
  • What if you sort by 3 columns ? or other order(asc \desc)? – Batanichek Oct 28 '16 at 11:16
  • As @Batanichek says this solution is limited to a very specific situation (two columns first decreasing, second increasing). It would be interesting to have a more general solution. It solves my particular problem though. Thanks. – eindzl Oct 28 '16 at 11:51
0

dplyr example

library(dplyr)
sort_list <- c("z","b")
sort_order=c(TRUE,FALSE)
dd %>% arrange_(.dots=ifelse(sort_order,paste0("desc(",sort_list,")"),sort_list))
Batanichek
  • 7,045
  • 26
  • 45