Simon C.
Simon C.

Reputation: 1067

Why sort is slower than order function in R?

All is in the title. I would expect that order uses sort to find the order of the values in a vector. Thus sort should be quicker than order to sort a vector, but this is not the case:

library(microbenchmark)
ss=sample(100,10000,replace=T)
microbenchmark(sort(ss))
microbenchmark(ss[order(ss)])

result:

> microbenchmark(sort(ss))
Unit: microseconds
    expr     min       lq     mean  median       uq      max neval
 sort(ss) 141.535 144.6415 173.6581 146.358 150.2295 2531.762   100
> microbenchmark(ss[order(ss)])
Unit: microseconds
        expr     min       lq     mean  median       uq     max neval
 ss[order(ss)] 109.198 110.9865 115.6275 111.901 115.3655 197.204   100

Example with a larger vector:

ss=sample(100,1e8,replace=T)
microbenchmark(sort(ss), ss[order(ss)], times = 5)
# Unit: seconds
#           expr      min       lq     mean   median       uq      max neval
#       sort(ss) 5.427966 5.431971 5.892629 6.049515 6.207060 6.346633     5
#  ss[order(ss)] 3.381253 3.500134 3.562048 3.518079 3.625778 3.784997     5

Upvotes: 5

Views: 924

Answers (2)

De Novo
De Novo

Reputation: 7610

because sort.default() uses order (rather than the other way around).

function (x, decreasing = FALSE, na.last = NA, ...) 
{
  if (is.object(x)) 
    x[order(x, na.last = na.last, decreasing = decreasing)]
  else sort.int(x, na.last = na.last, decreasing = decreasing, 
    ...)
}

sort has to determine its method, then execute the same x[order(x)] call you're executing in one step when you use x[order(x)] directly. You can ramp up the size of the input as much as you want. For an integer vector, x[order(x)] should always outperform sort(x).

@Hugh's answer a year later demonstrates that the bulk of the difference is in the default treatment of NA values. It should be the accepted answer here.

Upvotes: 5

Hugh
Hugh

Reputation: 16089

The treatment of NA values under the default arguments is different. In sort, the entire vector must be scanned for NA values, which are then removed; in order, they are simply put last. When the argument sort.last = TRUE is used in both, the performance is basically identical.

ss=sample(100,1e8,replace=T) 
bench::mark(sort(ss), ss[order(ss)], sort(ss, na.last = TRUE))
# A tibble: 3 x 14
  expression    min   mean median    max `itr/sec` mem_alloc  n_gc n_itr total_time result
  <chr>      <bch:> <bch:> <bch:> <bch:>     <dbl> <bch:byt> <dbl> <int>   <bch:tm> <list>
1 sort(ss)   2.610s 2.610s 2.610s 2.610s     0.383 762.940MB     0     1     2.610s <int ~
2 ss[order(~ 1.597s 1.597s 1.597s 1.597s     0.626 762.940MB     0     1     1.597s <int ~
3 sort(ss, ~ 1.592s 1.592s 1.592s 1.592s     0.628 762.940MB     0     1     1.592s <int ~
# ... with 3 more variables: memory <list>, time <list>, gc <list>

Upvotes: 5

Related Questions