How to save a function containing parameters into an RDS file? - r

Suppose that after some calculations I obtain a (floating) number, stored in the variable a, for example
a <- sqrt(2)
Now, I want to define a function that uses that parameter, for example:
myfunction <- function(x){x-a}
How can I save myfunction into an RDS file, in such a way that it can be loaded and used in a new R session, where the variable a is not defined?
Or from a different perspective: How to define the function, but substituting for a its actual numerical value in the function definition? That is, I'd like R to actually define the function
myfunction <- function(x){x - 2.1415.....}
where the actual value of a has been substituted in the definition.
Simply trying saveRDS(myfunction, 'myfunction.rds') does not work: if I start a new R session and do
myfunction <- readRDS('myfunction.rds')
myfunction(1)
then R complains that a is not defined.
Please note that I'm here giving a minimal working example of the problem. Obviously, in the case above I could just define myfunction <- function(x){x-sqrt(2)} and save that in an RDS file; it could be loaded in a new session and used without problems.
However, in my case I have many parameters like a, not just one, obtained from long calculations. I'm not interested in saving their values, I only want to save the function that uses them in its definition, and be able to use that function in a new R session.

An RDS file won't save the global environment, but if you create a closure, it will preserve the values in that environment. One such way to do what would be
myfunction <- {function(a) function(x){x-a}}(a)
And then you can call this function like a regular function
myfunction(1)

Related

How do I output a matrix from a custom function with a name based on the name of the input matrix

