R中data.table如何计算含平局逻辑的行内第n大值以统计赛事利润
锦标赛选手总盈利统计实现方案
核心逻辑
涉及的平局排名、奖金平分问题,本质是体育赛事通用的并列排名奖金池分配规则:
- 先按得分从高到低确定并列组,同得分选手归为同一组
- 每组对应的排名区间为:从组内最高排名(即该组得分对应的最小排名)开始,连续覆盖该组人数个排名位置
- 将区间内所有排名对应的奖金总和归集为该组的总奖金池,平均分配给组内每位选手
- 最后按选手维度汇总所有轮次的分配奖金,即为总盈利
该方案完全替代之前用do.call(pmax, .SD)的实现逻辑,无需手动判断各名次、处理平局场景。
高性能data.table实现(适配10万行规模数据)
首先明确奖金基准:对应第1-4名的单轮奖金为c(20, 10, 5, 0),后续可根据规则调整。
library(data.table) # 基础奖金池:索引对应排名1、2、3、4的单轮奖金 prize_by_rank <- c(20, 10, 5, 0) # 导入示例数据 df <- data.table('player_1' = c(10, 9, 5, 6, 7), 'player_2' = c(12, 9, 7, 6, 5), 'player_3' = c(6, 10, 6, 5, 12), 'player_4' = c(4, 8, 10, 5, 12)) # 新增轮次编号,区分每一行对应的赛事轮次 df[, round_id := .I] # 宽表转长表,适配逐轮逐选手的分组计算 long_dt <- melt(df, id.vars = "round_id", variable.name = "player", value.name = "score") # 按轮次计算标准竞争排名(同得分取最小排名值,完美匹配并列规则) long_dt[, rank := frank(-score, ties.method = "min"), by = round_id] # 按轮次+相同得分分组,计算单轮每位选手的分配奖金 long_dt[, round_prize := sum(prize_by_rank[rank[1]:(rank[1] + .N -1)]) / .N, by = .(round_id, score)] # 汇总每位选手的总盈利 total_profit <- long_dt[, .(total_profit = sum(round_prize)), by = player]
代码运行结果验证
total_profit # player total_profit # 1: player_1 37.5 # 2: player_2 52.5 # 3: player_3 47.5 # 4: player_4 37.5
该结果与手动逐轮计算的结果完全一致:
- 第1轮:player2拿第一名20美元、player1第二名10美元、player3第三名5美元、player4无奖金
- 第2轮:player3独自第一名拿20美元,player1、player2并列第二平分15美元奖金,各得7.5美元,player4无奖金
- 第3轮:player4第一名20美元、player2第二名10美元、player3第三名5美元、player1无奖金
- 第4轮:player1、player2并列第一平分30美元总奖金各得15美元,player3、player4并列第三平分5美元奖金各得2.5美元
- 第5轮:player3、player4并列第一平分30美元总奖金各得15美元,player1第三名5美元、player2无奖金
性能说明
整套流程全部基于data.table的分组优化实现,无逐行循环操作,10万行规模数据的计算耗时仅为毫秒级,完全满足大数据量的处理需求。如果需要适配data.frame格式,仅需将data.table对象转为标准data.frame,核心分组逻辑使用dplyr的group_by、min_rank类函数即可实现等价效果。
内容的提问来源于stack exchange,提问作者hotbacon
相关产品推荐
相关产品推荐

