比应用 'ddply' 按组按函数聚合变量更快的方法

  • 本文关键字:变量 方法 函数 应用 ddply r
  • 更新时间 :
  • 英文 :


目的

我有事件ID(即oid)和与每个事件关联的二元级观察:代理(即aid),合作伙伴(即pid)。事件按事件发生的时间(即o4.in)排序。此外,我有一个变量来指示两列中的一对值是否出现在上一个事件中(即j2.consecutive)。

我使用ddply按组聚合虚拟变量,如以下示例中所述(例如,t2创建aj1.consecutiveapj1.consecutive)。但是,对于 4m 的观察,代码花费了太多时间,并且 Rstudio 在运行代码时经常终止。

除了应用ddply之外,是否有更快,更紧凑的方法来完成相同的任务?

数据

library(tidyverse)
library(tibble)
library(data.table)

rename <- dplyr::rename
select <- dplyr::select

set.seed(10001)
cases <- sample(1:5, 1000, replace=T)

set.seed(10002)
agent <- sample(1:20, 1000, replace=T)

set.seed(10003)
partner <- sample(1:20, 1000, replace=T)

set.seed(123)
n <- 1000  # no of random datetimes needed
minDate <- as.POSIXct("1999/01/01")
maxDate <- as.POSIXct("2000-01-01")

epoch <- "1970-01-01"
timestamps <- 
as.POSIXct(pmax(runif(n, minDate, maxDate), runif(n, minDate, maxDate)), origin = epoch)

df <-
data.frame(cases, agent, partner, timestamps) %>% 
rename(
aid = agent,
pid = partner,
oid = cases,
o4.in = timestamps
) %>% 
filter(aid != pid) %>%
arrange(o4.in) 
t <- setDT(df)[order(o4.in)]
t[, oid.lag.a := shift(oid), by = aid
][, oid.lag.p := shift(oid), by = pid]

t <- 
t[, j2.consecutive := fcoalesce(+(oid.lag.a == oid.lag.p), 0L)] %>% 
arrange(aid, o4.in)

当前方法

# aggregating the dummy variable by groups
t2 <-
t %>%
ungroup %>%
ddply(c('oid', 'aid'), function(i){
i %>%
mutate(aj1.consecutive = (sum(j2.consecutive) - j2.consecutive)/(n()-1))
} , .progress = 'text') %>%
arrange(oid, pid) %>%
ddply(c('oid', 'pid'), function(i){
i %>%
mutate(apj1.consecutive = (sum(j2.consecutive) - j2.consecutive)/(n()-1))
} , .progress = 'text') %>%
arrange(aid, o4.in)

更有效的data.table选项是使用:=set函数。 据?':='

set 是 := 的低开销可循环版本。它对于通过引用(使用 for 循环)重复更新某些列的行特别有用。

此外,基于?setorder

setorder(和

setorderv)根据提供的列(和列顺序)对data.table的行重新排序。它通过引用对表重新排序,因此非常节省内存。

下面的代码通过引用分配(:=),按"oid","aid"或"oid","pid"分组,并按setorder进行排序,从而使其更有效率。copy是在原始对象上进行的,因此在执行分配时不会更改它

library(data.table)
t3 <- copy(t)
t3[, aj1.consecutive :=  (sum(j2.consecutive) - 
j2.consecutive)/(.N-1), .(oid, aid)]
setorder(t3, oid, pid)
t3[, apj1.consecutive := (sum(j2.consecutive) - 
j2.consecutive)/(.N-1), .(oid, pid)]
setorder(t3, aid, o4.in)

- 检查OP的输出

all.equal(t2, t3, check.attributes = FALSE)
[1] TRUE

最新更新