Use a list to name objects - r

I have a list
l <- list('a','b','c')
That I want to use iteratively to create object names in a function. For example:
f <- function(){
object <- 1 + 2
}
I wanted to run the operation a+b multiple times and each time create a new object with each time naming an object based on something on my list. Is there a way to use a list to iteratively name an object?
I hope this question makes sense...
EDIT:
I am hoping that my output would give me 3 objects:
a, b, and c. Each of these would equal 3 based on the function.

Not sure what the overall goal is but here's a way to do that :
f <- function(){
object <- p + q
}
p <- 10
q <- 5
list2env(setNames(replicate(length(l), f(), simplify = FALSE), l), .GlobalEnv)
a
#[1] 15
b
#[1] 15
c
#[1] 15

Related

Is there a Reduce operator in R whose operation is not binary?

R has the Reduce function that lets one recursively build an object. The essential arguments of Reduce are a binary function f and a vector x. A binary function takes two arguments of the same type and returns a value of that same type.
It would be useful to have a version of reduce where the function takes objects of different types and whose return value is of one of those two types. Does R have such a function? Below there is a working example.
df <- data.frame(A = 1:4, B = letters[1:4])
v <- list(LETTERS[1:4], 4:1)
op <- function(df, s) {
df[[ncol(df)+ 1]] <- s
df
}
reduce.with.list <- function(x, v, op) {
recur.op <- function(x, n) {
x <- op(x, v[[n]])
if (n == length(v)) return(x)
recur.op(x, n+1)
}
recur.op(x, 1)
}
reduce.with.list(df, v, op)
The result of running this code is:
A B V3 V4
1 1 a A 4
2 2 b B 3
3 3 c C 2
4 4 d D 1
I want to make clear that this is just an example. My objective is not to add columns to a dataframe. The function I am looking for should be able to take a vector of length 1 of type T1, a list of objects of type T2 and an operator op that takes two arguments, one of type T1 and the other of type T2 and return an object of type T1.

Recursive function using loop applied to a data frame

