4

R data.frame を、data.frame 内の変数の 1 つの累積合計と同じ量で集計したいと考えています。私はかなりグーグルで検索しましたが、おそらく何か役に立つものを見つけるための正しい用語を知りません.

この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 に減らしたいと思います。各行には、v の n 単位の量に対応する、v で重み付けされた p の加重平均が含まれます。この種の何か:


> 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

最初の質問です。上記のコードに対するより良い (より効率的なアプローチ) があるかどうか疑問に思っていました。より重要な 2 番目の質問は、より正確なバケットを取得するために上記のコードを修正する方法です。つまり、 の各行には、上記のようにおおよそではなく、正確に の単位がx.agg含まれている必要があります。たとえば、最初の行には の98 単位に対応する最初の 17 行の集計が含まれます。次の行 (18 番目) には の 5 単位が含まれており、次のバケットに完全に含まれています。代わりに達成したいのは、行 18 の 2 単位を最初のバケットに割り当て、残りの 3 単位を次のバケットに割り当てることです。100vxvv

提供されたヘルプに事前に感謝します。

4

2 に答える 2

3

正確なバケットを探している場合、p の値は 2 つの「分割」に対して同じであると想定しています。例では、最初のバケットに入る 18 行目の 2 単位の 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))

これにより、行ごとに p の各値が v 回繰り返されるベクトルが作成され、結果は unlist を使用して単一のベクトルに結合されます。

この集計の後、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
于 2013-02-26T18:15:08.373 に答える
3

毎回繰り返さずにこれを行う別の方法を次に示しp vます。そして、私が理解している方法は、それが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 秒かかります。

于 2013-02-26T21:04:49.253 に答える