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

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

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

Добавлен: 06.04.2021

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

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

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

8.1. GHOSTS

CIRCLE 8. BELIEVING IT DOES AS INTENDED

8.1.37

string not the name

If you have a character string that contains the name of an object and you want
the object, then use

get

:

funs <- c(’mean’, ’median’)
get(funs[2])(data)

If you found

as.name

and thought that would solve your problem, you were

right but you need one more step:

eval(as.name(funs[2]))(data)

8.1.38

get a component

The

get

function is extremely powerful, but it is not clairvoyant. If you say:

get(’myobj$comp’)

it thinks (quite rightly) you are asking for an object named ’myobj$comp’. If
you want the

comp

component of

myobj

, you have to say:

get(’myobj’)$comp

8.1.39

string not the name (encore)

If you have a character string that contains the name of a component that you
want to extract from a list, then you can not use the

8

$

8

operator. You need

to use

8

[[

8

:

>

mylist <- list(aaa=1:5, bbb=letters)

>

subv <- ’aaa’

>

mylist$subv

NULL
>

# the next three lines are all the same

>

mylist$aaa

[1] 1 2 3 4 5
>

mylist[[’aaa’]]

[1] 1 2 3 4 5
>

mylist[[subv]]

[1] 1 2 3 4 5

8.1.40

string not the name (yet again)

If you create a character string with

paste

and you want that to be the name

of an object, you can not use that on the left of an assignment:

>

paste(’x’, 1, sep=’’) <- 3:5

Error: Target of assignment expands to non-language object

65


background image

8.1. GHOSTS

CIRCLE 8. BELIEVING IT DOES AS INTENDED

But

assign

will do this:

for(i in 1:n) assign(paste(’obj’, i, sep=’.’), mylist[[i]])

WARNING: An operation like this can land you in Circle 3 ([page

17

failing to

vectorize—this example is anti-vectorizing) and/or the heresy of Circle 6 (page

35

).

As we have just seen with

get

, assigning to a name like

’myobj$comp’

is

not going to get you where you want to go—it will create an object with a non-
standard name rather than modifying the

comp

component of

myobj

. Create a

copy of the object, change the component in the copy, then assign the name to
the copy.

8.1.41

string not the name (still)

A formula can easily be created out of a character string:

>

myvars <- paste(’V’, 1:9, sep=’’)

>

myvars

[1] "V1" "V2" "V3" "V4" "V5" "V6" "V7" "V8" "V9"
>

as.formula(paste(’y ~ ’, paste(myvars[c(3,5,8)],

+

collapse=’ + ’)))

y ~ V3 + V5 + V8

8.1.42

name not the argument

You may want to produce a plot or other output that states the dataset that
was used. You might try something like:

myfun <- function(x, ...)

{

plot(x, main=x, ...)

}

But that is going to produce a less than pleasing main title. The

substitute

function along with

deparse

is probably what you are looking for.

myfun2 <- function(x, ...)

{

plot(x, main=deparse(substitute(x)), ...)

}

66


background image

8.1. GHOSTS

CIRCLE 8. BELIEVING IT DOES AS INTENDED

8.1.43

unexpected else

Error: unexpected ’else’ in "else"

If you aren’t expecting ’else’ in ’else’, then where would you expect it?

While you may think that R is ludicrous for giving you such an error message,

R thinks you are even more ludicrous for expecting what you did to work.

R takes input until it gets a complete statement, evaluates that statement,

then takes more input. Here is how to get that error:

if(any(abs(x) > 1)) atan(x)
else asin(x)

When R gets to the end of the first line, it has a perfectly complete statement
so it evaluates it. Now it finds a statement starting with ’else’—this makes no
sense. Proper formatting is the key. If it is convenient, you can put the whole
statement on one line:

if(any(abs(x) > 1)) atan(x) else asin(x)

Alternatively, use curly braces (and a well-placed ’else’):

if(any(abs(x) > 1))

{

atan(x)

}

else

{

asin(x)

}

8.1.44

dropping dimensions

>

xmat <- array(1:4, c(2,2))

>

xmat[1,] # simple vector, not a matrix

[1] 1 3
>

xmat[1, , drop=FALSE] # still a matrix

[,1] [,2]

[1,]

1

3

By default dimensions of arrays are dropped when subscripting makes the di-
mension length 1. Subscripting with

drop=FALSE

overrides the default.

NOTE: Failing to use

drop=FALSE

inside functions is a major source of bugs.

You only test the function when the subscript has length greater than 1. The
function fails once it hits a situation where the subscript is length 1—somewhere
downstream a matrix is expected and there is a simple vector there instead.

NOTE: Failing to use

drop=FALSE

inside functions is a major source of bugs.

67


background image

8.1. GHOSTS

CIRCLE 8. BELIEVING IT DOES AS INTENDED

8.1.45

drop data frames

The

drop

function has no effect on a data frame. If you want dropping to be

done in a data frame, you need to use the

drop

argument in subscripting.

Dropping in data frames can be surprising (but it is logical).

>

xdf <- data.frame(a=1:2, b=c(’v’, ’w’))

>

xdf[1,] # data frame

a b

1 1 v
>

drop(xdf[1,]) # data frame

a b

1 1 v
>

xdf[1, , drop=TRUE] # list

$a
[1] 1
$b
[1] v
Levels: v w
>

xdf[,1] # numeric vector

[1] 1 2
>

xdf[, 1, drop=FALSE] # data frame

a

1 1
2 2
>

drop(xdf[, 1, drop=FALSE]) # data frame

a

1 1
2 2

8.1.46

losing row names

The row names of a data frame are lost through dropping:

>

xdf2 <- data.frame(a=1:4, b=42:45,

+

row.names=LETTERS[1:4])

>

xdf2[, 1]

[1] 1 2 3 4
>

as.matrix(xdf2)[, 1]

A B C D
1 2 3 4

Coercing to a matrix first will retain the row names, but possibly at the expense
of not getting the right values if the column types of the data frame are mixed.

>

xdf2b <- data.frame(a=1:4, b=letters[21:24],

+

row.names=LETTERS[1:4])

68


background image

8.1. GHOSTS

CIRCLE 8. BELIEVING IT DOES AS INTENDED

>

as.matrix(xdf2b)[,1]

A

B

C

D

"1" "2" "3" "4"
>

drop(as.matrix(xdf2b[, 1, drop=FALSE]))

A B C D
1 2 3 4

The final incantation, though a bit complicated, will give you the right thing.

8.1.47

apply function returning a vector

If you use

apply

with a function that returns a vector, that becomes the first

dimension of the result. This is likely not what you naively expect if you are
operating on rows:

>

matrix(15:1, 3)

[,1] [,2] [,3] [,4] [,5]

[1,]

15

12

9

6

3

[2,]

14

11

8

5

2

[3,]

13

10

7

4

1

>

apply(matrix(15:1, 3), 1, sort)

[,1] [,2] [,3]

[1,]

3

2

1

[2,]

6

5

4

[3,]

9

8

7

[4,]

12

11

10

[5,]

15

14

13

The naive expectation is really arrived at with:

t(apply(matrix(15:1, 3), 1, sort))

But note that no transpose is required if you operate on columns—the naive
expectation holds in that case.

8.1.48

empty cells in tapply

If there are combinations of levels that do not appear, then

tapply

gives

NA

as

the answer (or

NULL

if

simplify=FALSE

):

>

tapply(9, factor(1, levels=1:2), sum)

1

2

9 NA

>

tapply(9, factor(1, levels=1:2), sum, simplify=FALSE)

$

8

1

8

[1] 9
$

8

2

8

NULL

69