Recommended way for variable scoping [duplicate] - r

This question already has answers here:
Usage of local variables
(2 answers)
Closed 5 years ago.
In C and variants, when you have something like this:
{
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
}
a 7 will be printed, but the tmp1 and tmp2 variables will be removed from stack once the scope } ends. I sometimes want similar functionality in R so that I do not have to clean up (many) temporary variables after some point in time.
One way to make this work in R is like this:
(function(){
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})()
Now that seems a bit hackish -- or it may be just me.
Are there any better or more concise (i.e. more readable) ways to do this in R?

You might want to use local in base R for that purpose:
local({
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})
So any variable created within the scope of local will vanish as soon as the scope is left.
From ?local:
local evaluates an expression in a local environment.
See ?local for more details.
Additionally, with (suggested by #Rich Scriven in the comments) also in base R can be used where 1 is just a dummy data:
with(1, {
tmp1 <- 5
tmp2 <- 2
print(tmp1 + tmp2)
})
From ?with:
with is a generic function that evaluates expr in a local environment constructed from data.

Within the main code body the variables are global. However, as you found yourself their scope is local within functions:
addme <- function(){
tmp1 <- 5;
tmp2 <- 2;
return (tmp1+tmp2)
}
then call calling addme() will return the sum, but the variable remain inaccessible outside
> addme()
[1] 7
> tmp1
Error: object 'tmp1' not found

Related

How do I create a list of functions in R? [duplicate]

This question already has answers here:
Enclosing variables within for loop
(3 answers)
Closed 4 years ago.
I am trying to create a list of functions where each function is slightly different. The following code works fine:
fun1 <- function(n) {
fun2 <- function(x) {
x^n
}
return(fun2)
}
powerfuns <- vector("list", 3)
powerfuns[[2]] <- fun1(2)
powerfuns[[3]] <- fun1(3)
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64
The second element of the list is a function that squares its argument while the third is a function that cubes its argument. But it doesn't seem to work if I create the list using a for-loop instead:
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 64
powerfuns[[3]](4)
# [1] 64
Now both functions cube their arguments. There seems to be something about the for-loop that causes the environments for the functions to be identical. get("n", environment(powerfuns[[2]])) returns 3 in the second example but 2 in the first example. Does anyone have a suggestion about how I can get the result I want using a loop or something similar? Thanks!
This has to do with lazy evaulation. Since fun1 doesn't use the value n you pass in right away, it doesn't capture the value in the way you expect. You can fix this with
fun1 <- function(n) {
force(n)
fun2 <- function(x) {
x^n
}
return(fun2)
}
Then you can run the same code
powerfuns <- vector("list", 3)
for (i in 1:3) {
powerfuns[[i]] <- fun1(i)
}
powerfuns[[2]](4)
# [1] 16
powerfuns[[3]](4)
# [1] 64

How would you write this using apply family of functions in R? Should you?

Here is my R Script that works just fine:
perc.rank <- function(x) trunc(rank(x)) / length(x) * 100.0
library(dplyr)
setwd("~/R/xyz")
datFm <- read.csv("yellow_point_02.csv")
datFm <- filter(datFm, HRA_ClassHRA_Final != -9999)
quant_cols <- c("CL_GammaRay_Despiked_Spline_MLR", "CT_Density_Despiked_Spline_FinalMerged",
"HRA_PC_1HRA_Final", "HRA_PC_2HRA_Final","HRA_PC_3HRA_Final",
"SRES_IMGCAL_SHIFT2VL_Slab_SHIFT2CL_DT", "Ultrasonic_DT_Despiked_Spline_MLR")
# add an extra column to datFm to store the quantile value
for (column_name in quant_cols) {
datFm[paste(column_name, "quantile", sep = "_")] <- NA
}
# initialize an empty dataframe with the new column names appended
newDatFm <- datFm[0,]
# get the unique values for the hra classes
hraClassNumV <- sort(unique(datFm$HRA_ClassHRA_Final))
# loop through the vector and create currDatFm and append it to newDatFm
for (i in hraClassNumV) {
currDatFm <- filter(datFm, HRA_ClassHRA_Final == i)
for (column_name in quant_cols) {
currDatFm <- within(currDatFm,
{
CL_GammaRay_Despiked_Spline_MLR_quantile <- perc.rank(currDatFm$CL_GammaRay_Despiked_Spline_MLR)
CT_Density_Despiked_Spline_FinalMerged_quantile <- perc.rank(currDatFm$CT_Density_Despiked_Spline_FinalMerged)
HRA_PC_1HRA_Final_quantile <- perc.rank(currDatFm$HRA_PC_1HRA_Final)
HRA_PC_2HRA_Final_quantile <- perc.rank(currDatFm$HRA_PC_2HRA_Final)
HRA_PC_3HRA_Final_quantile <- perc.rank(currDatFm$HRA_PC_3HRA_Final)
SRES_IMGCAL_SHIFT2VL_Slab_SHIFT2CL_DT_quantile <- perc.rank(currDatFm$SRES_IMGCAL_SHIFT2VL_Slab_SHIFT2CL_DT)
Ultrasonic_DT_Despiked_Spline_MLR_quantile <- perc.rank(currDatFm$Ultrasonic_DT_Despiked_Spline_MLR)
}
)
}
newDatFm <- rbind(newDatFm, currDatFm)
}
newDatFm <- newDatFm[order(newDatFm$Core_Depth),]
# head(newDatFm, 10)
write.csv(newDatFm, file = "Ricardo_quantiles.csv")
I have a few questions though. Every R book or video that I have read or watched, recommends using the 'apply' family of language constructs over the classic 'for' loop stating that apply is much faster.
So the first question is: how would you write it using apply (or tapply or some other apply)?
Second, is this really true though that apply is much faster than for? The csv file 'yellow_point_02.csv' has approx. 2500 rows. This script runs almost instantly on my Macbook Pro which has 16 Gig of memory.
Third, See the 'quant_cols' vector? I created it so that I could write a generic loop (for columm_name in quant_cols) ....But I could not make it to work. So I hard-coded the column names post-fixed with '_quantile' and called the 'perc.rank' many times. Is there a way this could be made dynamic? I tried the 'paste' stuff that I have in my script, but that did not work.
On the positive side though, R seems awesome in its ability to cut through the 'Data Wrangling' tasks with very few statements.
Thanks for your time.

R make functions with lapply: bug or what? [duplicate]

This question already has answers here:
Explain a lazy evaluation quirk
(2 answers)
Closed 7 years ago.
I am using lapply to make new functions and noticed that that sometimes it returns what is expected, and sometimes it returns only copies of the lastly created function.
Here is an example for the illustration, consider that I want to make the following simple list of functions
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
myfunc
})
Unfortunately, a simple evaluation shows that I am not getting what I hoped
listFuncs[[1]](10)
[1] 13
listFuncs[[2]](10)
[1] 13
Indeed, the list only contains the function
myfunc = function(y){3+y}
However, if I output something during the creation of the functions, for example
listFuncs = lapply( 1:3, function(X){
myfunc = function(y){X+y}
print(myfunc(0)) ## NEW LINE HERE !!!
myfunc
})
then my list of functions is "as expected"
[1] 1
[1] 2
[1] 3
> listFuncs[[1]](10)
[1] 11
> listFuncs[[2]](10)
[1] 12
Does anyone understand what is going on ? By advance, thank you.
You can use the force function:
listFuncs = lapply( 1:3,
function(X) {
force(X)
myfunc <- function(y) { X+y }
myfunc
}
)
listFuncs[[1]](10) ## 11

