R - pass a global variable to a function, modify it and save - r

I'm trying to build a dynamic function utilizing eval,parse, or whatever works
Intention of a function: a value setter.
Parameter input: list, name of list item, value
Return: don't really care
Current code
#call fun_lsSetValue(state_list,selected,"dropdown")
fun_lsSetValue <- function(ls,name,value){
pars <- as.list(match.call()[-1])
element <- as.character(eval(expression(pars$name)))
if(is.null(value))
eval(parse(text="ls[[element]] <- ''"))
else
eval(parse(text="ls[[element]] <- value"))
#part that I need help, I need to assign ls to "state_list" without
#having to hard coded it in this function
#I have tried everything I can think of like
#assign(deparse(substitute(ls)),ls,.GlobalEnv)
#state_list <<- ls works, but I want to be dynamic
}
The problem I found is I need to pass the value of a local variable "ls" to where it came from dynamically (state_list)
I know a <- function(a,name,value) {... return(a)} work, but this syntax is really not my preference.
Since I'm trying to learn if same thing can be done without the assign out side of function.
Any advise would be helpful.

Even though this is a terrible idea in general, something like
fun_lsSetValue <- function(ls,name,value){
lsname <- deparse(substitute(ls))
name <- deparse(substitute(name))
ls <- get(lsname, envir=globalenv())
if(is.null(value)) {
value<-''
}
ls[[name]]<-value
assign(lsname, ls,envir=globalenv())
}
should work
a <- list(x=1)
fun_lsSetValue(a,x,3)
a
# $x
# [1] 3

Related

"Capturing" the global environment

