Joh*_* K. 10 r dplyr data.table
我有一个相对较大的数据帧(~2,000,000行),对于每一行,我需要在该观察组中计算大于或等于当前行值的每个值的总和.
这是一个示例数据框:
sample_df = data.frame(
group_id = c(1,1,1,1,2,2,2,2),
value = c(10,12,14,12,8,8,21,10)
)
Run Code Online (Sandbox Code Playgroud)
我目前有一个非常缓慢的解决方案,使用循环和一些过滤来做到这一点,但是,更好的解决方案是更优选的.我一直在尝试使用dplyr,但我无法弄清楚如何在数据分组后得到其他观察值的总和.
通过上面的玩具示例,这里将是所需的输出:
desired_output = data.frame(
group_id = c(1,1,1,1,2,2,2,2),
value = c(10,12,14,12,8,8,21,10),
output = c(38,26,0,26,39,39,0,21)
)
Run Code Online (Sandbox Code Playgroud)
为了找到已经发布的解决方案,我没有看到一个明确的答案,它解释了如何将一组中的每个观察结果与其他观察结果进行比较,并按照某些标准对该组进行筛选.我更喜欢基于dplyr的解决方案,但如果有高效的base-R或data.table解决方案,我会同样感激!
用tidyverse.诀窍是用来map_dbl循环每一个value.
library(tidyverse)
sample_df %>%
group_by(group_id) %>%
mutate(output= map_dbl(value,~sum(value[value>=.x]))-value) %>%
ungroup
# A tibble: 8 x 3
group_id value output
<dbl> <dbl> <dbl>
1 1 10 38
2 1 12 26
3 1 14 0
4 1 12 26
5 2 8 39
6 2 8 39
7 2 21 0
8 2 10 21
Run Code Online (Sandbox Code Playgroud)
value在mutate行中是你的value'subcolumn'(组),而.x它是你循环的元素.
基础解决方案
within(sample_df,output <- unlist(tapply(
value,group_id,function(x) sapply(x,function(y) sum(x[x>=y])-y))))
# group_id value output
# 1 1 10 38
# 2 1 12 26
# 3 1 14 0
# 4 1 12 26
# 5 2 8 39
# 6 2 8 39
# 7 2 21 0
# 8 2 10 21
Run Code Online (Sandbox Code Playgroud)
不太紧凑,有点棘手,但速度更快,仅使用data.table.
诀窍在于,一旦您的数据按值降序排序,group_id您需要计算的每个数据都是一个group_id非常快的累积总和.
每当value组中出现多次时,您希望保留最后累积的总和,该总和已考虑到之前发生的所有事件.
library(data.table)
DT=as.data.table(sample_df)[order(group_id,-value),]
DT[,output:=cumsum(value)-value,keyby=.(group_id)]
temp=DT[, .SD[.N], by=.(group_id,value)] # Keep the last row by group and value
DT=merge(setDF(sample_df)[,.(group_id,value)],temp,by=c("group_id","value"),sort=F)
# group_id value output
# 1: 1 10 38
# 2: 1 12 26
# 3: 1 12 26
# 4: 1 14 0
# 5: 2 8 39
# 6: 2 8 39
# 7: 2 10 21
# 8: 2 21 0
Run Code Online (Sandbox Code Playgroud)
该解决方案4000比为10^6观测基准提出的替代解决方案快一倍.它可以10^8在不到一分钟的时间内完成观察.
# N data.table.trick dplyr sapply base
#1: 1e+06 0.067678928 secs 261.32966185 secs 282.639625 secs 275.08949995 secs
#2: 1e+05 0.013036013 secs 3.55517507 secs 5.356922 secs 3.36490607 secs
#3: 1e+04 0.007019043 secs 0.09926391 secs 0.312326 secs 0.04562092 secs
Run Code Online (Sandbox Code Playgroud)
我sys.Time()使用以下基准计算时间:
N=10^8 # observation
G=20 # group
V=100 # values
sample_df = data.table(
group_id = sample(1:G,N,replace=T),
value = sample(1:V,V,replace=T)
)
Run Code Online (Sandbox Code Playgroud)