【问题标题】:Rank most recent scores of students within a given date - 30 days window对给定日期内最近的学生分数进行排名 - 30 天窗口
【发布时间】:2018-09-12 03:56:27
【问题描述】:

以下是我的 dataframe/data.table 的样子。 rank 列是我的 desired 计算字段。

library(data.table)
df <- fread('
             Name   Score         Date              Rank
             John    42         1/1/2018              3   
             Rob     85         12/31/2017            2
             Rob     89         12/26/2017            1
             Rob     57         12/24/2017            1
             Rob     53         08/31/2017            1
             Rob     72         05/31/2017            2
             Kate    87         12/25/2017            1
             Kate    73         05/15/2017            1
             ')
df[,Date:= as.Date(Date, format="%m/%d/%Y")]

我试图在 30 天的窗口内计算每个给定时间点数据中每个学生的排名。为此,我需要获取所有学生在给定时间点的最新分数,然后传递排名函数。

在第一行,截至1/1/2018John 在过去 30 天的窗口中还有两个竞争对手:12/31/2017 中最近得分为 85 的 Rob 和最近得分为8712/25/2017 中,这两个日期都在 1/1/2018 - 30 日窗口内。 John 的排名为 3,最低得分为 42。如果只有一名学生在date(at a given row) - 30 day window 范围内,则排名为 1。

第三行的日期是12/26/2017。所以 Rob 在12/26/2017 的分数是89。在12/26/2017 - 30 天的时间窗口内只有一个学生的案例,那就是 kate 在12/25/2017 上的最新分数(87)。所以在(12/26/2017) - 30 的时间窗口内,Rob 的89 得分高于Kate 的87 得分,因此Rob 的排名为1

我一直在考虑使用 Efficient way to perform running total in the last 365 day window 此处的框架,但在使用排名之前很难想出一种方法来获取所有学生在给定时间点的所有最近分数。

【问题讨论】:

  • Rob 在 12/31 的得分也应该是 89,因此他在第 2 行排名第 1,对吧?
  • @Frank Hey Frank,我在想,截至 12/31,Rob 的最新分数是 85,仅次于 Kate 在 12/25 的 87(在 12/31 - 30 天)窗口)。

标签: r dplyr data.table rank


【解决方案1】:

这似乎有效:

ranks = df[.(d_dn = Date - 30L, d_up = Date), on=.(Date >= d_dn, Date <= d_up), allow.cart=TRUE][, 
  .(LatestScore = last(Score)), by=.(Date = Date.1, Name)]

setorder(ranks, Date, -LatestScore)
ranks[, r := rowid(Date)]

df[ranks, on=.(Name, Date), r := i.r]

   Name Score       Date Rank r
1: John    42 2018-01-01    3 3
2:  Rob    85 2017-12-31    2 2
3:  Rob    89 2017-12-26    1 1
4:  Rob    57 2017-12-24    1 1
5:  Rob    53 2017-08-31    1 1
6:  Rob    72 2017-05-31    2 2
7: Kate    87 2017-12-25    1 1
8: Kate    73 2017-05-15    1 1

...使用last,因为笛卡尔连接似乎是排序的,我们想要最新的测量值。

更新连接的工作原理

i. 前缀表示它是 x[i, ...] 连接中来自 i 的列,并且分配 := 始终位于 x 中。因此,它在x 中查找i 的每一行并找到匹配项,将值从i 复制到x

另一种有时有用的方法是在i 中查找x 行,类似于df[, r := ranks[df, on=.(Name,Date), x.r]] 在这种情况下x.r 仍然来自ranks 表(现在在x 相对位置加入)。


还有……

ranks = df[CJ(Name = Name, Date = Date, unique=TRUE), on=.(Name, Date), roll=30, nomatch=0]
setnames(ranks, "Score", "LatestScore")

# and then use the same last three lines above    

我不确定一个与另一个的效率,但我想这取决于名称的数量、测量频率以及测量天数重合的频率。

【讨论】:

  • 正要发布像df[, rank := df[.(iName = Name, iDate1 = Date - 30, iDate2 = Date), on = .(Date &gt;= iDate1, Date &lt;= iDate2), by = .EACHI, .SD[order(x.Date), .SD[.N], by = Name][, frank(-Score)[Name == iName]]] $V1][] 这样的野兽,按.EACHI 分组,但你的要简洁得多。
  • r:= i.r 似乎神奇地清理和排序 df 和 rank on=.(Name, Date) 之间的连接。什么是 i.r?
【解决方案2】:

使用data.table 的解决方案虽然不确定它是否是最有效的用法:

df[.(iName=Name, iScore=Score, iDate=Date, StartDate=Date-30, EndDate=Date), 
    .(Rank=frank(-c(iScore[1L], .SD[Name != iName, max(Score), by=.(Name)]$V1), 
        ties.method="first")[1L]), 
    by=.EACHI, 
    on=.(Date >= StartDate, Date <= EndDate)]

说明:

1) 外部方括号在日期范围内进行非等值连接(即每行的 30 天前和最近日期)。尝试根据输入数据研究以下输出:

df[.(iName=Name, iScore=Score, iDate=Date, StartDate=Date-30, EndDate=Date),
    c(.(RowGroup=.GRP), 
        .SD[, .(Name, Score, Date, OrigDate, iName, iScore, iDate, StartDate, EndDate)]),
    by=.EACHI,
    on=.(Date >= StartDate, Date <= EndDate)]

2) .EACHI 是对i 的每一行进行j 计算。

3)j里面,iScore[1L]是当前行的分数,.SD[Name != iName]表示取与当前行学生不对应的分数。然后,我们在 30 天窗口内为这些学生中的每个学生使用 max(Score)

