对于分组数据帧(dplyr)R中的每个元素,值的总和大于或等于

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解决方案,我会同样感激!

Moo*_*per 9

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)


Max*_*Max 8

不太紧凑,有点棘手,但速度更快,仅使用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)

  • 我没有考虑保留行顺序.谢谢 ! (2认同)