Welcome to R Square

根据区间匹配符合条件的字段值

楚新元 / 2025-03-17


最近在知乎上看到一个问题:Excel中如何在范围内带出相应的值?,具体需求如下:

主播的奖励系数根据有效时长和有效开播日两个因素按照就近就低的原则确定,而这两个奖励标准是一个区间范围,如下表所示。现在需要根据主播实际的直播数据计算他们的奖励系数。
例如:主播开播 16 小时,有效开播日 8 天,则主播得到的奖励系数为 0.0003;主播开播 18 小时,有效开播日 2 天,则主播得到的奖励系数为 0;主播开播 30 小时,有效开播日 8 天,则主播得到的奖励系数仍为 0.0003。

有效时长 有效开播日 奖励系数
[0,15) [0,7) 0.0000
[15,20) [7,10) 0.0003
[20,25) [10,12) 0.0004
[25,40) [12,15) 0.0005
[40,60) [15,20) 0.00055
[60,$+\infty$) [20,$+\infty$) 0.0006

创建示例数据

# 加载相关 R 包
library(lubridate)
library(dplyr)

# 奖励标准
dict = data.frame(
  hours = c(0, 15, 20, 25, 40, 60), 
  days = c(0, 7, 10, 12, 15, 20), 
  reward_coeff = c(0, 0.0003, 0.0004, 0.0005, 0.00055, 0.0006)
)

# 主播实际直播数据
data = data.frame(
  ID = c("A", "B", "C", "D"), 
  hours = c(
    "89小时48分钟7秒", 
    "16小时23分钟34秒", 
    "18小时45分钟13秒", 
    "30小时32分钟2秒"
  ), 
  days = c(19, 8, 2, 8)
)

梳理解决思路

我们首先需要把奖励标准里有效时长和有效开播日的临界点找出来,然后根据主播的实际数据和这个临界点比较,看落在了哪个区间里,其次分别根据主播的有效时长和有效开播日确定的区间得到两个奖励系数,最后取这两个奖励系数的最小值即可。

代码实现

data |> 
  mutate(
    hours = hms(hours) / hours(1),
    reward_coeff = pmin(
      dict$reward_coeff[findInterval(hours, dict$hours)], 
      dict$reward_coeff[findInterval(days, dict$days)]
    )
  ) |> 
  rename(
    `主播ID` = ID,
    `有效时长` = hours,
    `有效开播日` = days,
    `奖励系数` = reward_coeff
  ) -> result
print(result)
#>   主播ID 有效时长 有效开播日 奖励系数
#> 1      A 89.80194         19  0.00055
#> 2      B 16.39278          8  0.00030
#> 3      C 18.75361          2  0.00000
#> 4      D 30.53389          8  0.00030

以上代码可能不是很优雅,因为如果奖励标准有很多个,那么 pmin() 函数里就需要把每个条件都罗列出来,因此,更优雅的方法应该是写一个函数,然后利用 purrr 包批量对每一列的数据进行判断。另外,知乎上张敬信老师给出的解决思路用到了非等连接,也是很巧妙的方法,供参考。