4) 连接所有这些分数并计算当前行分数的排名,同时通过采取第一个平局来处理平局。

注意:

请参阅 ?data.table 以了解 ijbyon.EACHI 所指的内容。


在 OP 的 cmets 之后编辑:

我会添加一个 OrigDate 列并查找与最新日期匹配的列。

df[, OrigDate := Date]

df[.(iName=Name, iScore=Score, iDate=Date, StartDate=Date-30, EndDate=Date), 
    .(Name=iName, Score=iScore, Date=iDate, 
        Rank=frank(-c(iScore[1L], 
                .SD[Name != iName, Score[OrigDate==max(OrigDate)], by=.(Name)]$V1), 
            ties.method="first")[1L]), 
    by=.EACHI, 
    on=.(Date >= StartDate, Date <= EndDate)]

【讨论】:

  • 感谢您的有用解释。我的目标是使用这些学生的最新分数,而不是在 30 天的窗口内为每个学生使用 max(Score)。因此,截至 2018 年 1 月 1 日,将 John 的 42 与 Rob 的 85 进行比较,后者是最近(12/31)到 1/12018,而不是最大(分数)89,它不是最近的,因为它发生在 12/ 26. John 的 42 分也与 Kate 的 87 分进行了比较,后者恰好是最高分,也是 Kate (12/25) 相对于 2018 年 1 月 1 日的最新分数。你推荐什么编辑?
【解决方案3】:

我想出了以下部分解决方案,但遇到了问题 - 是否有可能会有两个人在同一日期发生?

如果没有,请查看以下代码:

library(tidyverse) # easy manipulation
library(lubridate) # time handling
# This function can be added to 
get_top <- function(df, date_sel) {
  temp <- df %>% 
    filter(Date > date_sel - months(1)) %>% # look one month in the past from given date
    group_by(Name) %>% # and for each occuring name
    summarise(max_score = max(Score)) %>% # find the maximal score
    arrange(desc(max_score)) %>% # sort them
    mutate(Rank = 1:n()) # and rank them
  temp
}

现在,您必须在表格中找到给定日期的名称并返回其排名。

【讨论】:

    【解决方案4】:
    library(data.table)
    library(magrittr)
    
    setorder(df, -Date)
    
    fun <- function(i){
        df[i:nrow(df), head(.SD, 1), by = Name] %$% 
            rank(-Score[Date > df$Date[i] - 30])[1]
    }
    df[, rank := sapply(1:.N, fun)]
    

    【讨论】:

      【解决方案5】:

      这可以通过加入dfdf 后面 30 天内或相同日期且得分更高或相同的行来完成。然后对于每个原始行和连接行名称,获取最新的连接行名称。每个原始df 行的剩余连接行数就是排名。

      library(sqldf)
      
      sqldf("with X as
        (select a.rowid r, a.*, max(b.Date) Date
        from df a join df b
        on b.Date between a.Date - 30 and a.Date and b.Score >= a.Score
        group by a.rowid, b.Name)
      
        select Name, Date, Score, count(*) Rank 
        from X
        group by r
        order by r")
      

      给予:

        Name       Date Score Rank
      1 John 2018-01-01    42    3
      2  Rob 2017-12-31    85    2
      3  Rob 2017-12-26    89    1
      4  Rob 2017-12-24    57    1
      5  Rob 2017-08-31    53    1
      6  Rob 2017-05-31    72    2
      7 Kate 2017-12-25    87    1
      8 Kate 2017-05-15    73    1
      

      【讨论】:

        【解决方案6】:

        tidyverse 解决方案(dplyr + tidyr):

        df %>%
          complete(Name,Date) %>%
          group_by(Name)      %>% 
          mutate(last_score_date = `is.na<-`(Date,is.na(Score))) %>%
          fill(Score,last_score_date) %>%
          filter(!is.na(Score) & Date-last_score_date <30) %>%
          group_by(Date) %>%
          mutate(Rank = rank(-Score)) %>%
          right_join(df)
        
        # # A tibble: 8 x 5
        # # Groups:   Date [?]
        # Name       Date Score last_score_date  Rank
        # <chr>     <date> <int>          <date> <dbl>
        # 1  John 2018-01-01    42      2018-01-01     3
        # 2   Rob 2017-12-31    85      2017-12-31     2
        # 3   Rob 2017-12-26    89      2017-12-26     1
        # 4   Rob 2017-12-24    57      2017-12-24     1
        # 5   Rob 2017-08-31    53      2017-08-31     1
        # 6   Rob 2017-05-31    72      2017-05-31     2
        # 7  Kate 2017-12-25    87      2017-12-25     1
        # 8  Kate 2017-05-15    73      2017-05-15     1
        
        • 我们添加DateName 的所有缺失组合
        • 然后我们为last_score_date 创建一个列,当分数不是NA 时等于Date
        • 通过填写 NAs 分数已成为最新分数
        • 我们过滤掉 NA,只保留小于 30 天的分数
        • 这是我们按日期列出的有效分数表
        • 从那里很容易添加等级
        • 原始表上的最终 right_join 为我们提供了预期的输出

        数据

        library(data.table)
        df <- fread('
                    Name   Score         Date   
                    John    42         01/01/2018  
                    Rob     85         12/31/2017
                    Rob     89         12/26/2017
                    Rob     57         12/24/2017
                    Rob     53         08/31/2017
                    Rob     72         05/31/2017
                    Kate    87         12/25/2017
                    Kate    73         05/15/2017
                    ')
        df[,Date:= as.Date(Date, format="%m/%d/%Y")]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-07-30
          • 2021-10-31
          • 2021-05-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多