ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 06.04.2021

Просмотров: 869

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

CIRCLE 2. GROWING OBJECTS

If you use too much memory, R will complain. The key issue is that R holds

all the data in RAM. This is a limitation if you have huge datasets. The up-side
is flexibility—in particular, R imposes no rules on what data are like.

You can get a message, all too familiar to some people, like:

Error: cannot allocate vector of size 79.8 Mb.

This is often misinterpreted along the lines of: “I have xxx gigabytes of memory,
why can’t R even allocate 80 megabytes?” It is because R has already allocated
a lot of memory successfully. The error message is about how much memory R
was going after at the point where it failed.

The user who has seen this message logically asks, “What can I do about

it?” There are some easy answers:

1. Don’t be a glutton by using bad programming constructs.

2. Get a bigger computer.

3. Reduce the problem size.

If you’ve obeyed the first answer and can’t follow the second or third, then
your alternatives are harder. One is to restart the R session, but this is often
ineffective.

Another of those hard alternatives is to explore where in your code the

memory is growing. One method (on at least one platform) is to insert lines
like:

cat(’point 1 mem’, memory.size(), memory.size(max=TRUE), ’

\

n’)

throughout your code. This shows the memory that R currently has and the
maximum amount R has had in the current session.

However, probably a more efficient and informative procedure would be to

use Rprof with memory profiling. Rprof also profiles time use.

Another way of reducing memory use is to store your data in a database and

only extract portions of the data into R as needed. While this takes some time
to set up, it can become quite a natural way to work.

A “database” solution that only uses R is to save (as in the

save

function)

objects in individual files, then use the files one at a time. So your code using
the objects might look something like:

for(i in 1:n)

{

objname <- paste(’obj.’, i, sep=’’)
load(paste(objname, ’.rda’, sep=’’))
the obj <- get(objname)
rm(list=objname)
# use the obj

}

15


background image

CIRCLE 2. GROWING OBJECTS

Are tomorrow’s bigger computers going to solve the problem? For some people,
yes—their data will stay the same size and computers will get big enough to
hold it comfortably. For other people it will only get worse—more powerful
computers means extraordinarily larger datasets. If you are likely to be in this
latter group, you might want to get used to working with databases now.

If you have one of those giant computers, you may have the capacity to

attempt to create something larger than R can handle. See:

?’Memory-limits’

for the limits that are imposed.

16


background image

Circle 3

Failing to Vectorize

We arrive at the third Circle, filled with cold, unending rain. Here stands
Cerberus barking out of his three throats. Within the Circle were the blas-
phemous wearing golden, dazzling cloaks that inside were all of lead—weighing
them down for all of eternity. This is where Virgil said to me, “Remember your
science—the more perfect a thing, the more its pain or pleasure.”

Here is some sample code:

lsum <- 0
for(i in 1:length(x))

{

lsum <- lsum + log(x[i])

}

No. No. No.

This is speaking R with a C accent—a strong accent. We can do the same

thing much simpler:

lsum <- sum(log(x))

This is not only nicer for your carpal tunnel, it is computationally much faster.
(As an added bonus it avoids the bug in the loop when

x

has length zero.)

The command above works because of vectorization. The

log

function is

vectorized in the traditional sense—it does the same operation on a vector of
values as it would do on each single value. That is, the command:

log(c(23, 67.1))

has the same result as the command:

c(log(23), log(67.1))

The

sum

function is vectorized in a quite different sense—it takes a vector and

produces something based on the whole vector. The command

sum(x)

is equiv-

alent to:

17


background image

CIRCLE 3. FAILING TO VECTORIZE

x[1] + x[2] + ... + x[length(x)]

The

prod

function is similar to

sum

, but does products rather than sums. Prod-

ucts can often overflow or underflow (a suburb of Circle 1)—taking logs and
doing sums is generally a more stable computation.

You often get vectorization for free. Take the example of

quadratic.formula

in Circle 1 (page

9

). Since the arithmetic operators are vectorized, the result of

this function is a vector if any or all of the inputs are. The only slight problem
is that there are two answers per input, so the call to

cbind

is used to keep

track of the pairs of answers.

In binary operations such as:

c(1,4) + 1:10

recycling automatically happens along with the vectorization.

Here is some code that combines both this Circle and Circle 2 (page

12

):

ans <- NULL
for(i in 1:507980)

{

if(x[i] < 0) ans <- c(ans, y[i])

}

This can be done simply with:

ans <- y[x < 0]

A double

for

loop is often the result of a function that has been directly trans-

lated from another language. Translations that are essentially verbatim are
unlikely to be the best thing to do. Better is to rethink what is happening with
R in mind. Using direct translations from another language may well leave you
longing for that other language. Making good translations may well leave you
marvelling at R’s strengths. (The catch is that you need to know the strengths
in order to make the good translations.)

If you are translating code into R that has a double

for

loop, think.

If your function is not vectorized, then you can possibly use the

Vectorize

function to make a vectorized version. But this is vectorization from an external
point of view—it is not the same as writing inherently vectorized code. The

Vectorize

function performs a loop using the original function.

Some functions take a function as an argument and demand that the function

be vectorized—these include

outer

and

integrate

.

There is another form of vectorization:

>

max(2, 100, -4, 3, 230, 5)

[1] 230
>

range(2, 100, -4, 3, 230, 5, c(4, -456, 9))

[1] -456 230

18


background image

CIRCLE 3. FAILING TO VECTORIZE

Figure 3.1: The hypocrites by Sandro Botticelli.

This form of vectorization is to treat the collection of arguments as the vector.
This is NOT a form of vectorization you should expect, it is essentially foreign to
R—

min

,

max

,

range

,

sum

and

prod

are rare exceptions. In particular,

mean

does

not adhere to this form of vectorization, and unfortunately does not generate
an error from trying it:

>

mean(2, -100, -4, 3, -230, 5)

[1] 2

But you get the correct answer if you add three (particular) keystrokes:

>

mean(c(2, -100, -4, 3, -230, 5))

[1] -54

One reason for vectorization is for computational speed. In a vector operation
there is always a loop. If the loop is done in C code, then it will be much faster
than if it is done in R code. In some cases, this can be very important. In
other cases, it isn’t—a loop in R code now is as fast as the same loop in C on
a computer from a few years ago.

Another reason to vectorize is for clarity. The command:

volume <- width * depth * height

19