Loading multiple libraries simultaneously - r

I am trying to write a function to load several libraries at once. Through other forums and threads I have found that this works:
read_library <- function(...) {
x <- c(...)
invisible(lapply(x, library, character.only = TRUE))
}
However, this forces me to write my libraries between "", i.e.:
read_library("tidyverse",
"readr",
"infer",
"modelr",
"statip",
"knitr",
"rmdformats")
Which I would like to avoid. Is there anything I could add to my function that would eliminate this first world problem?

Another way could be using alist with eval and substitute like below:
read_library <- function(...) {
obj <- eval(substitute(alist(...)))
#print(obj)
return(invisible(lapply(obj, function(x)library(toString(x), character.only=TRUE))))
}
read_library(gtools, ggplot2, tidyverse)
alist handles its arguments as if they described function arguments. So the values are not evaluated together with substitute it returns un-evaluated expression. Once we have the expression, we use eval to get a list of objects of class name, so that we can parse it as string in lapply.

You could use substitute with list like :
read_library <- function(...) {
invisible(lapply(substitute(list(...))[-1], function(x)
library(deparse(x), character.only = TRUE)))
}
read_library(tidyverse,
readr,
infer,
modelr,
knitr,
scales)

Related

Using R, in a function how to reference one or more functions by either the fn.name (as a string) or the fn.obj?

