2013-02-26 86 views
4

我想通過等於data.frame中的一個變量的累積總和來聚合R data.frame。我搜索了很多,但可能我不知道正確的術語來找到有用的東西。等分桶聚合數據框

假設我有此data.frame:


> x <- data.frame(cbind(p=rnorm(100, 10, 0.1), v=round(runif(100, 1, 10)))) 
> head(x) 
      p v 
1 10.002904 4 
2 10.132200 2 
3 10.026105 6 
4 10.001146 2 
5 9.990267 2 
6 10.115907 6 
7 10.199895 9 
8 9.949996 8 
9 10.165848 8 
10 9.953283 6 
11 10.072947 10 
12 10.020379 2 
13 10.084002 3 
14 9.949108 8 
15 10.065247 6 
16 9.801699 3 
17 10.014612 8 
18 9.954638 5 
19 9.958256 9 
20 10.031041 7 

我想在x減少到一個較小的data.frame其中每一行包含p的加權平均值,通過V加權,對應於量n個單位這種訴的東西。


> n <- 100 
> cum.v <- cumsum(x$v) 
> f <- cum.v %/% n 
> x.agg <- aggregate(cbind(v*p, v) ~ f, data=x, FUN=sum) 
> x.agg$'v * p' <- x.agg$'v * p'/x.agg$v 
> x.agg 
    f  v * p v 
1 0 10.039369 98 
2 1 9.952049 94 
3 2 10.015058 104 
4 3 9.938271 103 
5 4 9.967244 100 
6 5 9.995071 69 

第一個問題,我不知道是否有一個更好的(更有效的方法),以上面的代碼。第二,更重要的問題是如何糾正上面的代碼以獲得更精確的分流。也就是說,x.agg中的每一行都應包含v的單位100單位,而不僅僅是大致如上所述。例如,第一行包含對應於98個單位v的前17行x的合計。下一行(第18行)包含5個單位v,完全包含在下一個存儲桶中。我想要取而代之的是將第18行的2個單元歸入第一個桶,其餘3個單元歸入下一個。

在此先感謝您提供的任何幫助。

回答

3

後這種聚集是微不足道的下面是另一個與出重複每個pv次執行此方法。我理解的方式是,它橫渡100的地方(見下文)

18 9.954638 5 98 
19 9.958256 9 107 

應改爲:

18 9.954638 5 98 
19.1 9.958256 2 100 # ---> 2 units will be considered with previous group 
19.2 9.958256 7 107 # ----> remaining 7 units will be split for next group 

代碼:

n <- 100 
# get cumulative sum, an id column (for retrace) and current group id 
x <- transform(x, cv = cumsum(x$v), id = seq_len(nrow(x)), grp = cumsum(x$v) %/% n) 

# Paste these two lines in R to install IRanges 
source("http://bioconductor.org/biocLite.R") 
biocLite("IRanges") 

require(IRanges) 
ir1 <- successiveIRanges(x$v) 
ir2 <- IRanges(seq(n, max(x$cv), by=n), width=1) 
o <- findOverlaps(ir1, ir2) 

# gets position where multiple of n(=100) occurs 
# (where we'll have to do something about it) 
pos <- queryHits(o) 
# how much do the values differ from multiple of 100? 
val <- start(ir2)[subjectHits(o)] - start(ir1)[queryHits(o)] + 1 
# we need "pos" new rows of "pos" indices 
x1 <- x[pos, ] 
x1$v <- val # corresponding values 
# reduce the group by 1, so that multiples of 100 will 
# belong to the previous row 
x1$grp <- x1$grp - 1 
# subtract val in the original data x 
x$v[pos] <- x$v[pos] - val 
# bind and order them  
x <- rbind(x1,x) 
x <- x[with(x, order(id)), ] 
# remove unnecessary entries 
x <- x[!(duplicated(x$id) & x$v == 0), ] 
x$cv <- cumsum(x$v) # updated cumsum 

x$id <- NULL 
require(data.table) 
x.dt <- data.table(x, key="grp") 
x.dt[, list(res = sum(p*v)/sum(v), cv = tail(cv, 1)), by=grp] 

運行您的數據:

# grp  res cv 
# 1: 0 10.037747 100 
# 2: 1 9.994648 114 

運行在@ geektrader的數據:

# grp  res cv 
# 1: 0 9.999680 100 
# 2: 1 10.040139 200 
# 3: 2 9.976425 300 
# 4: 3 10.026622 400 
# 5: 4 10.068623 500 
# 6: 5 9.982733 562 

這裏有一個比較大的數據的基準:

set.seed(12345) 
x <- data.frame(cbind(p=rnorm(1e5, 10, 0.1), v=round(runif(1e5, 1, 10)))) 

require(rbenchmark) 
benchmark(out <- FN1(x), replications=10) 

#   test replications elapsed relative user.self 
# 1 out <- FN1(x)   10 13.817  1 12.586 

這需要對1E5行約1.4秒

3

如果您正在尋找精確的鏟裝,我假設P的值相同2「分裂」 V 即在你的榜樣,2臺排第18的是走在第一個P桶值爲9.954638

有了上述的前提下,你可以做以下不超大規模數據集..

> set.seed(12345) 
> x <- data.frame(cbind(p=rnorm(100, 10, 0.1), v=round(runif(100, 1, 10)))) 
> z <- unlist(mapply(function(x,y) rep(x,y), x$p, x$v, SIMPLIFY=T)) 

這將創建與各行的對重複v次,每次值向量和結果使用不公開合併成單一的載體。

aggregate使用功能

> aggregate(z, by=list((1:length(z)-0.5)%/%100), FUN=mean) 
    Group.1   x 
1  0 9.999680 
2  1 10.040139 
3  2 9.976425 
4  3 10.026622 
5  4 10.068623 
6  5 9.982733 
+0

哪裏可以找到這個'index'函數? – Arun 2013-02-26 21:11:50

+1

@阿倫哦,我的壞。我認爲它是已經加載的xts的功能。它可以用rownames代替,結果仍然有效。甚至seq從1到nrow(z) – 2013-02-27 00:48:49