# this first multisort was the original way I wrote it
# it could have a maximum of four columns to sort
# multisort<-function(frame, col1, dec1=F, col2, dec2=F, col3="", dec3=F, col4="", dec4=F){
# #there are three required arguments, frame, col1, and col2
# #this means you must have at least two columns from a dataset that you want to sort
# if(col4=="") {
# if(col3=="") {
# #only have two columns to sort
# sub<-frame[order(getVal(frame,col2),decreasing=dec2),]
# } else {
# #three columns to sort
# sub<-multisort(frame,col2,dec2,col3,dec3)
# }
# } else {
# #must sort all four columns
# if(col3=="") {
# #quit trying to mess up the function
# sub<-multisort(frame,col2,dec2,col4,dec4)
# }
# sub<-multisort(frame,col2,dec2,col3,dec3,col4,dec4)
# }
# sub[order(getVal(sub,col1),decreasing=dec1),]
# }
#to sort just numeric values
#x[order(x$SAL,-x$AB),]
tmp<-read.csv('kforce.csv', header=T)
multisort<-function(frame, col1, dec1, col2, dec2, ...) {
#before doing any sorting, convert arguments into two vectors
mylist<-list(...)
col<-as.vector(c(col1,col2))
dec<-as.vector(c(dec1,dec2))
count<-6
for(i in mylist) {
if(i==TRUE || i==FALSE) {
if(count%%2==1) {
dec[count%/%2]<-i
count<-count+1
}
} else {
if(count%%2==0) { count<-count+1 }
else { count<-count+2 }
#set vectors, default dec to FALSE
col[count%/%2]<-i
dec[count%/%2]<-F
}
}
#the function parameters must be valid now
if(length(col)>2) {
extraArgs<-vector()
count<-1
for(i in col) {
pos=(count+1)%/%2
if(count>5) {
#append value to extraArgs
extraArgs[count-6]<-col[pos]
extraArgs[count-5]<-dec[pos]
}
count<-count+2
}
if(length(col)==3) {
sub<-multisort(frame,col[2],dec[2],col[3],dec[3])
} else {
sub<-multisort(frame,col[2],dec[2],col[3],dec[3],extraArgs)
}
} else {
#only two columns to sort - this is where the recursion ends!
sub<-frame[order(getVal(frame,col2),decreasing=dec2),]
}
sub[order(getVal(sub,col1),decreasing=dec1),]
}
getVal<-function(frame,col) {
eval(parse(text=paste(quote(frame),col,sep="$")))
}
multisort(tmp,"CO",T,"SAL",F,"POS",T,"AB")
#there are a few rules to make sure this works
#you must set descending to T or F for the first two columns (3rd & 5th argument)
#don't set arguments equal to a variable name (except for the first 5 arguments)
#the 6th argument (assuming it's a legitimate column) will be set to col3 regardless if set to something in function call
Another solution from Tatsuki Koyama -- much shorter R code which probably does the same thing:
sort.mat3 <- function(mat, sort.by, decreasing = F){
# This will sort a matrix or a data.frame based on the columns 'sort.by.'
# 'sort.by' and 'decreasing' are vectors of any length with length(sort.by) >= length(decreasing).
v <- rev(sort.by) ; de <- decreasing
if( length(de) < length(v) ){de[(length(de)+1) : length(v)] <- F}
for(i in 1:length(v)){
mat <- mat[order(mat[,v[i]], decreasing=rev(de)[i]),]}
mat}
# To reproduce Cole's example,
# > multisort(tmp,"CO",T,"SAL",F,"POS",T,"AB")
# do
sort.mat3(tmp, c(6,5,3), c(T, F, T))
# because "CO" is the 6th column, "SAL" is 5th and "POS" is 3rd column.
This topic: Main > WebHome > Seminars > RClinic > MultiSortFunction
Topic revision: revision 1