【问题标题】:Calculate second highest cumulative value by group按组计算第二高累积值
【发布时间】:2021-06-06 16:27:56
【问题描述】:

我有一个分组变量“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

【问题讨论】:

标签: r data.table


【解决方案1】:

这是使用非等连接的另一个选项:

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

【讨论】:

  • 我尝试啄赞成票,但它只允许一个。谢谢分享,真好!
  • 感谢@Henrik!。我认为您的解决方案更强大,因为它可以选择任何第 n 个值:)
  • 也许很健壮,但当优雅是关键时,这是一个糟糕的安慰。
  • 我们都渴望有趣的问题哈哈哈
  • @chinsoon12 这比上面的解决方案要快得多。这需要 7 秒,而 Henrik 的方法需要 450 秒。
【解决方案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]) 的序列。在每一步中,从向量开始到索引“i”的子集“x”(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 解决方案。

【讨论】:

    【解决方案3】:

    创建一个列长度为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)
    })
    

    【讨论】:

    • 抱歉,我在群组中需要这个,所以我更新了我的问题。否则效果很好。
    猜你喜欢
    • 2013-05-26
    • 2019-06-06
    • 2021-06-19
    • 2022-08-22
    • 2020-07-18
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    相关资源
    最近更新 更多