I have a repetitive region of R-script that manipulates matrices that I would like to simplify with a custom function. I can generate the custom function just fine, but I would like the output of the function to be a matrix in the global environment with a name based on the name of the input matrix.
So the code goes something like this:
CustomFunction <- function (input) {
input_mod <- input *2
}
(Obviously I have simplified the function here as the actual steps in the function don't matter for the question.)
But what I would like is for the output to be a matrix in the global environment with a name based on the input name. Then if I run the custom function three times on three different input matrices: input1, input2 and input3, the output from the custom function should be three matrices in the global environment named input1_mod, input2_mod and input3_mod.
My current abilities in R only lead me to be able to generate an output that always has the same name and I want to be able to run the function many times and store each of the output matrices for subsequent further manipulations.
Thanks for looking and hope you can help!
Have a look at the assign function, ex.
assign("Global.res", x^2, envir = .GlobalEnv)

Adding columns to data frame via user-defined function

I am trying to add columns to several dataframes. I am trying to create a function that will add the columns and then I would want to use that function with lapply over a list of object. The function is currently just adding empty columns to the data frame. But, if I solve the problem below, I would like to add to it to automatically populate the new columns (and still keeping the initial name of the object).
This is the code I have so far:
AAA_Metadata <- data.frame(AAA_Code=character(),AAA_REV4=character(),AAA_CONCEPT=character(),AAA_Unit=character(),AAA_Date=character(),AAA_Vintage=character())
add_empty_metadata <- function(x) {
temp_dataframe <- setNames(data.frame(matrix(ncol=length(AAA_Metadata),nrow=nrow(x))),as.list(colnames(AAA_Metadata)))
x <- cbind(temp_dataframe,x)
}
However when I run this
a <- data.frame(matrix(ncol=6,nrow=100))
add_empty_metadata(a)
and look at the Global Environment
object "a" still has 6 columns instead of 12.
I understand that I am actually working on a copy of "a" within the function (based on the other topics I checked, e.g. Update data frame via function doesn't work). So I tried:
x <<- cbind(temp_dataframe,x)
and
x <- cbind(temp_dataframe,x)
assign('x',x, envir=.GlobalEnv)
But none of those work. I want to have the new a in the Global Environment for future reference and keep the name 'a' unchanged. Any idea what I am doing wrong here?
Is this what you're looking for:
addCol <- function(x, newColNames){
for(i in newColNames){
x[,i] <- NA
}
return(x)
}
a <- data.frame(matrix(ncol=6,nrow=100));dim(a)
a <- addCol(a, newColNames = names(WIS_Metadata));dim(a)
Amazing source for this kind of stuff is Advanced R by Hadley Wickham with a website here.
R objects are immutable - they don't change - just get destroyed and rebuilt with the same name. a is never changed - it is used as an input to the function and unless the resulting object inside the function is reassigned via return, the version inside the function (this is a separate environment) is binned when the function is complete.

Using delayed assignment in a function: How do I send the promise back to the parent environment?

I would like to used delayedAssign to load a series of data from a set of files only when the data is needed. But since these files will always be in the same directory (which may be moved around), instead of hard coding the location of each file (which would be tedious to change later on if the directory was moved), I would like to simply make a function that accepts the filepath for the directory.
loadLayers <- function(filepath) {
delayedAssign("dataset1", readRDS(file.path(filepath, "experiment1.rds")))
delayedAssign("dataset2", readRDS(file.path(filepath, "experiment2.rds")))
delayedAssign("dataset3", readRDS(file.path(filepath,"experiment3.rds")))
return (list <- (setOne = dataset1, setTwo = dataset2, setThree = dataset3)
}
So instead of loading all the data sets at the start, I'd like to have each data set loaded only when needed (which speeds up the shiny app).
However, I'm having trouble when doing this in a function. It works when the delayedAssign is not in a function, but when I put them in a function, all the objects in the list simply return null, and the "promise" to evaluate them when needed doesn't seem to be fulfilled.
What would be the correct way to achieve this?
Thanks.
Your example code doesn't work in R, but even conceptually, you're using delayedAssign and then you immediately resolve it by referencing it in return() so you end up loading everything anyway. To make it clear, assignments are binding a symbol to a value in an enviroment. So in order for it to make any sense your function must return the environment, not a list. Or, you can simply use the global environment and the function doesn't need to return anything as you use it for its side-effect.
loadLayers <- function(filepath, where=.GlobalEnv) {
delayedAssign("dataset1", readRDS(file.path(filepath, "experiment1.rds")),
assign.env=where)
delayedAssign("dataset2", readRDS(file.path(filepath, "experiment2.rds")),
assign.env=where)
delayedAssign("dataset3", readRDS(file.path(filepath, "experiment3.rds")),
assign.env=where)
where
}

Lacking output strings when using function in R

I'm having a problem with the below function:
ab<-matrix(c(1:20),nrow=4)
rownames(ab)<-c("a","b","c","d")
cd<-c("a","c")
test<-function(x,y,ID_Tag){
for(i in y) {
M_scaled<-t(scale(t(x),center=T))
a<-quantile(M_scaled[match(i,rownames(x)),])
assign(paste0("Probes_",ID_Tag,"_quan_",i),a)
}
}
test(ab,cd,"C1")
x is the dataframe/matrix
y is the string I need to search for in rownames(x)
ID_Tag is is the number I use to distinguish my samples from each other.
The function is running, but no output is generated into strings afterwards.
Hope somebody can help me
When you use assign within a function it will make the assignment to a variable that is accessible within that function only (i.e. it's like using <-). To get around this, you need to specify the envir argument in assign to be either the global environment globalenv() or the parent frame of the function. So try changing your assign statement to
assign(..., envir = parent.frame())
or
assign(..., envir = globalenv())
depending on what you want exactly (in the example you provided they are equivalent). Have a look at ?parent.frame for more info on these. Another possibility is to specify the pos argument in assign, check ?assign.
As an aside, assigning global objects from within a function can lead to various problems in general. I find it better practice in your example to return a list of objects created in the for loop rather than use assign.

How do you stop source from abbreviating function names?

I am trying to source multiple functions, that differ by a number in the name.
For example: func1, func2.
I tried using "func_1", and "func_2", as well as putting the number first, "1func" and "2func". No matter how I index the function names, the source function just reads in one function that it calls "func" - which is not what I want.
I have tried using for-loops and sapply:
for-loop:
func.list <- list.files(path="/some_path",pattern="some pattern",full.names=TRUE)
for(i in 1:length(func.list)){
source(func.list[i])
}
sapply:
sapply(func.list,FUN=source)
I am going to be writing multiple versions of a data correction function, and would really like to be able to index them - because giving a concise, but specific, name would be difficult, and not allow me to selectively source just the function files from their directory.
In my code, func.list gives the output (I have replaced the actual directory because of privacy/contractual issues):
[1] "mypath/1resp.correction.R"
[2] "mypath/2resp.correction.R"
Then when I source func.list with either the for-loop or sapply code (listed above), R only loads one function named resp.correction, with the code body from "2resp.correction.R".
The argument to source is a file name, not a function name. So you cannot be fancy here: you need to provide the exact filenames.
It sounds like your two files contain the definitions of a function with the same name (resp.correction) in both files, so yes, as you source one file after the other, the function is overwritten in your global environment.
You could, inside your loop, reassign the function to a different name:
func.list <- list.files(path="/some_path",pattern="some pattern",full.names=TRUE)
for(i in 1:length(func.list)) {
source(func.list[i], local = TRUE)
assign(paste0("resp.correction", i), resp.correction, envir = .GlobalEnv)
}

Resources