Sometimes in R a function wants a string or sometimes it wants an object.
For example, rm(x); and rm("x"); work the same.
NOTE: In this example x or "x" is NOT a function. I generically call it an OBJECT. In the example below, I am referring to functions as FN.STR or FN.OBJ, but the QUESTION is looking for a general OBJECT MANIPULATOR. Given a thing, determine if it is a string or object, and return a string/object as requested by the Function. A function then serves as a general API to access R objects.
> rm
function (..., list = character(), pos = -1, envir = as.environment(pos),
inherits = FALSE)
{
dots <- match.call(expand.dots = FALSE)$...
if (length(dots) && !all(vapply(dots, function(x) is.symbol(x) ||
is.character(x), NA, USE.NAMES = FALSE)))
stop("... must contain names or character strings")
names <- vapply(dots, as.character, "")
if (length(names) == 0L)
names <- character()
list <- .Primitive("c")(list, names)
.Internal(remove(list, envir, inherits))
}
<bytecode: 0x00000136a0f22d80>
<environment: namespace:base>
This is useful on this function because the USER doesn't have to remember: do I need the string or object. In fact, you can mix: x=1; y=2; rm(x,"y"); The ... dots have been expended to make this happen... Ideally, it would be nice to save the ... dots for passing parameters through to other functions or for lazy loading like sum allows: sum(1,2,3) == sum(c(1,2,3)). [Or maybe ..1 could be reimagined to allow for multiple dots: ...a, ...b, {ldots}, ...z where the name/order of the ...{letter} would allow lots of match.call magic. Regardless, even this magic is happening in the function, not as a standalone VARIADIC external magicFunction]
Objective
Anyway, I am trying to write a few variadic functions that allow the USER to input either the FUN as fn.str or as fn.obj. At the GLOBAL scope, the following will work:
str.fromObjectName = function(objname)
{
res = deparse(substitute(objname));
res = str.replace('"', "", res);
return(res);
}
WHERE str.replace is a extension of gsub, so for simplicity, let's say:
str.replace = function(search, replace, str)
{
gsub(search, replace, str, fixed=TRUE);
}
So if it is an object, I get the string.name of it (whether or not it actually exists, no error thrown). If it is a string, it has an extra " wrapping it, so the str.replace takes care of that.
As a procedural function, I would like to be able to access this in OTHER functions. But with the nature of the R stack (or my lack of understanding), I can't seem to get it to work. I found a solution in some baseR code that I have applied to use the ... dots to TRAP the potential object. But it has some limitations: I have to use the ... dots so I can't use them for other purposes, and if I call a function from a function the evaluation of the original function name gets lost, so I apply the character.only=FALSE when I call the function INTERNALLY to the other function where at some point the FN.OBJ was converted to FN.STR
So if I review the base packages with character.only I believe the help or library uses it to trap the pkg as a string or object. Maybe the solution is trivial which I am missing, but as I review the base code, it seems like it may be a challenge. It appears the function doesn't know what to do automatically without the character.only flag.
> library
function (package, help, pos = 2, lib.loc = NULL, character.only = FALSE,
...
if (!character.only)
package <- as.character(substitute(package))
...
else if (!missing(help)) {
if (!character.only)
help <- as.character(substitute(help))
...
else invisible(.packages())
}
<bytecode: 0x0000013699060b10>
<environment: namespace:base>
An example
Here is a preamble of one function:
function.info = function(..., character.only=FALSE)
{
if(character.only)
{
fn.str = unlist(list(...));
} else {
fn.str = str.fromObjectName(...);
}
}
NOTE: the ... passthrough allows the GLOBAL function to correctly scope.
This allows the function str "sum" or the function object sum to be inputed into the function (making life BETTER for the user). In the spirit of DRY and VARIADIC programming, it would be nice if I could do this as one external function to function.info ... and allow multiple fn objects to be passed in as parameters that are either the str "sum" or the object sum which INTERNALLY for most purposes I just want the resulting str.
Question
Given a function, how to allow a user to pass multiple FUN elements as either obj/string (mix allowed) using an external function (DRY = don't repeat yourself). In the example, I am referring to FUN.OBJ, but the goal would be to return anything that could be called an OBJ in R, not just a function.
magicFunction = function(FUN.OBJ.OR.STR, return="STR")
{
# do something here ... FUN.OBJ could be any R.OBJ
FUN.AS.STR;
# or if return = "OBJ"
FUN.AS.OBJ
}
v.math = function(data=c("#abcdef","#123456"), FUN, param="hi", FUN.pre="hex2dec", FUN.post=dec2hex)
{
# takes input [whether a str/obj] and returns a string/object.
fn.str = magicFunction(FUN);
fn.pre = magicFunction(FUN.pre);
fn.post = magicFunction(FUN.post);
# get to the main event
}
In the above, "hex2dec" is a string (as in "foo") and dec2hex is an object (as in bar): both referring to functions [something akin to match.fun(base::sum) or match.fun("base::sum"); I think currently it only searches the TOP of the stack.]. I can call my function str.fromObjectName on them in the GLOBAL sphere and get what I want, but when placed inside another function, it will return something else. I did a bunch of sys.call VOODOO that I didn't understand fully, and it would allow it to work at one-level deep of function calls (I guess because of the nature of the call stack). And it would only work on have one FUN to evaluate in the v.math where I have 3 functions to evaluate.
Here's a wrapper around match.fun to allow for the user to include :: in a string argument:
as_fun = function(x) {
if(is.character(x) && grepl("::", x)) return(eval(parse(text = x)))
match.fun(x)
}
as_fun(base::sum)
# function (..., na.rm = FALSE) .Primitive("sum")
as_fun("base::sum")
# function (..., na.rm = FALSE) .Primitive("sum")
as_fun(sum)
# function (..., na.rm = FALSE) .Primitive("sum")
as_fun("sum")
# function (..., na.rm = FALSE) .Primitive("sum")

R: how to write a wrapper for a function calling another function

I use the function caRamel from the package with the same name. The function takes another function my_func as an argument....
caRamel(
fn=my_func,
other_caRamel_parameters...
)
my_func is a function taking a unique parameter i (by design, mandatory and given by the caRamel function):
my_func <- function(i) {
require(somelib)
my_path = "C:/myfolder/"
do things with i
...
}
By design, there is no way to pass further arguments to the my_func function inside the caRamel function and I have to hard code everything like the my_path variable for example inside my_func.
However, I would like to be able to pass my_path and others variables as parameters in the caRamel function like so:
caRamel(
fn=my_func,
other_caRamel_parameters...,
my_path="C:/myfolder/", ...
)
with:
my_func <- function(i, my_path) {
require(somelib)
my_path = my_path
do things with i
...
}
I was then wondering if it was possible to write a "wrapper" in this case so that further parameters can be passed to my_func? What could be the options to achieve that?
We can use {purrr} and {rlang} in a custom myCaRamel function which only takes the ellipsis ... as argument.
First we capture the dots with rlang::list2(...).
Then we get all formals of the caRamel function as names with rlang::fn_fmls_names.
Now we differentiate between arguments which go into the caRamel function and those which go into your custom function.
We use purrr::partial to supply the argument which go into your my_fun.
Then we call the original caRamel function using the partial function we created together with the arguments that go in to caRamel.
In the example below I rlang::expr in the last call to show that it is working. Please delete the expr() around the last call to make it actually work.
The downside is that every argument needs to be correctly named. Unnamed arguments and partial matching won't work. Further, if my_fun and caRamel contain arguments with similar names they only go into caRamel and won't reach my_fun.
library(purrr)
library(rlang)
library(caRamel)
my_fun <- function(x, my_path) {
# use my_path
# and some x
}
myCaRamel <- function(...) {
dots <- rlang::list2(...)
caramel_args <- rlang::fn_fmls_names(caRamel)
caramel_args_wo_fun <- caramel_args["func" != caramel_args]
other_args <- names(dots)[!names(dots) %in% caramel_args]
my_fn <- dots[["func"]]
my_fn <- purrr::partial(my_fn, !!! dots[other_args])
rlang::expr( # just to show correct out put - delete this line
caRamel(func = my_fn,
!!! dots[names(dots) %in% caramel_args_wo_fun])
) # delete this line
}
myCaRamel(func = my_fun,
my_path = "C:/myfolder/",
nvar = 10)
#> caRamel(func = my_fn, nvar = 10)
Created on 2021-12-11 by the reprex package (v2.0.1)

do.call cannot find function

First of all, sorry that I do not provide a fully reproducible example, but I'm using the devel version of a BioConductor package and the installation is a bit of a pain in the butt.
I am trying to use do.call to invoke functions based on a string I paste together. Like so:
testfunction <- function(){
print("do.call is working")
}
do.call(paste("test", "function", sep = ""), args = list())
This prints:
"do.call is working"
When I am trying to invoke my function of interest, like so:
for (dataset in unique(metadata[, 1])){
replace_idx <- which(metadata[,1 ] == dataset)
do.call(paste('curatedMetagenomicData::', dataset, ".genefamilies_relab.stool", sep = ""), args = list())
if (length(replace_idx) > 0){
# Do further stuff.
}
}
I get this error:
Error in
curatedMetagenomicData::ZellerG_2014.genefamilies_relab.stool() :
could not find function
"curatedMetagenomicData::ZellerG_2014.genefamilies_relab.stool"
Invoking the function outside of the loop, without do.call, works.
Could this be an environment problem of some sort? Any help is greatly appreciated.

How to write a wrapper function with different default values without leading to clash?

Suppose I write a wrapper function of jsonlite::fromJSON but use different default value for simplifyDataFrame=:
read.json <- function(txt, ...) {
jsonlite::fromJSON(txt, simplifyDataFrame = FALSE, ...)
}
read.json is thus a wrapper function of jsonlite::fromJSON with different default parameter. However, if user specifies simplifyDataFrame = TRUE to override the default of read.json there would be an argument name clash.
> read.json('{"a":1}')
$a
[1] 1
> read.json('{"a":1}', simplifyDataFrame = TRUE)
Error in jsonlite::fromJSON(txt, simplifyDataFrame = FALSE, ...) :
formal argument "simplifyDataFrame" matched by multiple actual arguments
What is the best/correct way to write a wrapper function with different default values of parameters that does not lead to potential name clash?
Like #hrbrmstr suggested, the simplest is to do:
read.json <- function(txt, simplifyDataFrame = FALSE, ...) {
jsonlite::fromJSON(txt, simplifyDataFrame = simplifyDataFrame, ...)
}
If you are going to do that with a lot of arguments and want to avoid typing too much, then I'd recommend you use the functional provided by the functional package:
library(functional)
read.json <- Curry(jsonlite::fromJSON, simplifyDataFrame = FALSE)
The code for Curry is as follows:
function (FUN, ...) {
.orig = list(...)
function(...) do.call(FUN, c(.orig, list(...)))
}
I once recommended Curry here https://stackoverflow.com/a/15636912/1201032 and Hadley made the following comment, offering more alternatives:
There's also plyr::partial and in ptools, %<<%, %>>% and %()%. It's not clear how partial evaluation and lazy evaluation of arguments should interact, and each package takes a slightly different approach.
This was before dplyr and magrittr were written; I imagine similar functions have been ported there as well.

R: Source function by name/Import subset of functions

I have a question with importing functions.
Say I have a R script named "functions" which looks like this:
mult <- function(x,y){
return(x*y)
}
divide <- function(x,y){
return(x/y)
}
Currently I am importing all functions in the script:
source(file="C:\\functions.R",echo=FALSE)
The problem is that the (actual) R script is getting very large.
Is there a way to import the "mult" function only?
I was looking at evalSource/insertSource but my code was not working:
insertSource("C:\\functions.R", functions="mult")
It looks like your code will work with a slight change: define an empty object for the function you want to load first, then use insertSource.
mult <- function(x) {0}
insertSource("C:\\functions.R", functions="mult")
mult
Which gives:
Object of class "functionWithTrace", from source
function (x, y)
{
return(x * y)
}
## (to see original from package, look at object#original)
The mult object has some additional information that I suppose is related to the original application for insertSource, but you could get rid of them with mult <- mult#.Data, which will set mult to the actual function body only.
Also, you might be interested in the modules project on github, which is trying to implement a lightweight version of R's package system to facilitate code reuse. Seems like that might be relevant, although I think you would have to split your functions into separate files in different subdirectories.
I ended up creating functions to do what you recommended.
This first group allows for multiple functions in one call:
LoadFunction <- function(file,...) {
dots <- match.call(expand.dots = FALSE)$...
dots <- sapply(dots, as.character)
output <- lapply(dots, function(x,file){eval(parse(text=paste(x," <- function(x) {0}",sep="")),envir = .GlobalEnv)
suppressMessages(insertSource(file, functions=x))
eval(parse(text=paste(x," <- ",x,"#.Data",sep="")),envir = .GlobalEnv) },file=file)
}
UnloadFunction <- function(...) {
dots <- match.call(expand.dots = FALSE)$...
dots <- sapply(dots, as.character)
output <- lapply(dots, function(x,file){eval(parse(text=paste("rm(",x,",envir = .GlobalEnv)",sep="")))},file=file)
}
They are called like this:
LoadFunction(file="C:\\functions.R",mult,divide)
UnloadFunction(mult,divide)
The second is only one function per call:
LoadFunction2 <- function(file,function_name) {
eval(parse(text=paste(function_name," <- function(x) {0}",sep="")),envir = .GlobalEnv)
suppressMessages(insertSource(file, functions=function_name))
eval(parse(text=paste(function_name," <- ",function_name,"#.Data",sep="")),envir = .GlobalEnv)
}
UnloadFunction2 <- function(function_name) {
eval(parse(text=paste("rm(",function_name,",envir = .GlobalEnv)",sep="")))
}
They are called like this:
LoadFunction2(file="C:\\functions.R","mult")
LoadFunction2(file="C:\\functions.R","divide")
UnloadFunction2("mult")
UnloadFunction2("divide")

Resources