Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dense Rank by Multiple Columns in R

Tags:

r

dplyr

How can I get a dense rank of multiple columns in a dataframe? For example,

# I have:
df <- data.frame(x = c(1,1,1,1,2,2,2,3,3,3), 
                 y = c(1,2,3,4,2,2,2,1,2,3))
# I want:
res <- data.frame(x = c(1,1,1,1,2,2,2,3,3,3), 
                  y = c(1,2,3,4,2,2,2,1,2,3),
                  r = c(1,2,3,4,5,5,5,6,7,8))
res
   x y z
1  1 1 1
2  1 2 2
3  1 3 3
4  1 4 4
5  2 2 5
6  2 2 5
7  2 2 5
8  3 1 6
9  3 2 7
10 3 3 8

My hack approach works for this particular dataset:

df %>%
  arrange(x,y) %>%
  mutate(r = if_else(y - lag(y,default=0) == 0, 0, 1)) %>%
  mutate(r = cumsum(r))

But there must be a more general solution, maybe using functions like dense_rank() or row_number(). But I'm struggling with this.

dplyr solutions are ideal.

like image 956
lowndrul Avatar asked Oct 04 '18 22:10

lowndrul


2 Answers

data.table

data.table has you covered with frank().

library(data.table)    
frank(df, x,y, ties.method = 'min')

[1]  1  2  3  4  5  5  5  8  9 10

You can df$r <- frank(df, x,y, ties.method = 'min') to add as a new column.

tidyr/dplyr

Another option (though clunkier) is to use tidyr::unite to collapse your columns to one plus dplyr::dense_rank.

library(tidyverse)

df %>%

  # add a single column with all the info
  unite(xy, x, y) %>%
  cbind(df) %>%

  # dense rank on that
  mutate(r = dense_rank(xy)) %>%

  # now drop the helper col
  select(-xy)
like image 39
arvi1000 Avatar answered Sep 22 '22 10:09

arvi1000


Right after posting, I think I found a solution here. In my case, it would be:

mutate(df, r = dense_rank(interaction(x,y,lex.order=T)))

But if you have a better solution, please share.

like image 175
lowndrul Avatar answered Sep 21 '22 10:09

lowndrul