Edith
Edith

Reputation: 77

Sorting dataframe by multiple columns through list of column names

I have a loop, where in the different iterations I want to sort the dataframe using different column lists. I can do this sorting when I hardcode the sorting variables. However I want to pass the column names using variable list. I could not find a way of doing this.

DT <-data.frame(avar = c(1,4,3,1), bvar = c("a","f","s","b"), cvar = c(3,4,5,2))

sort1 <-c("avar", "cvar")
sort2 <-c("avar", "bvar")
sorting <-list(sort1,sort2)
DT2<-list()

for (i in 1:2) {
  sorter<- sorting[[i]]
   #THE FOLLOWING SOLUTION WORKS!!!
   DT2[[i]] <- DT[do.call(order,DT[as.character(sorting[[i]])]),]       
}

What I mean by sorting by c("avar", "cvar") is that the data is to be sorted by avar first, and (if there are two values of avar that are the same) then by cvar. In other words, the output of sorting by that vector should be only one sorted dataframe (not a list). Same thing about sorting by c("avar", "bvar"). Above "ps1" stands for one of the proposed solutions. It gives me DT2[[1]] that is a list of two dataframes. that is not what I need. DT2 should be a list of two dataframes. DT2[[1]] should be one dataframe.

I want to also stress that I do need this sorting to happen through a loop, not through passing the list ("sorting") to the command. In other words, first iteration should sort the database by first item of the sorting list, which is the vector "sorter" in my code. In real application, the data in different iterations is not the same dataset.

After the first loop, DT2[[1]] should be sorted as follows:

avar  bvar  cvar
1     b     2
1     a     3
3     s     5
4     f     4    

after the second loop DT2[[2]] should be sorted as :

avar  bvar  cvar
1     a     3
1     b     2
3     s     5
4     f     4    

In real life I may also have a different number of sorting columns in different iteration.

Regarding solutions proposed that use "map" function: I have some geospacial packages loaded (mapproj, fiftystater, geofacet), so the "map" function does not work as suggested unless I unload those packages. Is there a way to qualify to use native "map" function rather than geospacial map function?

Thank you for your help!

Upvotes: 0

Views: 547

Answers (4)

Edith
Edith

Reputation: 77

The following solution works for me! Other proposed solutions I tried failed to sort by two variables in a given vector simultaneously.

DT <-data.frame(avar = c(1,4,3,1), bvar = c("a","f","s","b"), cvar = c(3,4,5,2))

sort1 <-c("avar", "cvar")
sort2 <-c("avar", "bvar")
sorting <-list(sort1,sort2)
DT2<-list()

for (i in 1:2) {
    #THE FOLLOWING SOLUTION WORKS!!!
    DT2[[i]] <- DT[do.call(order,DT[as.character(sorting[[i]])]),]       
}

Upvotes: 0

akrun
akrun

Reputation: 886938

Here is a method with setorder from data.table

library(data.table)
Map(setorderv, replicate(2, copy(DT), simplify = FALSE), sorting)
#[[1]]
#  avar bvar cvar
#4    1    b    2
#1    1    a    3
#3    3    s    5
#2    4    f    4

#[[2]]
#  avar bvar cvar
#1    1    a    3
#4    1    b    2
#3    3    s    5
#2    4    f    4

Or use arrange_at from dplyr (without using the evaluation way)

library(tidyverse)
map(sorting, ~ DT %>%
                 arrange_at(.x))
#[[1]]
#  avar bvar cvar
#1    1    b    2
#2    1    a    3
#3    3    s    5
#4    4    f    4

#[[2]]
#  avar bvar cvar
#1    1    a    3
#2    1    b    2
#3    3    s    5
#4    4    f    4

Upvotes: 0

Maurits Evers
Maurits Evers

Reputation: 50668

A dplyr+purrr solution

library(purrr)
library(dplyr)
map(sorting, ~arrange(DT, !!!syms(.x)))
#[[1]]
#  avar bvar cvar
#1    1    b    2
#2    1    a    3
#3    3    s    5
#4    4    f    4
#
#[[2]]
#  avar bvar cvar
#1    1    a    3
#2    1    b    2
#3    3    s    5
#4    4    f    4

Upvotes: 1

Ronak Shah
Ronak Shah

Reputation: 388807

Using base R we can apply order on selected columns in sorting using do.call. We use lapply to get list of dataframes

lapply(sorting, function(x) DT[do.call(order, DT[x]), ])


#[[1]]
#  avar bvar cvar
#4    1    b    2
#1    1    a    3
#3    3    s    5
#2    4    f    4

#[[2]]
#  avar bvar cvar
#1    1    a    3
#4    1    b    2
#3    3    s    5
#2    4    f    4

Upvotes: 1

Related Questions