This question suggests using methods(class=class(obj)) to extract the list of methods that are available for an object.
If used on an object where length(class(obj))>1, this leads to lots of warnings, e.g.
set.seed(101)
a <- matrix(rnorm(20), nrow = 10)
b <- a + rnorm(length(a))
obj <- lm(b ~ a)
gives class(obj) as c("lm","mlm"); methods(class=class(obj)) gives
(many times)< Warning in grep(pattern, all.names, value = TRUE) :
argument 'pattern' has length > 1 and only the first element will be used
Warning in gsub(name, "", S3reg) :
argument 'pattern' has length > 1 and only the first element will be used
Warning in sub(paste0("\.", class, "$"), "", row.names(info)) :
argument 'pattern' has length > 1 and only the first element will be used
followed by the results.
It seems (?) that applying methods(class=...) to the last element of class(obj) would work, but I'm interested in alternatives or discussion as to why that is (or is not) correct ...
To clarify, I would like the returned value to be a (preferably unique) character vector, so that I can use something like if ("foo" %in% allmethods(obj)) to test for the availability of a specified method for the object ...
Is this what you're looking for?
sapply(class(obj), function(x) methods(class = x))
Note that the code below gives an error, since argument generic.function becomes mlm. It must be argument class like above.
sapply(class(obj), methods)
Error in .S3methods(generic.function, class, parent.frame()) :
no function 'mlm' is visible
Related
As applied to the same R code or objects, quote and substitute typically return different objects. How can one make this difference apparent?
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
> tmc <- function(X){
out <- list(typ = typeof(X), mod = mode(X), cls = class(X))
out
}
> df1 <- data.frame(a = 1, b = 2)
Here the printed output of quote and substitute are the same.
> quote(df1)
df1
> substitute(df1)
df1
And the structure of the two are the same.
> str(quote(df1))
symbol df1
> str(substitute(df1))
symbol df1
And the type, mode and class are all the same.
> tmc(quote(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
> tmc(substitute(df1))
$typ
[1] "symbol"
$mod
[1] "name"
$cls
[1] "name"
And yet, the outputs are not the same.
> is.identical(df1)
[1] FALSE
Note that this question shows some inputs that cause the two functions to display different outputs. However, the outputs are different even when they appear the same, and are the same by most of the usual tests, as shown by the output of is.identical() above. What is this invisible difference, and how can I make it appear?
note on the tags: I am guessing that the Common LISP quote and the R quote are similar
The reason is that the behavior of substitute() is different based on where you call it, or more precisely, what you are calling it on.
Understanding what will happen requires a very careful parsing of the (subtle) documentation for substitute(), specifically:
Substitution takes place by examining each component of the parse tree
as follows: If it is not a bound symbol in env, it is unchanged. If it
is a promise object, i.e., a formal argument to a function or
explicitly created using delayedAssign(), the expression slot of the
promise replaces the symbol. If it is an ordinary variable, its value
is substituted, unless env is .GlobalEnv in which case the symbol is
left unchanged.
So there are essentially three options.
In this case:
> df1 <- data.frame(a = 1, b = 2)
> identical(quote(df1),substitute(df1))
[1] TRUE
df1 is an "ordinary variable", but it is called in .GlobalEnv, since env argument defaults to the current evaluation environment. Hence we're in the very last case where the symbol, df1, is left unchanged and so it identical to the result of quote(df1).
In the context of the function:
is.identical <- function(X){
out <- identical(quote(X), substitute(X))
out
}
The important distinction is that now we're calling these functions on X, not df1. For most R users, this is a silly, trivial distinction, but when playing with subtle tools like substitute it becomes important. X is a formal argument of a function, so that implies we're in a different case of the documented behavior.
Specifically, it says that now "the expression slot of the promise replaces the symbol". We can see what this means if we debug() the function and examine the objects in the context of the function environment:
> debugonce(is.identical)
> is.identical(X = df1)
debugging in: is.identical(X = df1)
debug at #1: {
out <- identical(quote(X), substitute(X))
out
}
Browse[2]>
debug at #2: out <- identical(quote(X), substitute(X))
Browse[2]> str(quote(X))
symbol X
Browse[2]> str(substitute(X))
symbol df1
Browse[2]> Q
Now we can see that what happened is precisely what the documentation said would happen (Ha! So obvious! ;) )
X is a formal argument, or a promise, which according to R is not the same thing as df1. For most people writing functions, they are effectively the same, but the internal implementation disagrees. X is a promise object, and substitute replaces the symbol X with the one that it "points to", namely df1. This is what the docs mean by the "expression slot of the promise"; that's what R sees when in the X = df1 part of the function call.
To round things out, try to guess what will happen in this case:
is.identical <- function(X){
out <- identical(quote(A), substitute(A))
out
}
is.identical(X = df1)
(Hint: now A is not a "bound symbol in the environment".)
A final example illustrating more directly the final case in the docs with the confusing exception:
#Ordinary variable, but in .GlobalEnv
> a <- 2
> substitute(a)
a
#Ordinary variable, but NOT in .GlobalEnv
> e <- new.env()
> e$a <- 2
> substitute(a,env = e)
[1] 2
I have a larger section of code but I've narrowed down the problem to this -
So I want to return a concatenated list.
do.call(c,"X")
Error in do.call(c, "X") : second argument must be a list
So above it complains about the SECOND argument not being a list.
asimplelist=list(2,3,4)
class(asimplelist)
[1] "list"
do.call(c,asimplelist)
Error in do.call(c, asimplelist) :
'what' must be a function or character string
Why will this not return a concatenated list ? C is a legit function, and it's being passed a list?
args(do.call)
function (what, args, quote = FALSE, envir = parent.frame())
NULL
So "what" is the function argument it is complaining about.
I will answer "stealing" my answer from this comment by Nick Kennedy:
It might be better to put the c in double quotes.
If the user has a non-function named c in the global environment, do.call(c, dates) will fail with the error "Error in do.call(c, list(1:3)) : 'what' must be a character string or a function".
Clearly it may not be best practice to define c, but it's quite common for people to do a <- 1; b <- 2; c <- 3.
For most purposes, R still works fine in this scenario; c(1, 2) will still work, but do.call(c, x) won't.
Of course if the user has redefined c to be a function (e.g. c <- sum), then do.call will use the redefined function.
I am attempting to create a list of matrices containing iid Normal numbers. For the sake of a simple example, let the matrices be 4 by 2 and consider a list of length 3. The following code seemed like it should work (to me):
MyMatrix <- lapply(1:3, function() {matrix(rnorm(8), 4, 2)})
But it failed, with the following error:
Error in FUN(1:3[[1L]], ...) : unused argument (1:3[[1]])
On a whim, I tried:
MyMatrix <- lapply(1:3, function(x) {matrix(rnorm(8), 4, 2)})
And it worked! But why? x is not used anywhere in the function, and on experimentation, the behaviour of the expression is not affected by whether x already exists in the workspace or not. It appears to be entirely superfluous.
I am new to R, so I would be very grateful if an experienced user could explain what is going on here and why my first line fails.
You can't have a function that doesn't take arguments and then pass it arguments. Which is exactly what you are doing when you run lapply, as each value is passed in turn as the first argument to the function. E.g.
out <- lapply(1:3, function(x) x)
str(out)
#List of 3
# $ : int 1
# $ : int 2
# $ : int 3
Simple example throwing an error:
test <- function() {"woot"}
test()
#[1] "woot"
test(1)
#Error in test(1) : unused argument (1)
lapply(1:3, test)
#Error in FUN(1:3[[1L]], ...) : unused argument (1:3[[1]])
It's good form for R to error out, as it likely means you're expecting the function's returned result to change based on the arguments passed to the function. And it wouldn't. There are functions like this included in base R, like Sys.time(), which will fail if you try to pass it superfluous arguments which might otherwise make sense:
Sys.time()
#[1] "2014-07-07 13:22:11 EST"
Sys.time(tz="UTC")
#Error in Sys.time(tz = "UTC") : unused argument (tz = "UTC")
I am confused by how list(...) works in R and S-plus. For following code
pp <- function(x, ...) { print( length( list(...)))}
pp(1,,,1)
In S-Plus, it works, but in R, it gives "Error in print(length(list(...))) : argument is missing, with no default"
I am more interest in how this works in R and how to get the value of list(...) in R functions.
I'm not sure why that syntax is allowed in S-plus but not in R.
Here, though, is some R code that will have essentially the same effect:
pp <- function(x, ...) {
print(length(as.list(match.call(expand.dots=FALSE))[["..."]]))
}
pp(1,,,1)
# [1] 3
Alternatively, using a trick from here:
ppp <- function(x, ...) {
print(length(substitute(...())))
}
ppp(1,,,1)
# [1] 3
You cannot use unnamed (edit: ... and missing) arguments in the dots and any arguments after the dots when these arguments are intended to be matched in an arg-list. The positional matching that automatically occurs for unnamed arguments only "works" in the typical argument processing for named arguments(in the arg list) before the dots.
> pp1 <- function(x, ...) { length( list(...))}
> pp1(1,z=NULL,xx=NULL,1)
[1] 3
> pp2 <- function(x, z, ...) { length( list(...))}
> pp2(1,z=NULL,xx=NULL,1)
[1] 2
> pp3 <- function(x, z, ...) { length( list(...))}
> pp3(1, ,xx=NULL,1)
[1] 2
> pp <- function(x, ...) { length( list(...))}
> pp(1, , xx=NULL, 1)
Error in pp(1, , xx = NULL, 1) : argument is missing, with no default
Reading the help page for match.call, the second "commonly used circumstance" is described as:
To pass most of the call to another function, often model.frame. Here the common idiom is that expand.dots = FALSE is used, and the ... element of the matched call is removed.
The sequence of argument matching (when not circumvented) is described in section 4.3.2 "Argument matching":
Positional matching. Any unmatched formal arguments are bound to unnamed supplied arguments, in order. If there is a ‘...’ argument, it will take up the remaining arguments, tagged or not.
If any arguments remain unmatched an error is declared.
I am using stopifnot and I understand it just returns the first value that was not TRUE. I f that is some freaky dynamic expression someone who is not into the custom function cannot really make something out of that. So I would love to add a custom error message. Any suggestions?
Error: length(unique(nchar(check))) == 1 is not TRUE
Basically states that the elements of the vector check do not have the same length.
Is there a way of saying: Error: Elements of your input vector do not have the same length!?
Use stop and an if statement:
if(length(unique(nchar(check))) != 1)
stop("Error: Elements of your input vector do not have the same length!")
Just remember that stopifnot has the convenience of stating the negative, so your condition in the if needs to be the negation of your stop condition.
This is what the error message looks like:
> check = c("x", "xx", "xxx")
> if(length(unique(nchar(check))) != 1)
+ stop("Error: Elements of your input vector do not have the same length!")
Error in eval(expr, envir, enclos) :
Error: Elements of your input vector do not have the same length!
A custom message can be added as a label to your expression:
stopifnot("Elements of your input vector do not have the same length!" =
length(unique(nchar(check))) == 1)
# Error: Elements of your input vector do not have the same length!
The assertive and assertthat packages have more readable check functions.
library(assertthat)
assert_that(length(unique(nchar(check))) == 1)
## Error: length(unique(nchar(check))) == 1 are not all true.
library(assertive)
assert_is_scalar(unique(nchar(check)))
## Error: unique(nchar(check)) does not have length one.
if(!is_scalar(unique(nchar(check))))
{
stop("Elements of check have different numbers of characters.")
}
## Error: Elements of check have different numbers of characters.
Or you could package it up.
assert <- function (expr, error) {
if (! expr) stop(error, call. = FALSE)
}
So you have:
> check = c("x", "xx", "xxx")
> assert(length(unique(nchar(check))) == 1, "Elements of your input vector do not have the same length!")
Error: Elements of your input vector do not have the same length!
What about embedding the stopifnot into tryCatch and then recasting the exception with stop using customized message?
Something like:
tryCatch(stopifnot(...,error=stop("Your customized error message"))
Unlike some other solutions this does not require additional packages. Compared to using if statement combined with stop you retain the performance advantages of stopifnot, when you use new R versions. Since R version 3.5.0 stopifnot evaluates expressions sequentially and stops on first failure.
I would recommend you check out Hadley's testthat package. It allows for intuitive testing: the names of the functions are great and the way you write them is like a sentence -- "I expect that length(unique(nchar(check))) is [exactly|approximately] 1". The errors produced are informative.
See here:
http://journal.r-project.org/archive/2011-1/RJournal_2011-1_Wickham.pdf
In your case,
> library(testthat)
> check = c("x", "xx", "xxx")
> expect_that(length(unique(nchar(check))), equals(1))
Error: length(unique(nchar(check))) not equal to 1
Mean relative difference: 2
Also note that you don't have the problem that #Andrie referenced with sometimes having to think about double negatives with stopifnot. I know it seems simple, but it caused me many headaches!
The answers already provided are quite good, and mine is just an addition to that collection. For some people it could be more convenient to use one-liner in form of the following function:
stopifnotinform <- function(..., message = "err") {
args <- list(...)
if (length(args) == 0) {
return(NULL)
}
for (i in 1:length(args)) {
result <- args[[i]]
if (is.atomic(result) && result == FALSE) {
stop(message)
}
}
}
# throws an error
stopifnotinform(is.integer(1L), is.integer(2), message = "Some number(s) provided is not an integer")
# continues with execution
stopifnotinform(is.integer(1L), is.integer(2L), message = "Some number(s) provided is not an integer")
Bear in mind that this solution provides you with only one (common) error message for all parameters in ....
Try this:
same_length <- FALSE
stopifnot("Elements of your input vector do not have the same length!" = same_length)
#> Error : Elements of your input vector do not have the same length!