按组计算第二大累积值

时间:2021-03-08 19:14:03

标签: r data.table

我有一个带有分组变量“grps”和值“x”的数据。我已经计算了每个组“cmx”中的 cummax。现在我需要找到每个组中“x”的第二个最高累积值,scmx

一些数据,包括所需的列scmx

library(data.table)
d = structure(list(date = structure(rep(c(18690, 18691, 18692, 18693, 18694, 18695, 18696, 18697), 2), class = "Date"),
                   x = c(18, 70, 57, 94, 94, 13, 98, 23, 20, 72, 59, 96, 96, 15, 100, 25),
                   grps = c(rep("g1", 8), rep("g2", 8))),
              row.names = c(NA, -16L), class = c("data.table", "data.frame"))
d[, cmx := cummax(x), by = .(grps)]
d[, scmx := c(18, 18, 57, 70, 70, 70, 94, 94, 20, 20, 59, 72, 72, 72, 96, 96)]

上下文

如果 x 对应于绩效评级,我想要做的是找出他们达到最佳绩效和次佳绩效的日期。我的一个类似问题,我需要找到与列中最高累积值相对应的行:

Fill down first row within each cumulative max, with a twist

3 个答案:

答案 0 :(得分:2)

一个 data.table 替代方案:

d[ , scmx2 := {
  c(x[1], sapply(seq(.N)[-1], function(i){
    v = x[1:i]
    v[frank(-v, ties.method = "dense") == 2][1]
  }))
}, by = grps]

#           date   x grps cmx scmx scmx2
#  1: 2021-03-04  18   g1  18   18    18
#  2: 2021-03-05  70   g1  70   18    18
#  3: 2021-03-06  57   g1  70   57    57
#  4: 2021-03-07  94   g1  94   70    70
#  5: 2021-03-08  94   g1  94   70    70
#  6: 2021-03-09  13   g1  94   70    70
#  7: 2021-03-10  98   g1  98   94    94
#  8: 2021-03-11  23   g1  98   94    94
#  9: 2021-03-04  20   g2  20   20    20
# 10: 2021-03-05  72   g2  72   20    20
# 11: 2021-03-06  59   g2  72   59    59
# 12: 2021-03-07  96   g2  96   72    72
# 13: 2021-03-08  96   g2  96   72    72
# 14: 2021-03-09  15   g2  96   72    72
# 15: 2021-03-10 100   g2 100   96    96
# 16: 2021-03-11  25   g2 100   96    96

在每个组 (by = grps) 内,循环 (sapply) 从 2 到当前组中的行数 (seq(.N)[-1]) 的序列。在每一步中,子集 'x' 从向量开始到索引 'i' (v = x[1:i])。

计算稠密秩并检查秩是否为2(frank(-v, ties.method = "dense") == 2),即第二大数的秩。使用逻辑索引对 'v' (v[...) 进行子集化。选择第一个匹配项([1];如果有多个值为 2 的值)。将此“扩展窗口”的结果与“x”的第一个元素 (c(x[1], ...) 连接起来。


在第一个窗口中,只有一个值,显然没有第二高的值。这里 OP 选择返回第一个值。对于所有值都相等的较长窗口,也需要做出相同的选择,这将在有相等值的前导运行时发生。如果我们宁愿返回 NA 而不是第一个值,那么替换行中的 x[1]

c(x[1], sapply(seq(.N)[-1], function(i){

...与NA_real_

小演示:

d = data.table(grps = c(1, 1, 2, 2, 2), x = c(3, 3, 4, 4, 5)) 

d[ , scmx2 := {
  c(NA_real_, sapply(seq(.N)[-1], function(i){
    v = x[1:i]
    v[frank(-v, ties.method = "dense") == 2][1]
  }))
}, by = grps]

#    grps x scmx
# 1:    1 3   NA # grp 1: all values equal in all windows -> all NA
# 2:    1 3   NA
# 3:    2 4   NA
# 4:    2 4   NA  
# 5:    2 5    4 # grp 2: only the last window has a second highest value  

这个问题确实类似于我上面链接的帖子(Finding cumulative second max per group in R)。但是,在这里 OP 要求提供 data.table 解决方案。

答案 1 :(得分:2)

这是使用非对等连接的另一种选择:

d[, s2 := .SD[.SD, on=.(grps, date<=date, x<cmx), by=.EACHI, max(x.x)]$V1]
d[is.na(s2), s2 := x][]

输出:

          date   x grps cmx scmx s2
 1: 2021-03-04  18   g1  18   18 18
 2: 2021-03-05  70   g1  70   18 18
 3: 2021-03-06  57   g1  70   57 57
 4: 2021-03-07  94   g1  94   70 70
 5: 2021-03-08  94   g1  94   70 70
 6: 2021-03-09  13   g1  94   70 70
 7: 2021-03-10  98   g1  98   94 94
 8: 2021-03-11  23   g1  98   94 94
 9: 2021-03-04  20   g2  20   20 20
10: 2021-03-05  72   g2  72   20 20
11: 2021-03-06  59   g2  72   59 59
12: 2021-03-07  96   g2  96   72 72
13: 2021-03-08  96   g2  96   72 72
14: 2021-03-09  15   g2  96   72 72
15: 2021-03-10 100   g2 100   96 96
16: 2021-03-11  25   g2 100   96 96

答案 2 :(得分:1)

创建一个长度为 x 列的序列。将该函数应用于 x 中从索引 1 到序列中当前数字的每个序列,只关心唯一值。 Rfast::nth 可用于取向量中的第二大数字。

library(Rfast)
sapply(seq(length(d$x)), function(x) {
  return(nth(unique(d$x[1:x]), 2, descending=TRUE))
})

[1] 2.652495e-315  1.800000e+01  5.700000e+01  7.000000e+01
[5]  7.000000e+01  7.000000e+01  9.400000e+01  9.400000e+01

为新数据框执行此操作。我们仍然可以使用上面创建的函数。排列数据框,使组名和值位于各自的列中,然后使用 lapplyrollapplyr 来捕获第二大唯一值。

d1=d %>% select(-cmx) %>% 
  pivot_wider(names_from=grps, values_from=x)
lapply(d1[-1], function(x) {
  my_list=rollapplyr(x, seq(length(x)), function(x) {return(nth(sort(unique(x), decreasing=TRUE), 2))})
  return(my_list)
})