按两列排名并保持联系

Rank by two columns and keep ties

我的问题是这个问题的延续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亿行。因此,理想情况下,解决方案会很快。

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

rleid returns "IDs" 相同日期按组 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

我们可以使用 data.table 中的 frankdense 作为 ties.method 在按 'ID' 分组后 absolute 差异'Date'和参考日期('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))

dplyr dense_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

基本 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")))