How to partition when ranking on a particular column?

最后都变了- 提交于 2019-11-29 00:39:59

问题


All:

I have a data frame like the follow.I know I can do a global rank order like this:

dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'),
    Value = c(4,3,1,3,4,6,6,1,8,4)
);
> dt
   ID Value
1  A1     4
2  A2     3
3  A4     1
4  A2     3
5  A1     4
6  A4     6
7  A3     6
8  A2     1
9  A1     8
10 A3     4
dt$Order <- rank(dt$Value,ties.method= "first")
> dt
   ID Value Order
1  A1     4     5
2  A2     3     3
3  A4     1     1
4  A2     3     4
5  A1     4     6
6  A4     6     8
7  A3     6     9
8  A2     1     2
9  A1     8    10
10 A3     4     7

But how can I set a rank order for a particular ID instead of a global rank order. How can I get this done? In T-SQL, we can get this done as the following syntax:

RANK() OVER ( [ < partition_by_clause > ] < order_by_clause > )

Any idea?


回答1:


My way but there's likely better. Never used rank, din't even know about it. Thanks, may be useful.

#Your Data
dt <- data.frame(
    ID = c('A1','A2','A4','A2','A1','A4','A3','A2','A1','A3'),
    Value = c(4,3,1,3,4,6,6,1,8,4)
)
dt$Order <- rank(dt$Value,ties.method= "first")

#My approach
dt$id <- 1:nrow(dt) #needed for ordering and putting things back together
dt <- dt[order(dt$ID),]
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
    ties.method = "first"))))
dt[order(dt$id), -4]

Yields:

   ID Value Order Order.by.group
1  A1     4     5              1
2  A2     3     3              2
3  A4     1     1              1
4  A2     3     4              3
5  A1     4     6              2
6  A4     6     8              2
7  A3     6     9              2
8  A2     1     2              1
9  A1     8    10              3
10 A3     4     7              1

EDIT:

If you don't care about preserving the original order of the data then this works with less code:

dt <- dt[order(dt$ID),]
dt$Order.by.group <- unlist(with(dt, tapply(Value, ID, function(x) rank(x, 
   ties.method= "first"))))

   ID Value Order.by.group
1  A1     4              1
5  A1     4              2
9  A1     8              3
2  A2     3              2
4  A2     3              3
8  A2     1              1
7  A3     6              2
10 A3     4              1
3  A4     1              1
6  A4     6              2



回答2:


Many options.

Using ddply from the plyr package:

library(plyr)
ddply(dt,.(ID),transform,Order = rank(Value,ties.method = "first"))
   ID Value Order
1  A1     4     1
2  A1     4     2
3  A1     8     3
4  A2     3     2
5  A2     3     3
6  A2     1     1
7  A3     6     2
8  A3     4     1
9  A4     1     1
10 A4     6     2

Or if performance is an issue (i.e. very large data) using the data.table package:

library(data.table)
DT <- data.table(dt,key = "ID")
DT[,transform(.SD,Order = rank(Value,ties.method = "first")),by = ID]
      ID Value Order
 [1,] A1     4     1
 [2,] A1     4     2
 [3,] A1     8     3
 [4,] A2     3     2
 [5,] A2     3     3
 [6,] A2     1     1
 [7,] A4     1     1
 [8,] A4     6     2
 [9,] A3     6     2
[10,] A3     4     1

or in all its gory detail a base R solution using split lapply do.call and rbind:

do.call(rbind,lapply(split(dt,dt$ID),transform,
              Order = rank(Value,ties.method = "first")))



回答3:


Here are a couple of approaches:

ave This takes each set of Value numbers that have the same ID and applies rank separately to each such set. No packages are used.

Rank <- function(x) rank(x, ties.method = "first")
transform(dt, rank = ave(Value, ID, FUN = Rank))

giving:

   ID Value rank
1  A1     4    1
2  A2     3    2
3  A4     1    1
4  A2     3    3
5  A1     4    2
6  A4     6    2
7  A3     6    2
8  A2     1    1
9  A1     8    3
10 A3     4    1

Note that the above solution keeps the original row order. It could be sorted afterwards if that were desired.

sqldf with RPostgreSQL

# see FAQ #12 on the sqldf github home page for info on sqldf and PostgreSQL
# https://cran.r-project.org/web/packages/sqldf/README.html

library(RPostgreSQL)
library(sqldf)

sqldf('select 
          *, 
          rank() over (partition by "ID" order by "Value") rank 
       from "dt"
')

This solution reorders the rows. It is assumed that that is ok since your example solution did that (but if not append a sequence number column to dt and add an appropriate order by clause to re-order the result back into the sequence number order).




回答4:


You can use the data.table package.

setDT(dt) dt[, Order := rank(Value, ties.method = "first"), by = "ID"] dt <- as.data.frame(dt)

giving the desired output:

   ID Value Order
1  A1     4     1
2  A2     3     2
3  A4     1     1
4  A2     3     3
5  A1     4     2
6  A4     6     2
7  A3     6     2
8  A2     1     1
9  A1     8     3
10 A3     4     1


来源:https://stackoverflow.com/questions/9961700/how-to-partition-when-ranking-on-a-particular-column

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!