oercim
oercim

Reputation: 1848

R-ranking values of a column by grouping, conditional to another variable

I have such a data frame(df):

group col1 col2  
x      a    22    
x      a    23  
x      b    16  
x      b    18  
y      a    11  
y      a    12  
y      a    16  
y      a    45  
y      b    24  

Desired output is:

group col1 col2 rank 
x      a    22  1  
x      a    23  2
x      b    16  0
x      b    18  0
y      a    11  1
y      a    12  2
y      a    16  3
y      a    45  4
y      b    24  0

Namely,

How can I do that by using R? I will be very glad for any help. Thanks a lot.

Upvotes: 9

Views: 5627

Answers (3)

akrun
akrun

Reputation: 887048

You could try

library(dplyr)
 df %>%
    group_by(group, col1) %>% 
    mutate(rank=replace(min_rank(col2), col1=='b',0) )
#    group col1 col2 rank
#1     x    a   22    1
#2     x    a   23    2
#3     x    b   16    0
#4     x    b   18    0
#5     y    a   11    1
#6     y    a   12    2
#7     y    a   16    3
#8     y    a   45    4
#9     y    b   24    0

If you don't want gaps between ranks when there are ties, replace min_rank with dense_rank

Or, instead of replace

 res <- df %>% 
          group_by(group, col1) %>% 
          mutate(rank=(col1!='b')*min_rank(col2))

 as.data.frame(res) #would be `data.frame`
 #    group col1 col2 rank
 #1     x    a   22    1
 #2     x    a   23    2
 #3     x    b   16    0
 #4     x    b   18    0
 #5     y    a   11    1
 #6     y    a   12    2
 #7     y    a   16    3
 #8     y    a   45    4
 #9     y    b   24    0

Upvotes: 10

nicola
nicola

Reputation: 24480

In base R:

df$rank<-ave(df$col2,df[,1:2],FUN=rank)*(df$col1!="b")

Upvotes: 6

David Arenburg
David Arenburg

Reputation: 92282

Or using data.table v>= 1.9.5

library(data.table)
setDT(df)[, rank := frank(col2, ties.method = "dense"),
             by = .(group, col1)][col1 == "b", rank := 0L][]

#    group col1 col2 rank
# 1:     x    a   22    1
# 2:     x    a   23    2
# 3:     x    b   16    0
# 4:     x    b   18    0
# 5:     y    a   11    1
# 6:     y    a   12    2
# 7:     y    a   16    3
# 8:     y    a   45    4
# 9:     y    b   24    0

Or like @Arun suggested, you can skip one grouping step if you will set b to zero first

dt[, rank := 0L][col1 != "b", rank := frank(col2, ties.method="dense"), by=group][]

Upvotes: 6

Related Questions