Suppose I have the following matrix:
output <- cbind(c(1, 2, 3, 4, 5), c(1, 1, 2, 3, 1),
c(1, 1, 1, 2, 1), c(1, 1, 1, 1, 1))
I want to break ties for the 4th column with the 3rd and then with 2nd and then the 1st, i.e. I would like to end up obtaining this matrix instead.
output_1 <- cbind(c(1,2,5,3,4), c(1,1,1,2,3), c(1,1,1,1,2), c(1,1,1,1,1))
I tried to do output_1 <- output[order(output[,4],output[,3], output[,2], output[,1]),]
which gives me the output I want. But is there a way to automate it, so that every time I have a new column, I don't need to hardwire inserting it.
Many thanks in advance
CodePudding user response:
One way to order programmatically is to use do.call
on a list of the ordering components (matrix columns).
output[do.call(order, rev(asplit(output, 2))),]
# [,1] [,2] [,3] [,4]
# [1,] 1 1 1 1
# [2,] 2 1 1 1
# [3,] 5 1 1 1
# [4,] 3 2 1 1
# [5,] 4 3 2 1
Walk-through:
asplit(., 2)
converts a matrix into alist
, whereMARGIN=2
means by-column. We could easily have just list-ized a subset of columnssince you want to prioritize them last-column-first, its easy-enough to
rev
erse them; it's also easy enough to use a specific order, as inasplit(output,2)[c(4,2,3,1)]
;(Note that we could have
asplit
and ordered in a different order, such asasplit(output[,4:1], 2)
and no need forrev
.)that is a
list
, which is precisely whatdo.call
needs as its second argument; if you aren't familiar withdo.call
, then the following are equivalent:L <- list(1:3, 4, 5:99) do.call(somefunc, L) somefunc(L[[1]], L[[2]], L[[3]])
since the
do.call(order, ..)
returns an integer vector of the desired order, we just pass that as thei=
argument to[
for matrix row subset/order.