fahans
fahans

Reputation: 27

Construct new ranked columns out of rank columns in R

In the code below I create subsets of df by column and afterwards sorting it by the specific rank variable.

    df <- data.frame(
          id=c("a","b","c","d","e","f"),
          rank1=c(1,3,2,1,5,7),
          rank2=c(4,6,2,1,4,2),
          rank3=c(4,6,2,1,4,2))
        
        for(i in colnames(df)[-1]) {
  assign(i, df %>% 
           arrange(desc(across(all_of(i)))) %>%
           select(id))
}

Now I want to combine these ranked subsets into a new dataframe with one rank variable. This creates the new dataframe:

rankings <- data.frame(
            rank=1:6
)

How do I merge the new dataframe with the ranked subsets of my original dataframe? Preferably the columns should have the name of their ranking (rank1, rank2, rank3), so a rename is probably needed in the loop as well, which I can't get to work, too.

Upvotes: 1

Views: 77

Answers (1)

akrun
akrun

Reputation: 887128

With the current code of creating multiple objects in the env, get those objects into a list with mget by specifying the pattern argument of ls to match the object name substring, then loop over the list with imap, rename the 'id' column with name of the list element (.y), bind them to a single dataset column wise (_dfc), and then bind with the 'rankings' data as well

library(dplyr)
library(purrr)
mget(ls(pattern = '^rank\\d+$')) %>%
     imap_dfc(~ .x %>%
                  rename(!! .y := id)) %>%
     bind_cols(rankings, .)

-output

#   rank rank1 rank2 rank3
#1    1     f     b     b
#2    2     e     a     a
#3    3     b     e     e
#4    4     c     c     c
#5    5     a     f     f
#6    6     d     d     d

Also, instead of doing the assign to create multiple objects and then gathering those objects from the global env, this can be done in a more straightforward way by looping across the columns that have 'rank', return the 'id' column based on the order of those columns in descending and then create the rank column as sequence of rows (row_number())

df %>% 
    transmute(across(contains('rank'), ~ id[order(-.)])) %>%
    mutate(rank = row_number(), .before = 1)
#  rank rank1 rank2 rank3
#1    1     f     b     b
#2    2     e     a     a
#3    3     b     e     e
#4    4     c     c     c
#5    5     a     f     f
#6    6     d     d     d

Upvotes: 1

Related Questions