ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.04.2021
Просмотров: 901
Скачиваний: 1
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
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
] failing to
vectorize—this example is anti-vectorizing) and/or the heresy of Circle 6 (page
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
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
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
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