Referring to package and function as arguments in another function - r

I am trying to find methods for specific functions across different packages in R. For example methods(broom::tidy) will return all methods for the function tidy in the package broom. For my current issue it would be better if I could have the methods function in another function like so:
f1 <- function(x,y){
methods(x::y)
}
(I removed other parts of the code that are not relevant to my issue.)
However when I run the function like this:
f1 <- function(x,y){ methods(x::y)}
f1(broom,tidy)
I get the error
Error in loadNamespace(name) : there is no package called ‘x’
If I try to modify it as to only change the function but keep the package the same I get a similar error :
f2 <- function(y){ methods(broom::y)}
f2(tidy)
Error: 'y' is not an exported object from 'namespace:broom'
How can I get the package and function name to evaluate properly in the function? Does this current issue have to do with when r is trying to evaluate/substitute values in the function?

Both the :: and methods() functions use non-standard evaluation in order to work. This means you need to be a bit more clever with passing values to the functions in order to get it to work. Here's one method
f1 <- function(x,y){
do.call("methods", list(substitute(x::y)))
}
f1(broom,tidy)
Here we use substitute() to expand and x and y values we pass in into the namespace lookup. That solves the :: part which you can see with
f2 <- function(x,y){
substitute(x::y)
}
f2(broom,tidy)
# broom::tidy
We need the substitute because there could very well be a package x with function y. For this reason, variables are not expanded when using ::. Note that :: is just a wrapper to getExportedValue() should you otherwise need to extract values from namespaces using character values.
But there is one other catch: methods() doesn't evaluate it's parameters, it uses the raw expression to find the methods. This means we don't actually need the value of broom::tidy, we to pass that literal expression. Since we need to evaluate the substitute to get the expression we need, we need to build the call with do.call() in order to evaluate the substitute and pass that expression on to methods()

Related

how to specify a package or namespace for `rlang::exec`?

