knitr::opts_chunk$set(message=FALSE, warning=FALSE)
Here we show a simple example of the gains in efficiency that can be achieved by taking advantage of matrix operations rather than multiple calls to a function. For more on the topic, see this RCS module.
In this example, we compare three approaches to computing the row sums
of a large matrix. In the first (inefficient) approach, we make
use of a double for
loop. In the second (still inefficient) approach, we
make use of the function apply
. In the third (most
efficient) approach, we make use of the cartesian product operator
%*%
.
for
loopset.seed(123) # for reproducible results N <- 10000000 # 10^7 X <- matrix(rnorm(N),nrow=N/10,ncol=10) # a 10^6-by-10 matrix ## use of for loop forSum <- function(X) { rowS <- rep(0,nrow(X)) for ( i in 1:nrow(X) ) for ( j in 1:ncol(X) ) rowS[i] <- rowS[i] + X[i,j] return( rowS ) } Tfor <- system.time(tmp0 <- forSum(X)) print(Tfor)
apply
## use of apply function Tsum <- system.time(tmp1 <- apply(X,1,sum)) print(Tsum)
%*%
)## use of matrix multiplication In <- rep(1,10) # define a unit vector Tprd <- system.time(tmp2 <- X %*% In) print(Tprd) ## let's measure the speed-up Tsum["sys.self"]/Tprd["sys.self"] # CPU Tsum["user.self"]/Tprd["user.self"] # CPU + R/W Tfor["user.self"]/Tprd["user.self"] # CPU + R/W
As you can see, the matrix-based sum achieves a r round(Tsum["user.self"]/Tprd["user.self"])
-fold speed-up relative to the apply
-based implementation, and a r round(Tfor["user.self"]/Tprd["user.self"])
-fold speed-up relative to the for loop
-based implementation.]
It should be noted that there are actually native R functions for the
efficient sum of a matrix rows and columns (rowSums
and
colSums
). However, the point of this basic demonstration holds.
The gain in efficiency is due to the fact that in the for
loop formulation, all the operations are performed in R (which is an interpreted language, hence inefficient), and in the apply
formulation there are 1M function calls (1M calls of the sum
function), which is also inefficient. In the %*%
formulation, there is a single R function call, and all the number crunching is performed in whatever (compiled) language the %*%
is implemented, most likely Fortran.
In fact, notice that the gain in efficiency of the %*%
over apply
is largely lost when computing the column sum, since there are only 10 columns, hence only ten calls of the sum
function. The gain in efficiency with respect to the for
loop is still there.
## use of apply function T3 <- system.time(tmp3 <- forSum(t(X))) print(T3)
## use of apply function T4 <- system.time(tmp4 <- apply(X,2,sum)) print(T4)
## use of matrix multiplication In <- rep(1,1000000) # define a unit vector T5 <- system.time(tmp5 <- t(X) %*% In) print(T5)
As noted, cognizant of this issue, R provides efficient functions to perform row and column sums.
T6 <- system.time(rowSums(X)) print(T6)
T7 <- system.time(colSums(X)) print(T7)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.