I am trying to create a function that calculates recursive form which will applied to a data frame. I have a data frame object which has 6 columns and each one has a 10 rows.
Data <- data.frame()
for(i in 1:(10)) {Data <- rbind(Data ,c(A=i+2,B=sqrt(i),C=1/i,D=i/120,E=i/250,F=i+3)); names(Data ) <- letters[1:6]}
I want to use the following recursive function :
f<-function(x,para,c,d,e){
# Constant
h=0.25
#para_para<-c() set up the parameters of the model
y1=para[1]
y2=para[2]
y3=para[3]
# Terminal condition for the A and B at time T
A=0
B=0
# Recursion back to time t
steps<-round(d*250,0)
for (i in 1:steps){
A= A+ e*x +y1*B
B= y2*B+y3
}
f = exp(log(c)*x -A + B*h )
return(f)
}
Under some specific values the function works :
> para<-c(1,-0.001,0.5)
> W<-f(x=0.5,para,c=0.1,d=0.2,e=0.3)
> W
[1] 4.647528e-15
I want to apply this funtion to my data frame with respect the rows of my data frame with : c=Data$c,d=Data$d,e=Data$e. I tried this code with some warning:
f(x=0.5,para,c=Data$c,d=Data$d,e=Data$e)
[1] 0.6844600 0.4820543 0.3920244 0.3381478 0.3012412 0.2738966 0.2525667
[8] 0.2353113 0.2209680 0.2087918
Warning message:
In 1:steps : numerical expression has 10 elements: only the first used
In fact thisis not correct, because the function is applied only for the first competent of d which is 2=d*250. The problem is the steps because it changes and takes values from the rows of the data frame. One of the correct way to do it is :
> mapply(function(c,d,e) f(x=0.5,para,c,d,e),c=Data$c,d=Data$d,e=Data$e)
[1] 6.844600e-01 1.761008e-01 5.190021e-02 1.609455e-02 5.113622e-03
[6] 1.645010e-03 3.185962e-04 1.031473e-04 3.339030e-05 1.078962e-05
What I want to find is a simple way and direct way just using the f without using mapply.
Thanks in advance.
I think you know where the problem lies. Modifying your function slightly to see to that it can take the vector arguments:
f1<-function(x,para,c,d,e){
# Constant
h=0.25
#para_para<-c() set up the parameters of the model
y1=para[1]
y2=para[2]
y3=para[3]
# Recursion back to time t
f <- rep(NA, length(c))
for (i in 1:length(c)){
A=0
B=0
steps<-round(d[i]*250,0)
for (j in 1:steps){
A= A+ e[i]*x +y1*B
B= y2*B+y3
}
f[i] = exp(log(c[i])*x -A + B*h )
}
return(f)
}
Now it can take both scalar and vector arguments.
f1(x=0.5,para,c=0.1,d=0.2,e=0.3)
#[1] 4.647528e-15
f1(x=0.5, para, c=Data$c, d=Data$d, e=Data$e)
#[1] 6.844600e-01 1.761008e-01 5.190021e-02 1.609455e-02 5.113622e-03 1.645010e-03 3.185962e-04 1.031473e-04 3.339030e-05
#[10] 1.078962e-05
Does this give you what you want?
Using a lambda function inside apply:
> apply(Data, 1, function (p) f(x=0.5, para, p['c'], p['d'], p['e']))
1 2 3 4 5 6 7 8
6.844600e-01 1.761008e-01 5.190021e-02 1.609455e-02 5.113622e-03 1.645010e-03 3.185962e-04 1.031473e-04
9 10
3.339030e-05 1.078962e-05
You could also rewrite your function so it works more compactly with apply:
f2<-function(cde, x, para){
c <- cde[1]
d <- cde[2]
e <- cde[3]
# Constant
h=0.25
#para_para<-c() set up the parameters of the model
y1=para[1]
y2=para[2]
y3=para[3]
# Terminal condition for the A and B at time T
A=0
B=0
# Recursion back to time t
steps<-round(d*250,0)
for (i in 1:steps){
A= A+ e*x +y1*B
B= y2*B+y3
}
f = exp(log(c)*x -A + B*h )
return(f)
}
> apply(Data[,c('c','d','e')], 1, f2, x=0.5, para)
1 2 3 4 5 6 7 8
6.844600e-01 1.761008e-01 5.190021e-02 1.609455e-02 5.113622e-03 1.645010e-03 3.185962e-04 1.031473e-04
9 10
3.339030e-05 1.078962e-05
> all.equal(apply(Data[,c('c','d','e')], 1, f2, x=0.5, para),
apply(Data, 1, function (p) f(x=0.5, para, p['c'], p['d'], p['e'])))
[1] TRUE

Implementation of standard recycling rules

