[R] fast rowCumsums wanted for calculating the cdf
William Dunlap
wdunlap at tibco.com
Fri Oct 15 18:59:00 CEST 2010
> -----Original Message-----
> From: r-help-bounces at r-project.org
> [mailto:r-help-bounces at r-project.org] On Behalf Of Joshua Wiley
> Sent: Friday, October 15, 2010 12:23 AM
> To: Gregor
> Cc: r-help at r-project.org
> Subject: Re: [R] fast rowCumsums wanted for calculating the cdf
>
> Hi,
>
> You might look at Reduce(). It seems faster. I converted the matrix
> to a list in an incredibly sloppy way (which you should not emulate)
> because I cannot think of the simple way.
>
> > probs <- t(matrix(rep(1:10000000), nrow=10)) # matrix with
> row-wise probabilites
> > F <- matrix(0, nrow=nrow(probs), ncol=ncol(probs));
> > F[,1] <- probs[,1,drop=TRUE];
> > add <- function(x) {Reduce(`+`, x, accumulate = TRUE)}
> >
> >
> > system.time(F.slow <- t(apply(probs, 1, cumsum)))
> user system elapsed
> 36.758 0.416 42.464
> >
> > system.time(for (cc in 2:ncol(F)) {
> + F[,cc] <- F[,cc-1,drop=TRUE] + probs[,cc,drop=TRUE];
> + })
> user system elapsed
> 0.980 0.196 1.328
> >
> > system.time(add(list(probs[,1], probs[,2], probs[,3],
> probs[,4], probs[,5], probs[,6], probs[,7], probs[,8],
> probs[,9], probs[,10])))
> user system elapsed
> 0.420 0.072 0.539
One way to avoid the typing of
list(probs[,1], probs[,2], ...)
is to use split(probs, col(probs)). However, split() is
surprisingly slow in this case.
Another approach is to use a matrix multiply, by a ncol(probs)
by ncol(probs) matrix with 0's in lower triangle and 1's
elsewhere. Here are 4 approaches I've seen mentioned,
made into functions that output the matrix requested:
f1 <- function (x) t(apply(x, 1, cumsum))
f2 <- function (x){
if (ncol(x) > 1)
for (i in 2:ncol(x)) x[, i] <- x[, i] + x[, i - 1]
x
}
f3 <- function (x)
matrix(unlist(Reduce(`+`, split(x, col(x)), accumulate = TRUE)),
ncol = ncol(x))
f4 <- function (x) x %*% outer(seq_len(ncol(x)), seq_len(ncol(x)), "<=")
I tested it as follows
> probs <- matrix(runif(1e7), ncol=10, nrow=1e6)
> system.time(v1 <- f1(probs))
user system elapsed
19.45 0.25 16.78
> system.time(v2 <- f2(probs))
user system elapsed
0.68 0.03 0.79
> system.time(v3 <- f3(probs))
user system elapsed
38.25 0.24 34.47
> system.time(v4 <- f4(probs))
user system elapsed
0.70 0.05 0.56
> identical(v1,v2) && identical(v1,v3) && identical(v1,v4)
[1] TRUE
If you have a fancy optimized/multithreaded BLAS linked
to your version of R there you may find that %*% is
much faster (I'm using the precompiled R for Windows).
As ncol(x) gets bigger the %*% approach probably loses
its advantage.
Bill Dunlap
Spotfire, TIBCO Software
wdunlap tibco.com
> >
>
> .539 seconds for 10 vectors each with 1 million elements does not seem
> bad. For 30000 each, on my system it took .014 seconds, which for
> 200,000 iterations, I estimated as:
>
> > (200000*.014)/60/60
> [1] 0.7777778
>
> (and this is on a stone age system with a single core processor and
> only 756MB of RAM)
>
> It should not be difficult to get the list back to a matrix.
>
> Cheers,
>
> Josh
>
>
>
> On Thu, Oct 14, 2010 at 11:51 PM, Gregor <mailinglist at gmx.at> wrote:
> > Dear all,
> >
> > Maybe the "easiest" solution: Is there anything that speaks
> against generalizing
> > cumsum from base to cope with matrices (as is done in matlab)? E.g.:
> >
> > "cumsum(Matrix, 1)"
> > equivalent to
> > "apply(Matrix, 1, cumsum)"
> >
> > The main advantage could be optimized code if the Matrix is
> extreme nonsquare
> > (e.g. 100,000x10), but the summation is done over the short
> side (in this case 10).
> > apply would practically yield a loop over 100,000 elements,
> and vectorization w.r.t.
> > the long side (loop over 10 elements) provides considerable
> efficiency gains.
> >
> > Many regards,
> > Gregor
> >
> >
> >
> >
> > On Tue, 12 Oct 2010 10:24:53 +0200
> > Gregor <mailinglist at gmx.at> wrote:
> >
> >> Dear all,
> >>
> >> I am struggling with a (currently) cost-intensive problem:
> calculating the
> >> (non-normalized) cumulative distribution function, given
> the (non-normalized)
> >> probabilities. something like:
> >>
> >> probs <- t(matrix(rep(1:100),nrow=10)) # matrix with
> row-wise probabilites
> >> F <- t(apply(probs, 1, cumsum)) #SLOOOW!
> >>
> >> One (already faster, but for sure not ideal) solution -
> thanks to Henrik Bengtsson:
> >>
> >> F <- matrix(0, nrow=nrow(probs), ncol=ncol(probs));
> >> F[,1] <- probs[,1,drop=TRUE];
> >> for (cc in 2:ncol(F)) {
> >> F[,cc] <- F[,cc-1,drop=TRUE] + probs[,cc,drop=TRUE];
> >> }
> >>
> >> In my case, probs is a (30,000 x 10) matrix, and i need to
> iterate this step around
> >> 200,000 times, so speed is crucial. I currently can make
> sure to have no NAs, but
> >> in order to extend matrixStats, this could be a nontrivial issue.
> >>
> >> Any ideas for speeding up this - probably routine - task?
> >>
> >> Thanks in advance,
> >> Gregor
> >>
> >> ______________________________________________
> >> R-help at r-project.org mailing list
> >> https://stat.ethz.ch/mailman/listinfo/r-help
> >> PLEASE do read the posting guide
> http://www.R-project.org/posting-guide.html
> >> and provide commented, minimal, self-contained, reproducible code.
> >
> > ______________________________________________
> > R-help at r-project.org mailing list
> > https://stat.ethz.ch/mailman/listinfo/r-help
> > PLEASE do read the posting guide
> http://www.R-project.org/posting-guide.html
> > and provide commented, minimal, self-contained, reproducible code.
> >
>
>
>
> --
> Joshua Wiley
> Ph.D. Student, Health Psychology
> University of California, Los Angeles
> http://www.joshuawiley.com/
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide
> http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.
>
More information about the R-help
mailing list