It seems possible to assign a vector of functions in R like this:
F <- c(function(){return(0)},function(){return(1)})
so that they can be invoked like this (for example): F[[1]]().
This gave me the impression I could do this:
DF <- data.frame(F=c(function(){return(0)}))
which results in the following error
Error in as.data.frame.default(x[[i]], optional = TRUE) : cannot
coerce class ""function"" to a data.frame
Does this mean it is not possible to put functions into a data frame? Or am I doing something wrong?
No, you cannot directly put a function into a data-frame.
You can, however, define the functions beforehand and put their names in the data frame.
foo <- function(bar) { return( 2 + bar ) }
foo2 <- function(bar) { return( 2 * bar ) }
df <- data.frame(c('foo', 'foo2'), stringsAsFactors = FALSE)
Then use do.call() to use the functions:
do.call(df[1, 1], list(4))
# 6
do.call(df[2, 1], list(4))
# 8
EDIT
The above work around will work as long as you have a named function.
The issue seems to be that R see's the class of the object as a function, looks up the appropriate method for as.data.frame() (i.e. as.data.frame.function()) but can't find it. That causes a call to as.data.frame.default() which pretty must is a wrapper for a stop() call with the message you reported.
In short, they just seem not to have implemented it for that class.
While you can't put a function or other object directly into a data.frame, you can make it work if you go via a matrix.
foo <- function() {print("qux")}
m <- matrix(c("bar", foo), nrow=1, ncol=2)
df <- data.frame(m)
df$X2[[1]]()
Yields:
[1] "qux"
And the contents of df look like:
X1 X2
1 bar function () , {, print("qux"), }
Quite why this works while the direct path does not, I don't know. I suspect that doing this in any production code would be a "bad thing".
Related
Imagine you have a simple function that specifies which statistical tests to run for each variable. Its syntax, simplified for the purposes of this question is as follows:
test <- function(...) {
x <- list(...)
return(x)
}
which takes argument pairs such as Gender = 'Tukey', and intends to pass its result to other functions down the line. The output of test() is as follows:
test(Gender = 'Tukey')
# $Gender
# [1] "Tukey"
What is desired is the ability to replace the literal Gender by a dynamically assigned variable varname (e.g., for looping purposes). Currently what happens is:
varname <- 'Gender'
test(varname = 'Tukey')
# $varname
# [1] "Tukey"
but what is desired is this:
varname <- 'Gender'
test(varname = 'Tukey')
# $Gender
# [1] "Tukey"
I tried tinkering with functions such as eval() and parse(), but to no avail. In practice, I resolved the issue by simply renaming the resulting list, but it is an ugly solution and I am sure there is an elegant R way to achieve it. Thank in advance for the educational value of your answer.
NB: This question occurred to me while trying to program a custom function which uses mcp() from the effects package in its internals. The said mcp() function is the real world counterpart of test().
EDIT1: Perhaps it needs to be clarified that (for educational purposes) changing test() is not an option. The question is about how to pass the tricky argument to test(). If you take a look at NB, it becomes clear why: the real world counterpart of test(), namely mcp(), comes with a package. And while it is possible to create a modified copy of it, I am really curious whether there exists a simple solution in somehow 'converting' the dynamically assigned variable to a literal in the context of dot-arguments.
This works:
test <- function(...) {
x = list(...)
names(x) <- sapply(names(x),
function(p) eval(as.symbol(p)))
return(x)
}
apple = "orange"
test(apple = 5)
We can use
test <- function(...) {
x <- list(...)
if(exists(names(x))) names(x) <- get(names(x))
x
}
test(Gender = 'Tukey')
#$Gender
#[1] "Tukey"
test(varname = 'Tukey')
#$Gender
#[1] "Tukey"
What about this:
varname <- "Gender"
args <- list()
args[[varname]] <- "Tukey"
do.call(test, args)
I'm trying to read a function call as a string and evaluate this function within another function. I'm using eval(parse(text = )) to evaluate the string. The function I'm calling in the string doesn't seem to have access to the environment in which it is nested. In the code below, my "isgreater" function finds the object y, defined in the global environment, but can't find the object x, defined within the function. Does anybody know why, and how to get around this? I have already tried adding the argument envir = .GlobalEnv to both of my evals, to no avail.
str <- "isgreater(y)"
isgreater <- function(y) {
return(eval(y > x))
}
y <- 4
test <- function() {
x <- 3
return(eval(parse(text = str)))
}
test()
Error:
Error in eval(y > x) : object 'x' not found
Thanks to #MrFlick and #r2evans for their useful and thought-provoking comments. As far as a solution, I've found that this code works. x must be passed into the function and cannot be a default value. In the code below, my function generates a list of results with the x variable being changed within the function. If anyone knows why this is, I would love to know.
str <- "isgreater(y, x)"
isgreater <- function(y, x) {
return(eval(y > x))
}
y <- 50
test <- function() {
list <- list()
for(i in 1:100) {
x <- i
bool <- eval(parse(text = str))
list <- append(list, bool)
}
return(list)
}
test()
After considering the points made by #r2evans, I have elected to change my approach to the problem so that I do not arrive at this string-parsing step. Thanks a lot, everyone.
I offer the following code, not as a solution, but rather as an insight into how R "works". The code does things that are quite dangerous and should only be examined for its demonstration of how to assert a value for x. Unfortunately, that assertion does destroy the x-value of 3 inside the isgreater-function:
str <- "isgreater(y)"
isgreater <- function(y) {
return(eval( y > x ))
}
y <- 4
test <- function() {
environment(isgreater)$x <- 5
return(eval(parse(text = str) ))
}
test()
#[1] FALSE
The environment<- function is used in the R6 programming paradigm. Take a look at ?R6 if you are interested in working with a more object-oriented set of structures and syntax. (I will note that when I first ran your code, there was an object named x in my workspace and some of my efforts were able to succeed to the extent of not throwing an error, but they were finding that length-10000 vector and filling up my console with logical results until I escaped the console. Yet another argument for passing both x and y to isgreater.)
Thanks in advance, and sorry if this question has been answered previously - I have looked pretty extensively. I have a dataset containing a row of with concatenated information, specifically: name,color code,some function expression. For example, one value may be:
cost#FF0033#log(x)+6.
I have all of the code to extract the information, and I end up with a vector of expressions that I would like to convert to a list of actual functions.
For example:
func.list <- list()
test.func <- c("x","x+1","x+2","x+3","x+4")
where test.func is the vector of expressions. What I would like is:
func.list[[3]]
To be equivalent to
function(x){x+3}
I know that I can create a function using:
somefunc <- function(x){eval(parse(text="x+1"))}
to convert a character value into a function. The problem comes when I try and loop through to make multiple functions. For an example of something I tried that didn't work:
for(i in 1:length(test.func)){
temp <- test.func[i]
f <- assign(function(x){eval(expr=parse(text=temp))})
func.list[[i]] <- f
}
Based on another post (http://stats.stackexchange.com/questions/3836/how-to-create-a-vector-of-functions) I also tried this:
makefunc <- function(y){y;function(x){y}}
for(i in 1:length(test.func)){
func.list[[i]] <- assign(x=paste("f",i,sep=""),value=makefunc(eval(parse(text=test.func[i]))))
}
Which gives the following error: Error in eval(expr, envir, enclos) : object 'x' not found
The eventual goal is to take the list of functions and apply the jth function to the jth column of the data.frame, so that the user of the script can specify how to normalize each column within the concatenated information given by the column header.
Maybe initialize your list with a single generic function, and then update them using:
foo <- function(x){x+3}
> body(foo) <- quote(x+4)
> foo
function (x)
x + 4
More specifically, starting from a character, you'd probably do something like:
body(foo) <- parse(text = "x+5")
Just to add onto joran's answer, this is what finally worked:
test.data <- matrix(data=rep(1,25),5,5)
test.data <- data.frame(test.data)
test.func <- c("x","x+1","x+2","x+3","x+4")
func.list <- list()
for(i in 1:length(test.func)){
func.list[[i]] <- function(x){}
body(func.list[[i]]) <- parse(text=test.func[i])
}
processed <- mapply(do.call,func.list,lapply(test.data,list))
Thanks again, joran.
This is what I do:
f <- list(identity="x",plus1 = "x+1", square= "x^2")
funCreator <- function(snippet){
txt <- snippet
function(x){
exprs <- parse(text = txt)
eval(exprs)
}
}
listOfFunctions <- lapply(setNames(f,names(f)),function(x){funCreator(x)}) # I like to have some control of the names of the functions
listOfFunctions[[1]] # try to see what the actual function looks like?
library(pryr)
unenclose(listOfFunctions[[3]]) # good way to see the actual function http://adv-r.had.co.nz/Functional-programming.html
# Call your funcions
listOfFunctions[[2]](3) # 3+1 = 4
do.call(listOfFunctions[[3]],list(3)) # 3^2 = 9
attach(listOfFunctions) # you can also attach your list of functions and call them by name
square(3) # 3^2 = 9
identity(7) # 7 ## masked object identity, better detach it now!
detach(listOfFunctions)
I have loaded in a R console different type of objects.
I can remove them all using
rm(list=ls())
or remove only the functions (but not the variables) using
rm(list=lsf.str())
My question is:
is there a way to remove all variables except the functions
Here's a one-liner that removes all objects except for functions:
rm(list = setdiff(ls(), lsf.str()))
It uses setdiff to find the subset of objects in the global environment (as returned by ls()) that don't have mode function (as returned by lsf.str())
The posted setdiff answer is nice. I just thought I'd post this related function I wrote a while back. Its usefulness is up to the reader :-).
lstype<-function(type='closure'){
inlist<-ls(.GlobalEnv)
if (type=='function') type <-'closure'
typelist<-sapply(sapply(inlist,get),typeof)
return(names(typelist[typelist==type]))
}
You can use the following command to clear out ALL variables. Be careful because it you cannot get your variables back.
rm(list=ls(all=TRUE))
Here's a pretty convenient function I picked up somewhere and adjusted a little. Might be nice to keep in the directory.
list.objects <- function(env = .GlobalEnv)
{
if(!is.environment(env)){
env <- deparse(substitute(env))
stop(sprintf('"%s" must be an environment', env))
}
obj.type <- function(x) class(get(x, envir = env))
foo <- sapply(ls(envir = env), obj.type)
object.name <- names(foo)
names(foo) <- seq(length(foo))
dd <- data.frame(CLASS = foo, OBJECT = object.name,
stringsAsFactors = FALSE)
dd[order(dd$CLASS),]
}
> x <- 1:5
> d <- data.frame(x)
> list.objects()
# CLASS OBJECT
# 1 data.frame d
# 2 function list.objects
# 3 integer x
> list.objects(env = x)
# Error in list.objects(env = x) : "x" must be an environment
I wrote this to remove all objects apart from functions from the current environment (Programming language used is R with IDE R-Studio):
remove_list=c() # create a vector
for(i in 1:NROW(ls())){ # repeat over all objects in environment
if(class(get(ls()[i]))!="function"){ # if object is *not* a function
remove_list=c(remove_list,ls()[i]) # ..add to vector remove_list
}
}
rm(list=remove_list) # remove all objects named in remove_list
Notes-
The argument "list" in rm(list=) must be a character vector.
The name of an object in position i of the current environment is returned from ls()[i] and the object itself from get(ls()[i]). Therefore the class of an object is returned from class(get(ls()[i]))
I wonder how I can pass arguments of some function to a subpart of the function that uses with / within. e.g.:
myfunction <- function(dataframe,col1,col2){
res <- within(dataframe, somenewcol <-paste(col1,"-","col2",sep=""))
return(res)
}
where col1 and col2 are columns contained in the dataframe. What´s the correct way to pass the arguments col1 and col2 to the within expression? When I just try to use it, i get :
Error in paste(col1, "-", , :
object 'Some_passed_col' not found
Here´s an example:
dataset <- data.frame(rnorm(20),2001:2020,rep(1:10,2))
names(dataset) <- c("mydata","col1","col2")
myfunction <- function(dataframe,arg1,arg2){
res <- with(dataframe, onesinglecol <- paste(arg1,"-","arg2",sep=""))
return(res)
}
# call function
myfunction(dataset,col1,col2)
EDIT:
the following works for me now, but I cannot completely understand why... so any further explanation is appreciated:
myfunction(dataset,arg1="col1",arg2="col2")
if I adjust
res <- with(dataframe, onesinglecol <- paste(get(arg1),"-",get(arg2),sep=""))
Try
myfunction <- function(dataframe,arg1,arg2){
dataframe["onesinglecol"] <- dataframe[[arg1]] -dataframe[[arg2]]
return(dataframe)
}
And call it with character-valued column names rather than object names that are nowhere defined:
myfunction(dataset,"col1","col2")
mydata col1 col2 onesinglecol
1 0.6834402 2001 1 2000
2 1.6623748 2002 2 2000
3 -0.5769926 2003 3 2000 .... etc
I think this is done via the ... directive:
E.g.:
myfunction <- function(dataframe, ...){
var <- anotherfunction( arg1= 1, arg2 = 2 , ...)
return(var)
}
... is a placeholder for additional arguments passed through to "anotherfunction".
You are missing the fact that col1 and col2 do not exist in dataframe (from your) nor in the user workspace.
Basically, with() and within() work like this:
> foo <- 10
> bar <- data.frame(FOO = 10)
> FOO + foo
Error: object 'FOO' not found
> with(bar, FOO + foo)
[1] 20
In the first case, FOO was not found as it is inside bar. In the second case, we set up an environment within which our expression is evaluated. Inside that environment FOO does exist. foo is also found in the workspace.
In your first example (please don't edit error messages etc, show us exactly what code you ran and what error was produced) either one col1 or col2 didn't exist in the environment created within which your expression was evaluated.
Further, you appear to want to store in col1 and col2 the name of a column (component) of your dataframe. DWin has shown you one way to use this information. An alternative maintaining the use of within() is to use get() like this:
res <- within(dataframe, somenewcol <- paste(get(col1), "-", get(col2), sep=""))
Why this works, as per your extra edit and quandary, is that get() returns the object named by it's first argument. get("foo") will return the object named foo (continuing from my example above):
get("foo") ## finds foo and returns it
1 10
In your example, you have a data frame with names inter alia "col1" and "col2". You changed your code to get(arg1) (where arg1 <- "col1"), where you are asking get() to return the object with name "col1" from the evaluation environment visible at the time your function is being evaluated. As your dataframe contained a col1 component, which was visible because within() had made it available, get() was able to find an object with the required name and include it in the expression.
But at this point you are trying to jump through too many hoops because your questions haven't been specific. I presume you are asking this because of my answer here to your previous Q. That answer suggested a better alternative than attach(). But you weren't clear there what some arguments were or what your really wanted to do. If I had known now what you really wanted to do then I would have suggested you use DWin's Answer above.
You seem to not want to hard code the column/component names. If you could hard code, this would be the solution:
res <- within(dataframe, somenewcol <- paste(col1, "-", col2, sep = ""))
But seeing as you don't want to hard code you need a get() version or DWin's solution.