Kev Roy
Kev Roy

Reputation: 11

Rank on two columns, r

I am trying to make a column which will rank x, grouped by names.

names <- rep(c("Ben", "Joe", "Karen", "Joan"), times = 4)
set.seed(1)
x <- sample((1:10), 16, replace = TRUE)
learnrank <- data.frame(names,x)

The I used the tapply function:

learnrank$rank <- 
tapply(learnrank$x, learnrank$names, 
rank, ties.method = "first")

learnrank

names    x     rank
<chr>  <int>  <list>
Ben      3  <int [4]>
Joe      4  <int [4]>
Karen    6  <int [4]>
Joan    10  <int [4]>
Ben      3  <int [4]>
Joe      9  <int [4]>
Karen   10  <int [4]>
Joan     7  <int [4]>
Ben      7  <int [4]>
Joe      1  <int [4]>

What I would like is this below, where we rank all Ben scores from 1:4, Joe's scores from 1:4, etc.

names    x     rank
<chr>  <int>  <list>
Ben      3      1
Joe      4      2
Karen    6      2
Joan    10      4
Ben      3      2
Joe      9      4
Karen   10      4
Joan     7      3    
Ben      7      3
Joe      1      1

Does anyone have any suggestions?

Regards

Kevin

Upvotes: 0

Views: 83

Answers (4)

GKi
GKi

Reputation: 39657

You can use ave instead of tapply like:

learnrank$rank <- ave(learnrank$x, learnrank$names,
 FUN=function(x) rank(x, ties.method = "first"))
learnrank
#   names  x rank
#1    Ben  9    4
#2    Joe  4    1
#3  Karen  7    3
#4   Joan  1    1
#5    Ben  2    2
#6    Joe  7    3
#7  Karen  2    1
#8   Joan  3    2
#9    Ben  1    1
#10   Joe  5    2
#11 Karen  5    2
#12  Joan 10    4
#13   Ben  6    3
#14   Joe 10    4
#15 Karen  7    4
#16  Joan  9    3

Upvotes: 2

Ronak Shah
Ronak Shah

Reputation: 388907

You can order the data and assign row number for each name.

This can be done in base R :

learnrank <- learnrank[do.call(order, learnrank), ]
learnrank$rank <- with(learnrank, ave(x, names, FUN = seq_along))

Or using dplyr :

library(dplyr)
learnrank %>%
  arrange_all() %>%
  group_by(names) %>%
  mutate(row = row_number())

Upvotes: 2

Martin Gal
Martin Gal

Reputation: 16978

Using dplyr:

learnrank %>%
  group_by(names) %>%
  mutate(rank=rank(x))

gives you

# A tibble: 16 x 3
# Groups:   names [4]
   names     x  rank
   <fct> <int> <dbl>
 1 Ben       9   4  
 2 Joe       4   1  
 3 Karen     7   3.5
 4 Joan      1   1  
 5 Ben       2   2  
 6 Joe       7   3  
 7 Karen     2   1  
 8 Joan      3   2  
 9 Ben       1   1  
10 Joe       5   2  
11 Karen     5   2  
12 Joan     10   4  
13 Ben       6   3  
14 Joe      10   4  
15 Karen     7   3.5
16 Joan      9   3 

With the argument ties.method you can handle the tie of ranks. Choosing ties.method="first" is equivalent to

learnrank %>%
  group_by(names) %>%
  mutate(rank=row_number(x))

Upvotes: 1

Allan Cameron
Allan Cameron

Reputation: 173793

Here's a dplyr solution:

library(dplyr)

learnrank %>% 
  group_by(names) %>% 
  mutate(x = sort(x), rank = order(-x)) %>%
  arrange(names)
#> # A tibble: 16 x 3
#> # Groups:   names [4]
#>    names     x  rank
#>    <fct> <int> <int>
#>  1 Ben       1     4
#>  2 Ben       2     3
#>  3 Ben       6     2
#>  4 Ben       9     1
#>  5 Joan      1     4
#>  6 Joan      3     3
#>  7 Joan      9     2
#>  8 Joan     10     1
#>  9 Joe       4     4
#> 10 Joe       5     3
#> 11 Joe       7     2
#> 12 Joe      10     1
#> 13 Karen     2     3
#> 14 Karen     5     4
#> 15 Karen     7     2
#> 16 Karen     7     1

Upvotes: 2

Related Questions