答えではありませんが、問題を組み立てるのに役立つかもしれません。最悪の場合のパフォーマンスは、多くの短いグループを合計することであり、これはベクトルのサイズに比例してスケーリングするようです
> n = 100000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
user system elapsed
0.228 0.000 0.229
> n = 1000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
user system elapsed
1.468 0.040 1.514
> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f))
user system elapsed
17.369 0.748 18.166
再注文を避けて、2つのショートカットが利用できるようです
> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time(rowsum(x, f, reorder=FALSE))
user system elapsed
16.501 0.476 17.025
キャラクターへの内的強制を避ける
> n = 10000000; x = runif(n); f = as.character(sample(n/2, n, TRUE));
> system.time(rowsum(x, f, reorder=FALSE))
user system elapsed
8.652 0.268 8.949
そして、関連しているように見える基本的な操作-グループ化係数の一意の値を見つけ出し(結果ベクトルを事前に割り当てるため)、合計を実行します
> n = 10000000; x = runif(n); f = sample(n/2, n, TRUE)
> system.time({ t = tabulate(f); sum(x) })
user system elapsed
0.640 0.000 0.643
そうです、より高速な単一目的の実装にはかなりの余地があるようです。これは自然な for のように思わdata.table
れ、C で実装するのはそれほど難しくありません。R を使用して集計を行い、「従来の」C インターフェイスを使用して合計を行う混合ソリューションを次に示します。
library(inline)
rowsum1.1 <- function(x, f) {
t <- tabulate(f)
crowsum1(x, f, t)
}
crowsum1 = cfunction(c(x_in="numeric", f_in="integer", t_in = "integer"), "
SEXP res_out;
double *x = REAL(x_in), *res;
int len = Rf_length(x_in), *f = INTEGER(f_in);
res_out = PROTECT(Rf_allocVector(REALSXP, Rf_length(t_in)));
res = REAL(res_out);
memset(res, 0, Rf_length(t_in) * sizeof(double));
for (int i = 0; i < len; ++i)
res[f[i] - 1] += x[i];
UNPROTECT(1);
return res_out;
")
と
> system.time(r1.1 <- rowsum1.1(x, f))
user system elapsed
1.276 0.092 1.373
と同じ結果を実際に返すにはrowsum
、これを適切な次元名を持つマトリックスとして形成する必要があります
rowsum1 <- function(x, f) {
t <- tabulate(f)
r <- crowsum1(x, f, t)
keep <- which(t != 0)
matrix(r[keep], ncol=1, dimnames=list(keep, NULL))
}
> system.time(r1 <- rowsum1(x, f))
user system elapsed
9.312 0.300 9.641
そのため、すべての作業で 2 倍の速さしかありません (そして、一般性ははるかに低くなります。x は数値でなければならず、f は整数でなければなりません。NA 値はありません)。はい、たとえば、カウントのないスペース レベルを割り当てるなどの非効率性があります (ただし、これにより、名前の文字ベクトルへの高価な強制が回避されます)。