One nice feature of R which is related to its inherent vectorized nature is the recycling rule described in An Introduction to R in Section 2.2.
Vectors occurring in the same expression need not all be of the same length. If they are not, the value of the expression is a vector with the same length as the longest vector which occurs in the expression. Shorter vectors in the expression are recycled as often as need be (perhaps fractionally) until they match the length of the longest vector. In particular a constant is simply repeated.
Most standard functions use this, but the code that does so is buried in the underlying C code.
Is there a canonical way to implement the standard recycling rules for a function entirely in R code? That is, given a function like
mock <- function(a, b, c) {
# turn a, b, and c into appropriate recycled versions
# do something with recycled a, b, and c in some appropriately vectorized way
}
where a, b, and c are vectors, possibly of different lengths and unknown types/classes, is there a canonical way to get a new set of vectors which are recycled according to the standard recycling rules? In particular, I can't assume that "do something" step will do the proper recycling itself, so I need to do it myself beforehand.
I've used this in the past,
expand_args <- function(...){
dots <- list(...)
max_length <- max(sapply(dots, length))
lapply(dots, rep, length.out = max_length)
}
I'd likely use the length.out argument of rep() to do most of the real work.
Here's an example that creates a better.data.frame() function (it should really be called "better".data.frame()), which places no restrictions on the lengths of the vectors it's handed as arguments. In this case, I recycle all of the vectors to the length of the the longest one, but you can obviously adapt this to serve your own recycling needs!
better.data.frame <- function(...) {
cols <- list(...)
names(cols) <- sapply(as.list(match.call()), deparse)[-1]
# Find the length of the longest vector
# and then recycle all columns to that length.
n <- max(lengths(cols))
cols <- lapply(cols, rep, length.out = n)
as.data.frame(cols)
}
# Try it out
a <- Sys.Date() + 0:9
b <- 1:3
c <- letters[1:4]
data.frame(a,b,c)
# Error in data.frame(a, b, c) :
# arguments imply differing number of rows: 10, 3, 4
better.data.frame(a,b,c)
# a b c
# 1 2012-02-17 1 a
# 2 2012-02-18 2 b
# 3 2012-02-19 3 c
# 4 2012-02-20 1 d
# 5 2012-02-21 2 a
# 6 2012-02-22 3 b
# 7 2012-02-23 1 c
# 8 2012-02-24 2 d
# 9 2012-02-25 3 a
# 10 2012-02-26 1 b
One short-and-dirty route for numerical arguments is to rely on cbind's automatic recycling. For example:
f.abc <- function(a,b,c) {
df.abc <- as.data.frame( suppressWarnings( cbind(a=a, b=b, c=c) ) )
#Then use, for example, with() to use a, b and c inside the data frame,
#or apply(df.abc,1, ...)
}
It does rely heavily on there being no other legitimate cause for warnings, though.

R: creating a named vector from variables

Inside a function I define a bunch of scalar variables like this:
a <- 10
b <- a*100
c <- a + b
At the end of the function, I want to return a,b,c in a named vector, with the same names as the variables, with minimal coding, i.e. I do not want to do:
c( a = a, b = b, c = c )
Is there a language construct that does this? For example, if I simply do return(c(a,b,c)) it returns an unnamed vector, which is not what I want. I currently have a hacky way of doing this:
> cbind(a,b,c)[1,]
a b c
10 1000 1010
Is there perhaps a better, less hacky, way?
Here's a function to do that for you, which also allows you to optionally name some of the values. There's not much to it, except for the trick to get the unevaluated expression and deparse it into a single character vector.
c2 <- function(...) {
vals <- c(...)
if (is.null(names(vals))) {
missing_names <- rep(TRUE, length(vals))
} else {
missing_names <- names(vals) == ""
}
if (any(missing_names)) {
names <- vapply(substitute(list(...))[-1], deparse, character(1))
names(vals)[missing_names] <- names[missing_names]
}
vals
}
a <- 1
b <- 2
c <- 3
c2(a, b, d = c)
# a b d
# 1 2 3
Note that it's not guaranteed to produce syntactically valid names. If you want that, apply the make.names function to the names vector.
c2(mean(a,b,c))
# mean(a, b, c)
# 1
Also, as with any function that uses substitute, c2 is more suited for interactive use than to be used within another function.

How to assign from a function which returns more than one value?

