我有一个非常大的数据框,我的目标是按用户ID列出累计美元。数据框看起来像这样,但它要大得多:
dt<-sample(seq(as.Date("2013-01-01"),as.Date("2013-05-01"),by="days"),10)
s<-c(rep(5252525,5),rep(1313131,5))
usd<-round(rnorm(10,100),2)
money<-data.frame(dt,s,usd)
money<-money[order(money$dt),]
money$Cumulative<-NA
users<-unique(money$s)
我开始使用for循环,但速度很慢:
for (i in 1:length(users)){
temp=which(money$s==users[i])
money$Cumulative[temp]=cumsum(money$usd[temp])
}
我在StackOverflow上读到我可以使用data.table来提高整体速度,这有点帮助:
money<-data.table(money)
setkey(money,s)
for (i in 1:length(users)){
temp=which(money$s==users[i])
money$Cumulative[temp]=cumsum(money$usd[temp])
}
我想让这个计算更快。接下来我该怎么办?
答案 0 :(得分:5)
由于money
列已经对dt
进行了排序,您可以使用ave
:
money$Cumulative <- ave(money$usd, money$s, FUN=cumsum)
或者您可以使用data.table:
moneyDT <- as.data.table(money[,1:3])
moneyDT[,cumulative := cumsum(usd), by=s]
答案 1 :(得分:1)
听起来你正在寻找data.table
选项。
使用Joshua提出的方法并指出使用玩具数据可能会产生误导(即类似的方法对于最小数据集lil_money
执行类似,但对于更实际的数据集money
则不同):
Unit: microseconds
expr min lq median uq max neval
useAve(lil_money) 694.269 730.491 741.358 756.753 13687.951 1000
useBy(lil_money) 709.664 748.603 759.470 775.770 5341.338 1000
Unit: milliseconds
expr min lq median uq max neval
useAve(money) 3940.8970 3966.0950 4002.4319 4090.3967 4145.2672 10
useBy(money) 105.7129 106.5789 109.6566 117.2939 122.1414 10
Identical output: TRUE
require(microbenchmark)
require(data.table)
start <- as.Date("2001-01-01")
money <- CJ(s=1:1e4, dt=start + 0:1e3)[, usd := runif(.N)]
lil_money <- money[s < 10 & dt < start + 10]
useAve <- function(DT) { DT[, cum_ave := ave(usd, s, FUN=cumsum)] }
useBy <- function(DT) { DT[, cum_by := cumsum(usd), by=s] }
print(microbenchmark(useAve(lil_money), useBy(lil_money), times=1000))
print(microbenchmark(useAve(money), useBy(money), times=10))
cat("Identical output:", identical(money$cum_ave, money$cum_by))