I am trying to write a function (within a package) which involves allowing the user to specify a function from a specific package to execute and provide additional arguments for that function.
I can do this in two steps with rlang::call2 as follows:
# `my_pkg` = "foo" (user-input)
# `my_fun` = "bar" (user-input)
# `args` is a named list (user-input)
the_call <- rlang::call2(.fn = my_fun , !!!args , .ns = my_pkg)
base::eval(the_call)
I have a feeling that the "correct" way to do this is to just use rlang::exec, but rlang::exec does not have a .ns argument like rlang::call2 does. Instead, it has an .env argument.
How do you specify the package (or namespace) of a function to rlang::exec? The .env argument to rlang::exec doesn't appear to be the answer, because the user should be able to specify a function in a specific package (that is installed) without first loading that package.
Or is rlang::exec not meant for this purpose?
Try
real_fun <- get(my_fun, envir=as.environment(paste0("package:", my_pkg)))
rlang::exec(real_fun, !!!args)
Since "packagename::functionname" (string) cannot be passed to rlang::exec, we can pass the real function. This can be retrieved by using get and passing a particular environment (from a package namespace, as above).
(This doesn't alleviate the need for two expressions to effect the exec, but it does allow one to use exec directly.)

How to declare S3 method to default to loaded environment?

In a package, I would like to call an S3 method "compact" for object foobar.
There would therefore be a compact.foobar function in my package, along with the compact function itself:
compact = function(x, ...){
UseMethod("compact", x)
}
However, this latter would be conflicting with purrr::compact.
I could default the method to use purrr (compact.default = purrr::compact, or maybe
compact.list = purrr::compact), but that would make little sense if the user does not have purrr loaded.
How can I default my method to the loaded version of compact, in the user environment? (so that it uses purrr::compact, any other declared compact function, or fails of missing function)
Unfortunately S3 does not deal with this situation well. You have to search for suitable functions manually. The following works, more or less:
get_defined_function = function (name) {
matches = getAnywhere(name)
# Filter out invisible objects and duplicates
objs = matches$objs[matches$visible & ! matches$dups]
# Filter out non-function objects
funs = objs[vapply(objs, is.function, logical(1L))]
# Filter out function defined in own package.
envs = lapply(funs, environment)
funs = funs[! vapply(envs, identical, logical(1L), topenv())]
funs[1L][[1L]] # Return `NULL` if no function exists.
}
compact.default = function (...) {
# Maybe add error handling for functions not found.
get_defined_function('compact')(...)
}
This uses getAnywhere to find all objects named compact that R knows about. It then filters out those that are not visible because they’re not inside attached packages, and those that are duplicate (this is probably redundant, but we do it anyway).
Next, it filters out anything that’s not a function. And finally it filters out the compact S3 generic that our own package defines. To do this, it compares each function’s environment to the package environment (given by topenv()).
This should work, but it has no rule for which function to prefer if there are multiple functions with the same name defined in different locations (it just picks an arbitrary one first), and it also doesn’t check whether the function signature matches (doing this is hard in R, since function calling and parameter matching is very flexible).

Are these strings or variables?

Coming from a C / Python / Java background, I have trouble understanding some R syntax, where literals look like variables, but seem to behave like strings. For example:
library(ggplot2)
library("ggplot2")
The two lines behave equivalently. However, I would expect the first line to mean "load the library whose name is stored in the ggplot2 variable" and give an error like object 'ggplot2' not found.
Speaking of ggplot2:
ggplot(data, aes(factor(arrivalRate), responseTime, fill=factor(mode))) +
geom_violin(trim=FALSE, position=dodge)
The variables arrivalRate, responseTime and mode do not exist, but somehow R knows to look them up inside the data data frame. I assume that aes actually receives strings, that are then processed using something like eval.
How does R parse code that it ends up interpreting some literals as strings?
promises
When an argument is passed to a function it is not passed as a value but is passed as a promise which consists of
the expression or code that the caller uses as the actual argument
the environment in which that expression is to be evaluated, viz. the caller's environment.
the value that the expression represents when the expression is evaluated in the promise's environment -- this slot is not filled in until the promise is actually evaluated. It will never be filled in if the function never accesses it.
The pryr package can show the info in a promise:
library(pryr)
g <- function(x) promise_info(x)
g(ggplot2)
giving:
$code
ggplot2 <-- the promise x represents the expression ggplot2
$env
<environment: R_GlobalEnv> <-- if evaluated it will be done in this environment
$evaled
[1] FALSE <-- it has not been evaluated
$value
NULL <-- not filled in because promise has not been evaluated
The only one of the above slots in the pryr output that can be accessed at the R level without writing a C function to do it (or using a package such as pryr that accesses such C code) is the code slot. That can be done using the R function substitute(x) (or other means). In terms of the pryr output substitute applied to a promise returns the code slot without evaluating the promise. That is, the value slot is not modified. Had we accessed x in an ordinary way, i.e. not via substitute, then the code would have been evaluated in the promise's environment, stored in the value slot and then passed to the expression in the function that accesses it.
Thus either of the following result in a character string representing what was passed as an expression, i.e. the character representation of the code slot, as opposed to its value.
f <- function(x) as.character(substitute(x))
f("ggplot2")
## [1] "ggplot2"
f(ggplot2)
## [1] "ggplot2"
library
In fact, library uses this idiom, i.e. as.character(substitute(x)), to handle its first argument.
aes
The aes function uses match.call to get the entire call as an expression and so in a sense is an alternative to substitute. For example:
h <- function(x) match.call()
h(pi + 3)
## h(x = pi + 3)
Note
One cannot tell without looking at the documentation or code of a function how it will treat its arguments.
An interesting quirk of the R language is the way it evaluates expressions. In most cases, R behaves the way you'd expect. Expressions in quotes are treated as strings, anything else is treated as a variable, function, or other token. But some functions allow for "non-standard evaluation", in which an unquoted expression is evaluated, more or less, as if it were a quoted variable. The most common example of this is R's way of loading libraries (which allows for unquoted or quoted library names) and its succinct formula interface. Other packages can take advantage of NSE. Hadley Wickham makes extensive use of it throughout his extremely popular tidyverse packages. Aside from saving the user a few characters of typing, NSE has a number of useful properties for dynamic programming.
As noted in the other answer, Wickham has an excellent tutorial on how it all works. RPubs user lionel also has a great working paper on the topic.
The concept is called "non-standard evaluation", and there are many different ways in which it can be used in different R functions. See this book chapter for an introduction.
This language feature can be confusing, and arguably is not needed for the library() function, but it allows incredibly powerful code when you need to specify computations on data frames, as is the case in ggplot2 or in dplyr, for example.
The lines
library(ggplot2)
library("ggplot2")
are not equivalent. In the first line, ggplot2 is a symbol, which may
or may not be bound to some value. In the second line, "ggplot2" is a
character vector of length one.
A function, however, can manipulate the arguments that it gets without
evaluating them, and can decide to treat both cases equivalently, which is what library does apparently.
Here's an example of how to manipulate an unevaluated expression:
> f <- function(x) match.call() # return unevaluated function call
> x <- f(foo)
> x
f(x = foo)
> mode(x)
[1] "call"
> x[[1]]
f
> x[[2]]
foo
> mode(x[[2]])
[1] "name"
> as.character(x[[2]])
[1] "foo"
> x <- f("foo")
> mode(x[[2]])
[1] "character"

I cannot find unique function from data.table package [duplicate]

help(unique) shows that unique function is present in two packages - base and data.table. I would like to use this function from data.table package. I thought that the following syntax - data <- data.table::unique(data) indicates the package to be used. But I get the following error -
'unique' is not an exported object from 'namespace:data.table'
But data <- unique(data) works well.
What is wrong here?
The function in question is really unique.data.table, an S3 method defined in the data.table package. That method is not really intended to be called directly, so it isn't exported. This is typically the case with S3 methods. Instead, the package registers the method as an S3 method, which then allows the S3 generic, base::unique in this case, to dispatch on it. So the right way to call the function is:
library(data.table)
irisDT <- data.table(iris)
unique(irisDT)
We use base::unique, which is exported, and it dispatches data.table:::unique.data.table, which is not exported. The function data.table:::unique does not actually exist (or does it need to).
As eddi points out, base::unique dispatches based on the class of the object called. So base::unique will call data.table:::unique.data.table only if the object is a data.table. You can force a call to that method directly with something like data.table:::unique.data.table(iris), but internally that will mostly likely result in the next method getting called unless your object is actually a data.table.
There are actually two infix operators in R that pull functions from particular package namespaces. You used :: but there is also a ::: that retrieves "unexported" functions. The unique-function is actually a family of functions and its behavior will depend on both the class of its argument and the particular packages that have been loaded. The R term of this is "generic". Try:
data <- data.table:::unique(data) # assuming 'data' is a data.table
The other tool that lets you peek behind the curtain that the lack of "exportation" is creating is the getAnywhere-function. It lets you see the code at the console:
> unique.data.table
Error: object 'unique.data.table' not found
> getAnywhere(unique.data.table)
A single object matching ‘unique.data.table’ was found
It was found in the following places
registered S3 method for unique from namespace data.table
namespace:data.table
with value
function (x, incomparables = FALSE, fromLast = FALSE, by = key(x),
...)
{
if (!cedta())
return(NextMethod("unique"))
dups <- duplicated.data.table(x, incomparables, fromLast,
by, ...)
.Call(CsubsetDT, x, which_(dups, FALSE), seq_len(ncol(x)))
}
<bytecode: 0x2ff645950>
<environment: namespace:data.table>

Extract argument code of a function in R/Subset function based on argument names

I am working with caret package. There are many functions which uses methods inside it. Example,
rfe
methods(rfe)
rfe.default
To check arguments of rfe.default we use args() function, or formalArgs() or formals()
args(rfe.default)
formalArgs(rfe.default)
formals(rfe.default)
Now, I just want to see the codes for each argument used inside rfe.default function, instead of whole function code which we get by typing
rfe.default
How to get the codes just for those arguments.
We can use deparse() function to get the range of lines from the function code. But is there any way to get the codes of arguments from the function based on argument names.
If an argument is using a function within it like here rfeContol = rfeControl()
I should be able to extract just this argument's code from rfe.default function instead of the whole code or rfe.default.
Thanks.

Resources