Не ответ, но может быть полезен для формулировки проблемы. Похоже, что в худшем случае производительность заключается в суммировании множества коротких групп, и это, похоже, линейно масштабируется с размером вектора.
> 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
Кажется, есть два коротких пути, позволяющих избежать повторного заказа
> 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
так что да, похоже, что есть много возможностей для более быстрой одноцелевой реализации. Это кажется естественным для 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). Да, есть неэффективность, например, выделение уровней пространства, которые не имеют счетчиков (хотя это позволяет избежать дорогостоящего приведения к вектору символов для имен).
person
Martin Morgan
schedule
07.06.2013
rowsum
для вектора, когда он предназначен для использования с матрицами. Вы не предложили код Cpp. - person IRTFM   schedule 07.06.2013rowsum
отправляетrowsum.default
в приведенном выше случае, и это уже вызывает код C, поэтому он уже должен быть достаточно быстрым. Возможно, вы сможете получить небольшое улучшение скорости, напрямую вызвавrowsum.default
или.Internal(rowsum_matrix(...))
, хотя последнее не рекомендуется и не разрешено в CRAN. - person G. Grothendieck   schedule 07.06.2013data.table
мог бы преуспеть... - person Paul Hiemstra   schedule 07.06.2013