2013-02-26 82 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