Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doing absolute descending sort of data.table through function?

I would like to do an absolute descending sort (i.e. sort ignoring sign so e.g. 5, -2, 1) of my data.table by passing a sort.field into a function.

I have looked at descending sort, but in my efforts I get errors or I am transforming the sign of my variable, and not sorting it properly.

This works:

library(data.table)
DT <- data.table(id = c("a","b","z"), 
                 score = c(1, 5, -2))
DT1 <- copy(DT)

#doing sort direct works
DT1 <- DT1[order(-abs(score))] 
# i.e. b, Z, a

But when passing a parameter I cannot find the right syntax (math errors, j must be provided, etc.)

#in function
sort.field = "score"

sortme <- function(dt, sort.field){

  dt <- dt[order(-abs(sort.field))]  
}

DT2 <- sortme(DT, sort.field)
#  ERROR get non-numeric argument to maths function as it sees string

I have tried various evals, as.name, with = F, etc.

   dt <- dt[, order(-abs(as.name(sort.field))]  

   # even
    expr <- substitute(x := -abs(x),  list(x=as.name(sort.field)))
    dt<- dt[,eval(expr)]

DT3 <- DT[,eval(expr)] # changes all to negative
DT4 <- DT[order(eval(expr))] # DT not happy

Please put me out of my misery! Thanks so much.

P.S. setorderv() handles straight Ascending and Descending cases. Yes, I could add a column, absolute it and then use setorderv, then delete the temp column, but I am looking for a more elegant solution.

Edit: Others have pointed to the similar answer for filtering. That question also covers data.frames, and not solely data tables, and is focused on filtering rows of data. It does not do a transformative ordering with a function like abs() of the field text, keeping all the rows and not changing the data. Also this question could help others looking at this absolute sort type for data.table which is not covered in its setorderv().

like image 781
micstr Avatar asked Apr 11 '15 06:04

micstr


2 Answers

Try this:

sort.field = "score"

sortme <- function(dt, sort.field) dt[order(-abs(dt[[sort.field]]))]

sortme(DT, sort.field)
#   id score
#1:  b     5
#2:  z    -2
#3:  a     1

What you did after the first function definition was to pass a string to the abs function, hence the ERROR get non-numeric argument to maths function as it sees string

like image 158
Colonel Beauvel Avatar answered Sep 28 '22 02:09

Colonel Beauvel


The eval(as.name()) would work

 sort.field = "score"
 sort_me <- function(dt, sort.field){
  dt[order(-abs(eval(as.name(sort.field))))]
 }
 sort_me(DT, sort.field)
 #   id score
 #1:  b     5
 #2:  z    -2
 #3:  a     1
like image 29
akrun Avatar answered Sep 28 '22 03:09

akrun