Convenience function for exporting objects to the global environment

UPDATE: I have added a variant
of Roland's implementation to the kimisc package.
Is there a convenience function for exporting objects to the global environment, which can be called from a function to make objects available globally?
I'm looking for something like
export(obj.a, obj.b)
which would behave like
assign("obj.a", obj.a, .GlobalEnv)
assign("obj.b", obj.b, .GlobalEnv)
Rationale
I am aware of <<- and assign. I need this to refactor oldish code which is simply a concatenation of scripts:
input("script1.R")
input("script2.R")
input("script3.R")
script2.R uses results from script1.R, and script3.R potentially uses results from both 1 and 2. This creates a heavily polluted namespace, and I wanted to change each script
pollute <- the(namespace)
useful <- result
to
(function() {
pollute <- the(namespace)
useful <- result
export(useful)
})()
as a first cheap countermeasure.
Simply write a wrapper:
myexport <- function(...) {
arg.list <- list(...)
names <- all.names(match.call())[-1]
for (i in seq_along(names)) assign(names[i],arg.list[[i]],.GlobalEnv)
}
fun <- function(a) {
ttt <- a+1
ttt2 <- a+2
myexport(ttt,ttt2)
return(a)
}
print(ttt)
#object not found error
fun(2)
#[1] 2
print(ttt)
#[1] 3
print(ttt2)
#[1] 4
Not tested thoroughly and not sure how "safe" that is.
You can create an environment variable and use it within your export function. For example:
env <- .GlobalEnv ## better here to create a new one :new.env()
exportx <- function(x)
{
x <- x+1
env$y <- x
}
exportx(3)
y
[1] 4
For example , If you want to define a global options(emulate the classic R options) in your package ,
my.options <- new.env()
setOption1 <- function(value) my.options$Option1 <- value
EDIT after OP clarification:
You can use evalq which take 2 arguments :
envir the environment in which expr is to be evaluated
enclos where R looks for objects not found in envir.
Here an example:
env.script1 <- new.env()
env.script2 <- new.env()
evalq({
x <- 2
p <- 3
z <- 5
} ,envir = env.script1,enclos=.GlobalEnv)
evalq({
h <- x +2
} ,envir = env.script2,enclos=myenv.script1)`
You can see that all variable are created within the environnment ( like local)
env.script2$h
[1] 4
env.script1$p
[1] 3
> env.script1$x
[1] 2
First, given your use case, I don't see how an export function is any better than using good (?) old-fashioned <<-. You could just do
(function() {
pollute <- the(namespace)
useful <<- result
})()
which will give the same result as what's in your example.
Second, rather than anonymous functions, it seems better form to use local, which allows you to run involved computations without littering your workspace with various temporary objects.
local({
pollute <- the(namespace)
useful <<- result
})
ETA: If it's important for whatever reason to avoid modifying an existing variable called useful, put an exists check in there. The same applies to the other solutions presented.
local({
.....
useful <- result
if(!exists("useful", globalenv())) useful <<- useful
})

Additional function name but with fewer arguments [duplicate]

This question already has answers here:
How to curry a ... argument by position in R?
(2 answers)
Closed 9 years ago.
I need to create additional name for my_function(i,x) (where i can be an integer from 1 to 25). I'd like it to work like this
my_function1(x) sames as my_function(1,x)
my_function2(x) sames as my_function(2,x)
my_function3(x) sames as my_function(3,x)
...
my_function25(x) sames as my_function(25,x)
One way to do achieve this would be:
my_function1 <- function (x) my_function(1, x)
my_function2 <- function (x) my_function(2, x)
my_function3 <- function (x) my_function(3, x)
...
But since there are 25 of them it would be reasonable to make it in a loop. For this I've tried:
for(i in 1:25){
assign(paste("my_function",i,sep=""),function(x) my_function(i,x))
}
but it doesn't work since i is passed by reference and in the end the result was
my_function1(x) sames as my_function(25,x)
my_function2(x) sames as my_function(25,x)
my_function3(x) sames as my_function(25,x)
...
How can I pass "i" by value? Or perhaps there is some other way...
Why would I want to do this? I'm improving someones else R package in terms of efficiency but at the same time I need it to be compatible with old version.
This is called currying, and is a part of functional programming.
library(functional)
myf <- function(a,x) cat(a,x,"\n")
myf1 <- Curry(myf, a=1)
myf1(5)
for(i in seq(25)) assign(paste0("myf",i), Curry(myf,a=i) )
> myf15(5)
15 5
I guess there's an important question here as to why you'd want to do this. This seems like exactly the kind of thing you'd want arguments not many related functions for.
Well, you can achieve the same result, using base functions too.
The trick is to force (force) the evaluation of i at each iteration and assign your function in the .Globalenv (or the environment you like)
my_function <- function(a, b) a + b
lapply(1:10, function(i) {
force(i)
assign(paste0("my_function", i), function(x) my_function(i, x), envir = .GlobalEnv)
}
)
my_function1(10)
## [1] 11
my_function9(10)
## [1] 19
I think bquote will help here:
for(i in 1:2){
assign(paste("my_function",i,sep=""), bquote( function(x) my_function( i = .(i) , x ) ) )
}
>my_function2
# function(x) my_function(i = 2L, x)
But the point still stands - why would you want to do this?

Resources