I have built my own function, where it returns many values. I really need to extract several values at once. For example, suppose the following is my function
myfunc <- function(x,y){
res <- x+y
res2 <- x^2
res3 <- x*2
out <- list()
out$add <- res
out$squ <- res2
out$or <- res3
out$ADD <- res+res2+res3
out$fi <- res^2+res2+res3
return(out)
}
Then,
> myres
$add
[1] 7
$squ
[1] 9
$or
[1] 6
$ADD
[1] 22
$fi
[1] 64
suppose I want to extract two values at a time, for example,
myres$add, and myres$ADD
is there a way to find them automatically in R instead of repeating it. My original function is very complicated and this will help a lot.
Perhaps, you can try something like this -
res <- myfunc(6, 4)
extract_values <- c('add', 'ADD')
res[extract_values]
#$add
#[1] 10
#$ADD
#[1] 58
You could concatenate them or join in a list:
c(myres$add, myres$squ)
list(myres$add, myres$squ)
If you only want one call to myres you could also index like this:
myres[c(1, 2)]
What you want is known as destructuring, and unfortunately R does not natively support it. There are multiple packages which support this. The one with the (IMHO) nicest syntax is my own package ‘unpack’, which allows you to write positional unpacking as follows:
c[add, ., ., ADD, .] = myfunc(3, 4)
After this, the variables add and ADD are directly available to the caller.
A similar solution (more powerful but with a less nice syntax) is provided by the ‘zeallot’ package.
Related
I want to make some calculations using loop in R.
I try assign but it still does not work well.
Can anyone give me a hint about how to setting up correct variable in R, please?
# My data
data <- read.table(textConnection("
a1 a2
a1 1.00000000 0.4803088
a1 0.48030878 1.0000000
"), header = TRUE)
no <- 2
for (k in 1:no){
paste0("dat.",k) <- aggregate(data[,c(paste0("a",k),paste0("b",k), paste0("b",k))],list(data$id),mean)
paste0("cor.",k) <- cor(paste0("dat.mean.",k),use = "complete.obs")
paste0("cal.",k) <- as.data.frame(paste0("dat.mean.",k))
paste0("lm.",k) <- lm(paste0("a",k) ~ paste0("b",k),data = paste0("lm.cal.",k))
}
I'm not sure which language you are coming from (SAS maybe?) but R is a proper functional programming language and doesn't use things like macros to automate tasks. Here's a more R-like way to approach the problem
no <- 2
results <- lapply(1:no, function(k) {
# use aggregate function to make correlation calculation.
this_dat_mean <- aggregate(data[,c(paste0("y", c("f","p","c"), "_",k))], list(data$id), mean)
this_cor <- cor(this_dat_mean, use = "complete.obs")
#write.table(this_cor, "file_path", row.names=T, col.names=T, quote=F)
# calculate the lm
this_lm_cal <- as.data.frame(this_dat_mean)
this_lm <- lm(reformulate(paste0("yc_",k), paste0("yf_",k)), data = this_lm_cal)
#write.table(this_lm, "file_path2", row.names=T, col.names=T, quote=F)
list(lm=this_lm, cor=this_cor)
})
Notice that we use a function to iterate over the inputs of interest. This function has a bunch of local variables. We can return a list of values that we want to preserve from the function. We can get at them by looking at
results[[1]]$lm
results[[2]]$cor
for example. It's better to create a (possibly named) list of values in R than to create a bunch of similarly named variables.
The lm model isn't a data.frame so you can't use write.table with that. Not sure what the goal was there.
For your use case, I second the point by MrFlick and suggest rewriting your code.
However, as I sometimes myself prefer dynamically generated variables in some situations and R is messy and do allow you to do so in selective ways (some things work, some do not), I would like to briefly explain you how:
> k=4
> paste0("lm.", k)
[1] "lm.4"
> paste0("lm.", k) <- 1515
Error in paste0("lm.", k) <- 1515 :
target of assignment expands to non-language object
> assign ( paste0("lm.", k) , 1515 )
> paste0("lm.", k)
[1] "lm.4"
> eval(parse(text = paste0("lm.", k) ))
[1] 1515
> str(eval(parse(text = paste0("lm.", k) )))
num 1515
> str(paste0("lm.", k) )
chr "lm.4"
in summary: every time you use a glued-together variable, you have to refer to it through eval/parse. And remember that <-will not work as opperator - use assign()
The apply functions in R are a nice way to simplify for loops to get to an output. Is there an equivalent function that helps one avoid for loops when replacing the values of a vector? This is better understood by example...
# Take this list for example
x = list( list(a=1,b=2), list(a=3,b=4), list(a=5,b=6) )
# To get all of the "a" elements from each list, I can do
vapply(x,"[[",1,"a")
[1] 1 3 5
# If I want to change all of the "a" elements, I cannot do
vapply(x,"[[",1,"a") = 10:12
Error in vapply(x, "[[", 1, "a") = 10:12 :
could not find function "vapply<-"
# (this error was expected)
# Instead I must do something like this...
new.a = 10:12
for(i in seq_along(x)) x[[i]]$a = new.a[i]
Is there a simpler or faster alternative to using a loop?
One option would be to first unlist the list x, then replace the values named "a", and then relist the new list u based on the list structure of x.
u <- unlist(x)
u[names(u) == "a"] <- 10:12
relist(u, x)
vapply is a special case of sapply where you need to pre-specify the return type.
If you a multivariate version of sapply, the function you are looking for is mapply (or Map which is a wrapper with SIMPLIFY=FALSE`)
In general, functions with side-effects are frowned upon in R. The standard approach would be to create a new object when modifying.
You could use modlifyList to perform the modifications
xnew <- Map(modifyList, x, val = lapply(10:12,function(x) list(a = x)))
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?
I want to use information from a field and include it in a R function, e.g.:
data #name of the data.frame with only one raw
"(if(nclusters>0){OptmizationInputs[3,3]*beta[1]}else{0})" # this is the raw
If I want to use this information inside a function how could I do it?
Another example:
A=c('x^2')
B=function (x) A
B(2)
"x^2" # this is the return. I would like to have the return something like 2^2=4.
Use body<- and parse
A <- 'x^2'
B <- function(x) {}
body(B) <- parse(text = A)
B(3)
## [1] 9
There are more ideas here
Another option using plyr:
A <- 'x^2'
library(plyr)
body(B) <- as.quoted(A)[[1]]
> B(5)
[1] 25
A <- "x^2"; x <- 2
BB <- function(z){ print( as.expression(do.call("substitute",
list( parse(text=A)[[1]], list(x=eval(x) ) )))[[1]] );
cat( "is equal to ", eval(parse(text=A)))
}
BB(2)
#2^2
#is equal to 4
Managing expressions in R is very weird. substitute refuses to evaluate its first argument so you need to use do.call to allow the evaluation to occur before the substitution. Furthermore the printed representation of the expressions hides their underlying representation. Try removing the fairly cryptic (to my way of thinking) [[1]] after the as.expression(.) result.
Frequently I encounter situations where I need to create a lot of similar models for different variables. Usually I dump them into the list. Here is the example of dummy code:
modlist <- lapply(1:10,function(l) {
data <- data.frame(Y=rnorm(10),X=rnorm(10))
lm(Y~.,data=data)
})
Now getting the fit for example is very easy:
lapply(modlist,predict)
What I want to do sometimes is to extract one element from the list. The obvious way is
sapply(modlist,function(l)l$rank)
This does what I want, but I wonder if there is a shorter way to get the same result?
probably these are a little bit simple:
> z <- list(list(a=1, b=2), list(a=3, b=4))
> sapply(z, `[[`, "b")
[1] 2 4
> sapply(z, get, x="b")
[1] 2 4
and you can define a function like:
> `%c%` <- function(x, n)sapply(x, `[[`, n)
> z %c% "b"
[1] 2 4
and also this looks like an extension of $:
> `%$%` <- function(x, n) sapply(x, `[[`, as.character(as.list(match.call())$n))
> z%$%b
[1] 2 4
I usually use kohske way, but here is another trick:
sapply(modlist, with, rank)
It is more useful when you need more elements, e.g.:
sapply(modlist, with, c(rank, df.residual))
As I remember I stole it from hadley (from plyr documentation I think).
Main difference between [[ and with solutions is in case missing elements. [[ returns NULL when element is missing. with throw an error unless there exist an object in global workspace having same name as searched element. So e.g.:
dah <- 1
lapply(modlist, with, dah)
returns list of ones when modlist don't have any dah element.
With Hadley's new lowliner package you can supply map() with a numeric index or an element name to elegantly pluck components out of a list. map() is the equivalent of lapply() with some extra tricks.
library("lowliner")
l <- list(
list(a = 1, b = 2),
list(a = 3, b = 4)
)
map(l, "b")
map(l, 2)
There is also a version that simplifies the result to a vector
map_v(l, "a")
map_v(l, 1)