【问题标题】:Rank by two columns and keep ties按两列排名并保持平局
【发布时间】:2017-05-17 19:39:12
【问题描述】:

我的问题是继续这个问题Link

我有一个这样的数据集:

 ID    |     Date 

  A        01/01/2015
  A        02/01/2015
  A        02/01/2015
  A        02/01/2015
  A        05/01/2015     
  B        01/01/2015

我想按参考日期对每个日期进行排名 - 2015 年 1 月 31 日。最接近参考日期的日期排在第 1 位,排在第 2 位,依此类推。结果如下所示:

  ID    |     Date           |  Sequence

  A        01/01/2015           3
  A        02/01/2015           2
  A        02/01/2015           2
  A        02/01/2015           2
  A        05/01/2015           1  
  B        01/01/2015          ...

虽然排名功能确实如此,但我也想保持所有联系。我怎么做?

另外,我正在处理一个巨大的数据集 - 大约。 3 亿行。因此,理想情况下,解决方案会很快。

【问题讨论】:

标签: r data.table dplyr rank


【解决方案1】:

我们可以使用frank from data.tabledense 作为ties.method 在'Date'和参考日期之间的absolute 差异上按'ID'分组后('2015-01-31 ')

library(data.table)
setDT(df)[, Sequence := frank(abs(as.IDate(Date, "%d/%m/%Y")- 
              as.IDate("2015-01-31")), ties.method = "dense"), by = ID]
df
#    ID       Date Sequence
#1:  A 01/01/2015        3
#2:  A 02/01/2015        2
#3:  A 02/01/2015        2
#4:  A 02/01/2015        2
#5:  A 05/01/2015        1
#6:  B 01/01/2015        1

数据

df <- structure(list(ID = c("A", "A", "A", "A", "A", "B"), Date = c("01/01/2015", 
 "02/01/2015", "02/01/2015", "02/01/2015", "05/01/2015", "01/01/2015"
)), .Names = c("ID", "Date"), class = "data.frame", row.names = c(NA, 
-6L))

【讨论】:

  • 谢谢。效果很好。我实际上并没有意识到坦率的功能。
  • 只是快速跟进。我们可以这样做: setDT(df)[, Sequence := frank(Date, ties.method = "random"), by = ID] 来加快处理速度吗?它适用于较小的物体。但是对于巨大的,它不会感动任何人。
【解决方案2】:

dplyrdense_rank:

library(dplyr)
df$Sequence <- dense_rank(as.numeric(as.Date('31/01/2015', '%d/%m/%Y') - as.Date(df$Date, '%d/%m/%Y')))
head(df) 

  ID       Date Sequence
1  A 01/01/2015        3
2  A 02/01/2015        2
3  A 02/01/2015        2
4  A 02/01/2015        2
5  A 05/01/2015        1
6  B 01/01/2015        3

【讨论】:

    【解决方案3】:

    这是一个可行的data.table 方法。

    rleid 按组 ID 返回同一日期的“ID”。但是,这些 ID 从 0 开始计数。在第二个链中,[(max(var) - var) + 1L 为每个 ID 组反转这些日期 ID。

    df[, var:=rleid(Date), by=ID][, var := (max(var) - var) + 1L, by=ID]
    df
       ID       Date var
    1:  A 01/01/2015   3
    2:  A 02/01/2015   2
    3:  A 02/01/2015   2
    4:  A 02/01/2015   2
    5:  A 05/01/2015   1
    6:  B 01/01/2015   1
    

    【讨论】:

    • 感谢您的回复。我在想这样的事情:var := (max(var) - var) + 1L, by=ID,但我不知道如何实现它。
    【解决方案4】:

    基础R 解决方案。首先通过将它们转换为Date 对象并获取差的绝对值来获取您的日期和目标日期。

    timediff <- abs(as.Date(df[["Date"]], format = "%d/%m/%Y") - as.Date("2015-01-31"))
    

    接下来我们可以使用rank 来获取它们的顺序。我们可以使用任何为关系生成单个值的ties.method,但"min""max" 可能是最好的,因为它们输出整数。

    diffrank <- rank(timediff, ties.method = "min")
    

    最后我们可以使用this solution 重新排序排名以消除实例之间的差距。

    df[["Sequence"]] <- as.numeric(factor(diffrank))
    

    如果您愿意,这一切都可以在一行中完成:

    df[["Sequence"]] <- as.numeric(factor(rank(
                            abs(as.Date(df[["Date"]], format = "%d/%m/%Y") - 
                                   as.Date("2015-01-31")), ties.method = "min")))
    

    【讨论】:

    • 我将在较小的数据集上进行尝试。但我认为它会比 data.table 解决方案慢一些。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 2018-05-13
    • 2013-04-06
    • 2017-05-26
    • 1970-01-01
    相关资源
    最近更新 更多