如何在特定列上排名时进行分区?

Rob*_*Min 14 r rank database-partitioning dataframe

所有:

我有一个类似于follow的数据框.我知道我可以做这样的全局排名顺序:

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
Run Code Online (Sandbox Code Playgroud)

但是,如何为特定ID而不是全局排名顺序设置排名顺序.我怎么能这样做?在T-SQL中,我们可以按照以下语法完成此操作:

RANK() OVER ( [ < partition_by_clause > ] < order_by_clause > )
Run Code Online (Sandbox Code Playgroud)

任何的想法?

jor*_*ran 13

很多选择.

使用ddplyplyr包:

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
Run Code Online (Sandbox Code Playgroud)

或者,如果使用data.table包,性能是一个问题(即非常大的数据):

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
Run Code Online (Sandbox Code Playgroud)

或者在其所有的血腥细节中使用split lapply do.call和的基本R解决方案rbind:

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

  • 很好的答案,像往常一样.为了从data.table获得最佳性能,最好尽可能避免使用`.SD`.这对于大型data.tables来说应该更快(这是你最有可能首先使用该包的地方!):`DT < - data.table(dt,key = c("ID","Value") ")); DT [,list(Value,Order = seq_len(.N)),by = ID]` (2认同)

G. *_*eck 6

以下是几种方法:

ave 这将获取具有相同ID的每组值数字,并将等级分别应用于每个这样的集合.没有使用包裹.

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

赠送:

   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
Run Code Online (Sandbox Code Playgroud)

请注意,上述解决方案保留原始行顺序.如果需要,可以在之后进行分类.

sqldf与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"
')
Run Code Online (Sandbox Code Playgroud)

此解决方案重新排序行.假设这是正确的,因为您的示例解决方案就是这样(但如果没有附加序列号列dt并添加适当的order by子句以将结果重新排序回序列号顺序).


Tyl*_*ker 4

我的方式,但可能有更好的。没用过等级,甚至不知道。谢谢,可能有用。

#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]
Run Code Online (Sandbox Code Playgroud)

产量:

   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
Run Code Online (Sandbox Code Playgroud)

编辑:

如果您不关心保留数据的原始顺序,那么这可以使用更少的代码:

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
Run Code Online (Sandbox Code Playgroud)