I'm using functions from an external package (that I cannot modify). These functions put a lot of stuff in the global environment, for instance the package does things like
the.data <<- data.frame(A=rnorm(10),B=rnorm(10),C=rnorm(10)) ## A sample dataset
package.plot <- function(){
x.coords <<- the.data$A/the.data$B
y.coords <<- the.data$C
plot(x.coords, y.coords)
}
(obviously hyper-simplified example... here the key is that x.coods and y.coords are rather complex derivations, sufficiently complex that I do not want to recode them but find it advantageous to re-use the existing code)
I want to use these functions in my own scripts, namely make the same graph with ggplot. Of course, a first, obvious solution is
my.better.plot <- function(){
package.plot()
tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point() # etc.
}
However, this has two issues:
I end up plotting twice (a minor issue, it is sufficiently fast
to be unnoticeable);
I "pollute" the global environment with
global x.coords and y.coords
Hence, I would like to run package.plot() in a "pseudo-global" environment to avoid ending up with global variables that may be modified in an "uncontrolled" way.
A workaround, of course, is
my.better.plot <- function(){
package.plot()
tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point() # etc.
rm(x.coords,envir=.GlobalEnv)
}
However, I'd prefer to do something like
my.better.and.cleaner.plot(){
within.envir(dummy_env,my.better.plot)
}
.. assuming that there is, indeed, a function "within.envir" that allows to run its second argument in a mock global environment.
Is something like this possible at all ? I did read http://adv-r.had.co.nz/Environments.html , but could not find the answer... (not one that I understood, at any rate). Bonus question : if this is possible, how can I extract the return value of ggplot from dummy_env and return it ?
This function avoids the side effects as much as possible:
library(ggplot2)
library(magrittr)
library(tibble)
my.better.plot <- function(){
x.coords <- 1
y.coords <- 1
environment(package.plot) <- environment()
bmp(tempfile())
package.plot()
dev.off()
print(tibble(x.coords,y.coords) %>% ggplot(aes(x=x.coords,y=y.coords))+geom_point()) # etc.
}
my.better.plot()
#creates only the ggplot in the current device
ls(globalenv())
#[1] "my.better.plot" "package.plot" "the.data"
So this is unfortunately very hacky, since the <<- operator will traverse the environment tree upwards if it does not find the variable name (hence why you should basically never use it.
The one workaround is to call the function from another environment that already has the variables in question initialized. Then it will assign it into those variables and not traverse further up into the globalEnv. You need to know the variable names beforehand though.
f <- function(x) a <<- x
f(5)
# a = 5 in GlobalEnv
rm(a)
CapturedCall <- function(fun, CapturedVars,...)
{
stopifnot(is.function(fun))
SandBox <- new.env()
for(varName in CapturedVars) assign(varName, NA,SandBox)
environment(fun) <- SandBox
fun(...)
}
CapturedCall(f,"a",1)
#Nothing in GlobalEnv

R functions using data.frame$column not working

The two following functions don't work at the moment, but do work when I write them out in full - not sure why. Any suggestions for fixes would be great.
change_specific_column_name <- function(data.frame,old_column_name,new_column_name){
names(data.frame)[names(data.frame) == old_column_name] <- new_column_name
}
change_specific_observations_name <- function(data.frame, column_name, old_obseration, new_observation){
data.frame$column_name[which(data.frame$column_name == old_obseration)] <- new_observation
}
test_frame <- data.frame(Does=1,This=2,Work=3)
change_specific_column_name(test_frame,"Work","Happen") # this doesn't change the name of the column
names(test_frame)[names(test_frame) == "Work"] <- "Happen" # writing out the function does change the name
Although not exact, you can think of the function's argument as passed-by-value, so it's perspicuous that changes made to the function's formal parameter don't impact the actual argument.
Any suggestions for fixes would be great.
If you really want a function to modify its argument, you could use a technique described e. g. under Call by reference in R; essentially just wrap your assignment in eval.parent(substitute(…)).
change_specific_column_name <- function(data.frame, old_column_name, new_column_name)
eval.parent(substitute(names(data.frame)[names(data.frame) == old_column_name] <- new_column_name))

Writing a loop in R

I have written a loop in R. The code is expected to go through a list of variables defined in a list and then for each of the variables perform a function.
Problem 1 - I cannot loop through the list of variables
Problem 2 - I need to insert each output from the values into Mongo DB
Here is an example of the list:
121715771201463_626656620831011
121715771201463_1149346125105084
Based on this value - I am running a code and i want this output to be inserted into MongoDB. Right now only the first value and its corresponding output is inserted
test_list <-
C("121715771201463_626656620831011","121715771201463_1149346125105084","121715771201463_1149346125105999")
for (i in test_list)
{ //myfunction//
mongo.insert(mongo, DBNS, i)
}
I am able to only pick the values for the first value and not all from the list
Any help is appreciated.
Try this example, which prints the final characters
myfunction <- function(x){ print( substr(x, 27, nchar(x)) ) }
test_list <- c("121715771201463_626656620831011",
"121715771201463_1149346125105084",
"121715771201463_1149346125105999")
for (i in test_list){ myfunction(i) }
for (j in 1:length(test_list)){ myfunction(test_list[j]) }
The final two lines should each produce
[1] "31011"
[1] "105084"
[1] "105999"
It is not clear whether "variable" is the same as "value" here.
If what you mean by variable is actually an element in the list you construct, then I think Ilyas comment above may solve the issue.
If "variable" is instead an object in the workspace, and elements in the list are the names of the objects you want to process, then you need to make sure that you use get. Like this:
for(i in ls()){
cat(paste(mode(get(i)),"\n") )
}
ls() returns a list of names of objects. The loop above goes through them all, uses get on them to get the proper object. From there, you can do the processing you want to do (in the example above, I just printed the mode of the object).
Hope this helps somehow.

How can I save the results of a function in R without returning it?

I want to save the results of a function without returning it. I know that the variables in the function are local. I tried the followings, they return NULL.
function1 <- function(directory)
{
setwd(directory)
#some codes
save(list=ls(.GlobalEnv), file= "result.Rdata")
# save.image(file= "result.Rdata")
}
function1("~/Desktop")
Please tell more what do you want to achieve. Do you want to save the result in the file, or in the R workspace? In the first case
function1 <- function(directory) {
setwd(directory)
#some codes
a <- 1
save(list=ls(), file= "result.Rdata")
}
will do the trick. Note: you want to save current environment by ls(), not the global environment (unless you assign values there). But why do you want to save all variables from inside of a function, instead of just the important values?
If you want to save to the workspace, use assign().
What about returning the results invisible
test <- function(x){
return(invisible(x^2))
}
test(10) # no display of the result
abc <- test(10)
abc #contains 100

R copying attributes over to anther object

I have an initial variable:
a = c(1,2,3)
attr(a,'name') <- 'numbers'
Now I want to create a new variable that is a subset of a and then have it have the same attributes as a. Is there like a copy.over.attr function or something around that does this without me having to go inside and identify which one is user defined attributes etc. This gets complicated when I have numerous attributes attached to a single variable.
It should be used with caution and care. There is mostattributes<-, which receives a list and attempts to set the attributes in the list to the object in its argument. At the very least, reading the source code will give you some nice ideas on how to check attributes between objects. Here's a little run on your sample a vector. It succeeds since it's not violating any properties of b
a = c(1,2,3)
attr(a,'name') <- 'numbers'
b <- a[-1]
attributes(b)
# NULL
mostattributes(b) <- attributes(a)
attributes(b)
# $name
# [1] "numbers"
Here's a sample of the source code where names are checked.
if (h.nam <- !is.na(inam <- match("names", names(value)))) {
n1 <- value[[inam]]
value <- value[-inam]
}
if (h.dim <- !is.na(idin <- match("dim", names(value)))) {
d1 <- value[[idin]]
value <- value[-idin]
}
if (h.dmn <- !is.na(idmn <- match("dimnames", names(value)))) {
dn1 <- value[[idmn]]
value <- value[-idmn]
}
attributes(obj) <- value
There is also attr.all.equal. It's not the operation you want, but I think you would benefit from reading that source code too. There are many good checks you can learn about in that one.
Wouldn't a simple attributes(b) <- attributes(a) work?
This will just be executed after creating b from a subset of the data in a, so it's not really a single statement, but should work.

Resources