这里有两个data.table 替代方案。如上,计算了累积平均值,但这里使用了base和data.table函数。
setDT(df)[order(level), .(level = unique(level),
mean_value = (cumsum(value) / .I)[!duplicated(level, fromLast = TRUE)])]
setDT(df)[order(level), .(level = unique(level),
mean_value = (cumsum(value) / .I)[rev(rowid(rev(level))) == 1])]
# level mean_value
# 1: A1 1.0000000
# 2: A2 0.7142857
# 3: A3 0.6000000
虽然速度对 OP 来说不是问题,但对于未来的访问者来说,值得注意的是,即使在一个适度的数据集上,200 个组,每个组 200 行,这些替代方案都比tidyverse 代码快得多。
为基准测试准备数据,200 组,每组 200 行:
ng = 2e2
n = 2e2
set.seed(1)
df = data.frame(value = rbinom(ng * n, 1, 0.5),
level = rep(seq(ng), each = n)
df3 = df
df4 = df
基准测试:
microbenchmark(
f1 = {
d1 = df %>%
arrange(level) %>%
mutate(mean_value = map_dbl(.x = seq_along(level), ~ mean(value[1:.x]))) %>%
group_by(level) %>%
summarise(mean_value = last(mean_value))
},
f2 = {
d2 = df %>%
arrange(level) %>%
summarise(mean_value = cummean(value)[!duplicated(level, fromLast = TRUE)],
level = unique(level)) %>%
select(level, mean_value)
},
f3 = {
d3 = setDT(df3)[order(level), .(level = unique(level), mean_value = (cumsum(value) / .I)[!duplicated(level, fromLast = TRUE)])]
},
f4 = {
d4 = setDT(df4)[order(level), data.table(level = level, mean_value = cumsum(value) / .I)[rev(rowid(rev(level))) == 1]]
},
times = 10L)
结果:
# Unit: milliseconds
# expr min lq mean median uq max neval
# f1 2834.601301 2869.658601 3052.233971 3030.448801 3191.671301 3303.532801 10
# f2 127.474801 130.049101 138.202461 135.293550 141.301601 173.230901 10
# f3 2.929702 3.401802 3.706061 3.450152 3.838801 5.585200 10
# f4 3.888700 3.984301 4.372161 4.166951 4.844901 5.198001 10
all.equal(as.data.frame(d1), d2)
all.equal(as.data.frame(d1), as.data.frame(d3))
all.equal(as.data.frame(d1), as.data.frame(d4))
all.equal(as.data.frame(d1), as.data.frame(d4))
all.equal(as.data.frame(d1), as.data.frame(d5))
# TRUE
另一种方法是非等值连接。在这种特殊情况下会慢一些,与累积平均技巧竞争,但它更通用,可以处理更复杂的函数。
setDT(df)[ , level := factor(level)]
df[.(level = sort(unique(df$level))), on = .(level <= level),
.(mean_value = mean(value)), by = .EACHI]