我有一个带有分组变量“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
答案 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
为新数据框执行此操作。我们仍然可以使用上面创建的函数。排列数据框,使组名和值位于各自的列中,然后使用 lapply
和 rollapplyr
来捕获第二大唯一值。
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)
})