Still trying to get into the R logic... what is the "best" way to unpack (on LHS) the results from a function returning multiple values?
I can't do this apparently:
R> functionReturningTwoValues <- function() { return(c(1, 2)) }
R> functionReturningTwoValues()
[1] 1 2
R> a, b <- functionReturningTwoValues()
Error: unexpected ',' in "a,"
R> c(a, b) <- functionReturningTwoValues()
Error in c(a, b) <- functionReturningTwoValues() : object 'a' not found
must I really do the following?
R> r <- functionReturningTwoValues()
R> a <- r[1]; b <- r[2]
or would the R programmer write something more like this:
R> functionReturningTwoValues <- function() {return(list(first=1, second=2))}
R> r <- functionReturningTwoValues()
R> r$first
[1] 1
R> r$second
[1] 2
--- edited to answer Shane's questions ---
I don't really need giving names to the result value parts. I am applying one aggregate function to the first component and an other to the second component (min and max. if it was the same function for both components I would not need splitting them).
(1) list[...]<- I had posted this over a decade ago on r-help. Since then it has been added to the gsubfn package. It does not require a special operator but does require that the left hand side be written using list[...] like this:
library(gsubfn) # need 0.7-0 or later
list[a, b] <- functionReturningTwoValues()
If you only need the first or second component these all work too:
list[a] <- functionReturningTwoValues()
list[a, ] <- functionReturningTwoValues()
list[, b] <- functionReturningTwoValues()
(Of course, if you only needed one value then functionReturningTwoValues()[[1]] or functionReturningTwoValues()[[2]] would be sufficient.)
See the cited r-help thread for more examples.
(2) with If the intent is merely to combine the multiple values subsequently and the return values are named then a simple alternative is to use with :
myfun <- function() list(a = 1, b = 2)
list[a, b] <- myfun()
a + b
# same
with(myfun(), a + b)
(3) attach Another alternative is attach:
attach(myfun())
a + b
ADDED: with and attach
I somehow stumbled on this clever hack on the internet ... I'm not sure if it's nasty or beautiful, but it lets you create a "magical" operator that allows you to unpack multiple return values into their own variable. The := function is defined here, and included below for posterity:
':=' <- function(lhs, rhs) {
frame <- parent.frame()
lhs <- as.list(substitute(lhs))
if (length(lhs) > 1)
lhs <- lhs[-1]
if (length(lhs) == 1) {
do.call(`=`, list(lhs[[1]], rhs), envir=frame)
return(invisible(NULL))
}
if (is.function(rhs) || is(rhs, 'formula'))
rhs <- list(rhs)
if (length(lhs) > length(rhs))
rhs <- c(rhs, rep(list(NULL), length(lhs) - length(rhs)))
for (i in 1:length(lhs))
do.call(`=`, list(lhs[[i]], rhs[[i]]), envir=frame)
return(invisible(NULL))
}
With that in hand, you can do what you're after:
functionReturningTwoValues <- function() {
return(list(1, matrix(0, 2, 2)))
}
c(a, b) := functionReturningTwoValues()
a
#[1] 1
b
# [,1] [,2]
# [1,] 0 0
# [2,] 0 0
I don't know how I feel about that. Perhaps you might find it helpful in your interactive workspace. Using it to build (re-)usable libraries (for mass consumption) might not be the best idea, but I guess that's up to you.
... you know what they say about responsibility and power ...
Usually I wrap the output into a list, which is very flexible (you can have any combination of numbers, strings, vectors, matrices, arrays, lists, objects int he output)
so like:
func2<-function(input) {
a<-input+1
b<-input+2
output<-list(a,b)
return(output)
}
output<-func2(5)
for (i in output) {
print(i)
}
[1] 6
[1] 7
I put together an R package zeallot to tackle this problem. zeallot includes a multiple assignment or unpacking assignment operator, %<-%. The LHS of the operator is any number of variables to assign, built using calls to c(). The RHS of the operator is a vector, list, data frame, date object, or any custom object with an implemented destructure method (see ?zeallot::destructure).
Here are a handful of examples based on the original post,
library(zeallot)
functionReturningTwoValues <- function() {
return(c(1, 2))
}
c(a, b) %<-% functionReturningTwoValues()
a # 1
b # 2
functionReturningListOfValues <- function() {
return(list(1, 2, 3))
}
c(d, e, f) %<-% functionReturningListOfValues()
d # 1
e # 2
f # 3
functionReturningNestedList <- function() {
return(list(1, list(2, 3)))
}
c(f, c(g, h)) %<-% functionReturningNestedList()
f # 1
g # 2
h # 3
functionReturningTooManyValues <- function() {
return(as.list(1:20))
}
c(i, j, ...rest) %<-% functionReturningTooManyValues()
i # 1
j # 2
rest # list(3, 4, 5, ..)
Check out the package vignette for more information and examples.
functionReturningTwoValues <- function() {
results <- list()
results$first <- 1
results$second <-2
return(results)
}
a <- functionReturningTwoValues()
I think this works.
There's no right answer to this question. I really depends on what you're doing with the data. In the simple example above, I would strongly suggest:
Keep things as simple as possible.
Wherever possible, it's a best practice to keep your functions vectorized. That provides the greatest amount of flexibility and speed in the long run.
Is it important that the values 1 and 2 above have names? In other words, why is it important in this example that 1 and 2 be named a and b, rather than just r[1] and r[2]? One important thing to understand in this context is that a and b are also both vectors of length 1. So you're not really changing anything in the process of making that assignment, other than having 2 new vectors that don't need subscripts to be referenced:
> r <- c(1,2)
> a <- r[1]
> b <- r[2]
> class(r)
[1] "numeric"
> class(a)
[1] "numeric"
> a
[1] 1
> a[1]
[1] 1
You can also assign the names to the original vector if you would rather reference the letter than the index:
> names(r) <- c("a","b")
> names(r)
[1] "a" "b"
> r["a"]
a
1
[Edit] Given that you will be applying min and max to each vector separately, I would suggest either using a matrix (if a and b will be the same length and the same data type) or data frame (if a and b will be the same length but can be different data types) or else use a list like in your last example (if they can be of differing lengths and data types).
> r <- data.frame(a=1:4, b=5:8)
> r
a b
1 1 5
2 2 6
3 3 7
4 4 8
> min(r$a)
[1] 1
> max(r$b)
[1] 8
If you want to return the output of your function to the Global Environment, you can use list2env, like in this example:
myfun <- function(x) { a <- 1:x
b <- 5:x
df <- data.frame(a=a, b=b)
newList <- list("my_obj1" = a, "my_obj2" = b, "myDF"=df)
list2env(newList ,.GlobalEnv)
}
myfun(3)
This function will create three objects in your Global Environment:
> my_obj1
[1] 1 2 3
> my_obj2
[1] 5 4 3
> myDF
a b
1 1 5
2 2 4
3 3 3
Lists seem perfect for this purpose. For example within the function you would have
x = desired_return_value_1 # (vector, matrix, etc)
y = desired_return_value_2 # (vector, matrix, etc)
returnlist = list(x,y...)
} # end of function
main program
x = returnlist[[1]]
y = returnlist[[2]]
Yes to your second and third questions -- that's what you need to do as you cannot have multiple 'lvalues' on the left of an assignment.
How about using assign?
functionReturningTwoValues <- function(a, b) {
assign(a, 1, pos=1)
assign(b, 2, pos=1)
}
You can pass the names of the variable you want to be passed by reference.
> functionReturningTwoValues('a', 'b')
> a
[1] 1
> b
[1] 2
If you need to access the existing values, the converse of assign is get.
[A]
If each of foo and bar is a single number, then there's nothing wrong with c(foo,bar); and you can also name the components: c(Foo=foo,Bar=bar). So you could access the components of the result 'res' as res[1], res[2]; or, in the named case, as res["Foo"], res["BAR"].
[B]
If foo and bar are vectors of the same type and length, then again there's nothing wrong with returning cbind(foo,bar) or rbind(foo,bar); likewise nameable. In the 'cbind' case, you would access foo and bar as res[,1], res[,2] or as res[,"Foo"], res[,"Bar"]. You might also prefer to return a dataframe rather than a matrix:
data.frame(Foo=foo,Bar=bar)
and access them as res$Foo, res$Bar. This would also work well if foo and bar were of the same length but not of the same type (e.g. foo is a vector of numbers, bar a vector of character strings).
[C]
If foo and bar are sufficiently different not to combine conveniently as above, then you shuld definitely return a list.
For example, your function might fit a linear model and
also calculate predicted values, so you could have
LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit
and then you would return list(Foo=foo,Bar=bar) and then access the summary as res$Foo, the predicted values as res$Bar
source: http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html
Year 2021 and this is something I frequently use.
tidyverse package has a function called lst that assigns name to the list elements when creating the list.
Post which I use list2env() to assign variable or use the list directly
library(tidyverse)
fun <- function(){
a<-1
b<-2
lst(a,b)
}
list2env(fun(), envir=.GlobalEnv)#unpacks list key-values to variable-values into the current environment
This is only for the sake of completeness and not because I personally prefer it. You can pipe %>% the result, evaluate it with curly braces {} and write variables to the parent environment using double-arrow <<-.
library(tidyverse)
functionReturningTwoValues() %>% {a <<- .[1]; b <<- .[2]}
UPDATE:
Your can also use the multiple assignment operator from the zeallot package:: %<-%
c(a, b) %<-% list(0, 1)
I will post a function that returns multiple objects by way of vectors:
Median <- function(X){
X_Sort <- sort(X)
if (length(X)%%2==0){
Median <- (X_Sort[(length(X)/2)]+X_Sort[(length(X)/2)+1])/2
} else{
Median <- X_Sort[(length(X)+1)/2]
}
return(Median)
}
That was a function I created to calculate the median. I know that there's an inbuilt function in R called median() but nonetheless I programmed it to build other function to calculate the quartiles of a numeric data-set by using the Median() function I just programmed. The Median() function works like this:
If a numeric vector X has an even number of elements (i.e., length(X)%%2==0), the median is calculated by averaging the elements sort(X)[length(X)/2] and sort(X)[(length(X)/2+1)].
If Xdoesn't have an even number of elements, the median is sort(X)[(length(X)+1)/2].
On to the QuartilesFunction():
QuartilesFunction <- function(X){
X_Sort <- sort(X) # Data is sorted in ascending order
if (length(X)%%2==0){
# Data number is even
HalfDN <- X_Sort[1:(length(X)/2)]
HalfUP <- X_Sort[((length(X)/2)+1):length(X)]
QL <- Median(HalfDN)
QU <- Median(HalfUP)
QL1 <- QL
QL2 <- QL
QU1 <- QU
QU2 <- QU
QL3 <- QL
QU3 <- QU
Quartiles <- c(QL1,QU1,QL2,QU2,QL3,QU3)
names(Quartiles) = c("QL (1)", "QU (1)", "QL (2)", "QU (2)","QL (3)", "QU (3)")
} else{ # Data number is odd
# Including the median
Half1DN <- X_Sort[1:((length(X)+1)/2)]
Half1UP <- X_Sort[(((length(X)+1)/2)):length(X)]
QL1 <- Median(Half1DN)
QU1 <- Median(Half1UP)
# Not including the median
Half2DN <- X_Sort[1:(((length(X)+1)/2)-1)]
Half2UP <- X_Sort[(((length(X)+1)/2)+1):length(X)]
QL2 <- Median(Half2DN)
QU2 <- Median(Half2UP)
# Methods (1) and (2) averaged
QL3 <- (QL1+QL2)/2
QU3 <- (QU1+QU2)/2
Quartiles <- c(QL1,QU1,QL2,QU2,QL3,QU3)
names(Quartiles) = c("QL (1)", "QU (1)", "QL (2)", "QU (2)","QL (3)", "QU (3)")
}
return(Quartiles)
}
This function returns the quartiles of a numeric vector by using three methods:
Discarding the median for the calculation of the quartiles when the number of elements of the numeric vector Xis odd.
Keeping the median for the calculation of the quartiles when the number of elements of the numeric vector Xis odd.
Averaging the results obtained by using methods 1 and 2.
When the number of elements in the numeric vector X is even, the three methods coincide.
The result of the QuartilesFunction() is a vector that depicts the first and third quartiles calculated by using the three methods outlined.
With R 3.6.1, I can do the following
fr2v <- function() { c(5,3) }
a_b <- fr2v()
(a_b[[1]]) # prints "5"
(a_b[[2]]) # prints "3"
To obtain multiple outputs from a function and keep them in the desired format you can save the outputs to your hard disk (in the working directory) from within the function and then load them from outside the function:
myfun <- function(x) {
df1 <- ...
df2 <- ...
save(df1, file = "myfile1")
save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")

Resources