I want to save dynamic data or dataframe (because it depends on parameters) inside a function and want to use this saved data another function. I thought of using saveRDS(), but since I work in my local, I didn't know if it would work when I made it public. I don't get any errors, warnings or notes when I check, but I'm looking for a better solution.
get_data <- function(startDate,endDate){
df <- ... # dynamic
saveRDS(df,"df.rds")
}
funcx <- function(){
df2 <- readRDS("df.rds")
}
Thanks in advance.
1) As mentioned in the comments just pass the data using the return value of the first function and arguments of the second.
get_data <- function(startDate, endDate) {
data.frame(startDate, endDate)
}
funcx <- function(df){
print(df)
}
get_data(1, 2) |> funcx()
or store the data in your workspace and then get it from there:
Data <- get_data(1, 2)
funcx(Data)
or put it in an option
options(Data = get_data(1, 2))
funcx(getOption("Data"))
2) Use a local environment:
e <- local({
df <- NULL
get_data <- function(startDate, endDate) {
df <<- data.frame(startDate, endDate)
}
funcx <- function(){
print(df)
}
environment()
})
e$get_data(1, 2)
e$funcx()
or put the environment right in the package. Look at lattice.options and lattice.getOption source code in the lattice package for an example of this where it uses the .LatticeEnv environment that is stored right in the package itself.
library(lattice)
ls(lattice:::.LatticeEnv)
lattice.options # displays source code of lattice.options
lattice.getOption # displays source code
3) The proto package can be used to clean up (2) a bit. proto objects are environments having their own methods. See the package vignette and help files.
library(proto)
p <- proto(
get_data = function(., startDate, endDate) {
.$df <- data.frame(startDate, endDate)
},
funcx = function(.) {
print(.$df)
}
)
p$get_data(1, 2)
p$funcx()
Related
How can I write a function that takes an argument which is passed to the data function in R.
As a simple example
load_data <- function(mydata) {
x <- data(mydata)
x
}
If I tried to use this to load iris
load_data(mydata = "iris")
The function just returns "data set mydata not found"
My end goal here is to have a function that attaches a dataset to the function's environment, and then the function would do some arbitrary calculations.
If you want data() to evaualte that character string, it needs to be passed via list=
load_data <- function(mydata) {
x <- data(list=mydata)
x
}
I'm not sure what you were expecting to be returned but the ?data help page tells you it just returns the name of the data sets loaded, not the data itself.
If you only want it to load into the function scope, then also set the environment= parameter
load_data <- function(mydata) {
data(list=mydata, environment=environment())
}
If you want iris to be stored in x then you don't need to call data at all.
load_data <- function(mydata) {
x <- mydata
x
}
load_data(iris)
How do I pass a pre-existing object, such as a DF, to my custom functions, given the setup I have (see below)?
Alternatively, do I need to set up my custom functions differently?
My functions reside in a series of *.R scripts.
I source the functions in my .Rprofile:
.env$fxShortName <- function(){
source("C:\\path\\to\\scriptFile.R")
}
Not-Quite Solutions:
1) Defining the Function Manually || It works w/ the obvious drawback that I need to manually load my functions each time.
2) Rscript + commandArgs || This works if I define the DF within the function, like this:
#foo.R
a <- data.frame(a = c(1))
b <- data.frame(b = c(1))
args <- commandArgs(trailingOnly = TRUE)
print(args)
data.frame.name <- args[1]
print(colnames(get(data.frame.name)))
Rscript creates a new R instance, though, so it doesn't see my pre-existing DF. At least, it doesn't find it out-of-the-box.
3) Function w/ substitute, match.call, etc. || I've adopted %>>% to set up auto-updated views of certain DFs, so I tried modifying the setup that works in that case. For %>>% I have this code in my .Rprofile:
.env$`%>>%` <- function(expr, x) {
x <- substitute(x)
call <- match.call()[-1]
fun <- function() {NULL}
body(fun) <- call$expr
makeActiveBinding(sym = deparse(x), fun = fun, env = parent.frame())
invisible(NULL)
}
This type of setup works with DFs from my current session. However, I prefer the structure offered by keeping my custom scripts separate from my .Rprofile.
4) get() & mget() || This seemed promising, but I don't understand it enough to definitively say whether or not it will help. And, yes, I did RTFM.
Reproducible Example:
myfx(head, preExistingDF)
Sample Code:
myfx <- function(expr, x) {
x <- substitute(x)
call <- match.call()[-1]
fun <- function() {NULL}
body(fun) <- call$expr
print(body(fun))}
Put the sample code in a script. Add the following code to your .Rprofile:
.env$mySamplefx <- function(){
source("C:\\path\\to\\myfx.R")
}
Then try it after adding the code directly to your .Rprofile.
I am using quantmod to adjust for dividends and splits. It seems to work but I have found the following problem: when adjusting my sma(200,0) historical values are wrong and they correct as the date approaches the current date. Please see the code below.
stockData <- new.env() #Make a new environment for quantmod to store data in
symbols = c("IWM","SPY","TLT","TSLA")
nr.of.positions<-3
getSymbols(symbols, src='yahoo',from = "2015-10-01",to = Sys.Date())
for (i in 1:length(symbols)) {
assign (symbols[i], adjustOHLC(get(symbols[i]),
adjust=c("split", "dividend"),
use.Adjusted=FALSE,
symbol.name=symbols[i]))
}
x <- list()
for (i in 1:length(symbols)) {
x[[i]] <- get(symbols[i], pos=stockData) # get data from stockData environment
x[[i]]$sma <-SMA(Cl(x[[i]]),10)
x[[i]]$smalong <-SMA(Cl(x[[i]]),200)
x[[i]]$adx<-ADX(HLC(x[[i]]),10)
x[[i]]$rsi <-RSI(Cl(x[[i]]),14)
x[[i]]$close <-(Cl(x[[i]]))
}
You're lucky that your code works. Or maybe you're unlucky, since an error would have let you know you did something wrong.
You create a stockData environment and the comment says you intended to store the data you pull in it. But you don't specify the stockData environment in your call to getSymbols, or your calls to assign and get in the first for loop. So they're all assigning and getting from the global environment.
Your code would be clearer if you avoid using get and assign within a for loop, and instead used convenience functions lapply and eapply.
stockData <- new.env()
symbols <- c("IWM","SPY","TLT","TSLA")
nr.of.positions <- 3
getSymbols(symbols, from = "2015-10-01", env = stockData)
# loop over objects in an environment with eapply
adj <- eapply(stockData, function(x) {
symbol <- sub("\\.Close$", "", colnames(Cl(x)))
adjustOHLC(x, symbol.name=symbol)
})
# loop over list returned by eapply
x <- lapply(adj, function(x) {
x$sma <- SMA(Cl(x),10)
x$smalong <- SMA(Cl(x),200)
x$adx <- ADX(HLC(x),10)
x$rsi <- RSI(Cl(x),14)
x$close <- Cl(x)
x
})
You'll notice the results of my code and your code are the same if you run them each in a clean R session. So the reason your code produced "wrong" results is probably because you had other objects in your workspace that were being assigned/accessed by your use of get and assign.
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")
I am trying to keep an assigned object from a function (building a ts function to begin to model a univariate process, simple I know!). I am having trouble finding a method to keep objects in my workspace. It works fine just using a for loop but I would like to parameterize the following:
ts.builder<-function(x,y,z){
for(i in 9:13){
assign(paste(x,i,sep="_"),ts(yardstick[1:528,i], freq=24))
assign(paste(y,i,sep="_"),ts(yardstick[529:552,i], freq=24))
assign(paste(z,i,sep="_"),ts(yardstick[1:552,i], freq=24))
}
}
ts.builder("yard.book.training","yard.book.small.valid", "yard.book.valid")
Any pointers?
I am thinking it may need a return statement, yet I have not found this to be of use yet.
Untested (a reproducible example helps a lot):
ts.builder <- function() {
xd <- list()
yd <- list()
zd <- list()
for (i in 9:13) {
xd[[i]] <- ts(yardstick[1:528,i], freq=24)
yd[[i]] <- ts(yardstick[529:552,i], freq=24)
zd[[i]] <- ts(yardstick[1:552,i], freq=24)
}
list(yard.book.training=xd, yard.book.small.valid=yd, yard.book.valid=zd)
}
l <- ts.builder()
Then here are the returned values:
l$yard.book.training[[9]]
etc.