如何基于data.table计算30天窗口内学生各时间点的成绩排名?
计算每个学生在30天时间窗口内的成绩排名
根据你提供的示例数据和需求,我整理了两种常见场景的实现方案,你可以根据实际需要选择:
方案1:每个学生自身历史30天内的成绩排名
这个方案只关注当前学生在自身过去30天内的成绩,对当前成绩做降序排名(排名从1开始,同分取最小排名),完美匹配你示例中Rob的Rank结果:
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天窗口内的排名 df <- df[order(Name, Date)] df[, Rank_self := sapply(.I, function(i) { current_date <- Date[i] current_score <- Score[i] # 筛选同学生30天内的所有成绩 window_scores <- Score[Date >= current_date - 30 & Date <= current_date] # 降序排名,ties.method控制同分处理逻辑 rank(-window_scores, ties.method = "min")[which(Score == current_score & Date == current_date)] }), by = Name] print(df)
运行后你会看到,Rob在2017-12-31的85分,在他自己的30天窗口(包含57、89、85三个分数)中排名第2,和你给出的示例Rank完全一致。
方案2:所有学生在30天窗口内的成绩排名
如果需要把所有学生的成绩都纳入30天窗口,计算当前成绩在全局范围内的排名,代码如下:
# 计算全局30天窗口内的排名 df <- df[order(Date)] df[, Rank_global := sapply(.I, function(i) { current_date <- Date[i] current_score <- Score[i] # 筛选所有学生30天内的成绩 window_scores <- df[Date >= current_date - 30 & Date <= current_date, Score] # 降序排名 rank(-window_scores, ties.method = "min")[which(window_scores == current_score & df[Date >= current_date - 30 & Date <= current_date, Date] == current_date)] })] print(df)
这个方案下,Rob在2017-05-31的72分,会和Kate在2017-05-15的73分一起参与排名,最终得到Rank2,和你示例中的值匹配。
优化说明:大数据集高效实现
如果你的数据量较大,上面的sapply方法效率会偏低,推荐用data.table的非等连接来优化:
# 高效非等连接实现全局排名 df[, Date_start := Date - 30] rank_df <- df[df, on = .(Date >= Date_start, Date <= Date), allow.cartesian = TRUE][ , .(Score, i.Score, i.Date), by = .EACHI][ , rank(-Score, ties.method = "min"), by = i.Date][ , .(Date = i.Date, Rank_global = V1)] df <- df[rank_df, on = .(Date)]
这种方式利用了data.table的向量化操作,处理大规模数据时速度会快很多。
内容的提问来源于stack exchange,提问作者gibbz00
相关产品推荐
相关产品推荐

