How to assign from a function with multiple outputs? [duplicate] - r

This question already has answers here:
How to assign from a function which returns more than one value?
(16 answers)
Closed 10 years ago.
Is there a way to output e.g. 2 objects without using list()?
my.fun=function(vector, index)
{
a=fun.a(vector, index)
b=fun.b(vector, index)
output=list(a,b)
}
Or to output 2 lists of objects? Considering that I could also have:
c=fun.a(vector, index)
d=fun.b(vector, index)
And wanted list(a,b) and another list(c,d) for the same function.
This is just a small example for what I am looking for, my function is applied to large objects and I export them as a list, however I would like to export also an intermidiate calculation. One option would be to merge all in the same list, but I would like to know if there is another solution to this.

You can only return one object in a function. But you have some other options. You could assign intermediate objects to the global environment (you need to be careful not to overwrite anything) or you could pass an environment to your function and assign objects to it.
Here's an example of the latter suggestion:
fun <- function(x, env) {
env$x2 <- x^2
x^3
}
set.seed(21)
x <- rnorm(10)
myEnv <- new.env()
fun(x, myEnv)
# [1] 4.987021e-01 1.424421e-01 5.324742e+00 -2.054855e+00 1.061014e+01
# [6] 8.125632e-02 -3.871369e+00 -8.171530e-01 2.559674e-04 -1.370917e-08
myEnv$x2
# [1] 6.288699e-01 2.727464e-01 3.049292e+00 1.616296e+00 4.828521e+00
# [6] 1.876023e-01 2.465527e+00 8.740486e-01 4.031405e-03 5.728058e-06

I found list2env ideal for what you're describing; the trickiest bit, for me, was working out what to give for the env parameter:
f=function(){
list(a=1,b="my string")
}
ret=f()
list2env(ret,env=environment())
#a=ret$a;b=ret$b #Same as previous line
print(a);print(b) #1 and "my string"

The return() object needs to be one thing ... a list or a vector. Since a list can be that "one thing" and a list can hold many things of many classes, all you need to di is initialize a list structure and then push things into it until you are ready to retrun that structure to the calling anvironment.
If you want to "push" individual items into the global (or other environment), you can use <<- or assign, although that is considered ugly practice and a violation of the paradigm of functional programming.

I believe you are limited to using lists, but you can combine them like:
> list(list(a=1, b=2), list(c=3, d=4))
[[1]]
[[1]]$a
[1] 1
[[1]]$b
[1] 2
[[2]]
[[2]]$c
[1] 3
[[2]]$d
[1] 4

Related

Why are functions only sometimes first class variables in R? Using them from built-in data structures causes "Error: attempt to apply non-function"

I am trying to understand how first class functions work in R. I had understood that functions were first class in R, but was sorely disappointed when applying that understanding. When a function is saved to a list, whether that be as an ordinary list, a vector or a dictionary style list or vector, it is no longer callable, leading to the following error:
Error: attempt to apply non-function
e.g.
print_func <- function() {
print('hi')
}
print_func()
[1] "hi"
my_list = list(print_func)
my_list[0]()
Error: attempt to apply non-function
my_vector = c(print_func)
my_vector[0]()
Error: attempt to apply non-function
my_map <- c("a" = print_func)
my_map["a"]()
Error: attempt to apply non-function
So why is this? Does R not actually treat functions as first class members in all cases, or is there another reason why this occurs?
I see that R vectors also do unexpected things (for me - perhaps not for experienced R users) to nested arrays:
nested_vector <- c("a" = c("b" = 1))
nested_vector["a"]
<NA>
NA
nested_vector["a.b"]
a.b
1
Here it makes sense to me that "a.b" might reference the sub-member of the key "b" under the key "a". But apparently that logic goes out the window when trying to call the upper level key "a".
R is 1-based; so, you refer to the first element of a vector using index 1 (and not 0 like in python).
There are two approaches to accessing list elements:
accessing list elements while keeping a list (return a list containing the desired elements)
pulling an element out of a list
In the first case, the subsetting is done using a single pair of brackets ([]) and you will always get a list back. Note that this is different from python where you get a list only if you select more than one element (lst = [fun1, fun2]; lst[0] return fun1 and not a one-element list like R while lst[0:2] returns a list).
In the second approach, the subsetting is done using a double pair of brackets ([[]]). you basically pull an element completely out of a list; more like subsetting one element out of a list in python.
print_func <- function() {
print('hi')
}
print_func()
my_list = list(print_func)
mode(my_list[1]) # return a list (not a function); so, it's not callable
[1] "list"
mode(my_list[[1]]) # return a function; so, it's callable
[1] "function"
my_list[1]() # error
my_list[[1]]() # works
[1] "hi"
#
my_vector = c(print_func)
mode(my_vector) # a list, so, not callable
[1] "list"
my_vector[1]() # error because it returns a list and not a function
my_vector[[1]]() # works
[1] "hi"
When subsetting with names, the same logic of single and double pair of brackets applies
my_map <- c("a" = print_func)
mode(my_map) # list, so, not callable
[1] "list"
my_map["a"]() # error
my_map[["a"]]() # works
[1] "hi"
Limey pointed out my 2 issues in the comments. I was using a 0-index and I was using single brackets. If I use a 1-index and double brackets it works, and functions are treated as first class variables.
My issue is resolved, and hopefully I won't make that same mistake again.

Change name of column vector in R [duplicate]

so we know that R have list() variable, and also know that R has function call names() to give names for variable. For example :
a=30
names(a)="number"
a
# number
# 30
But now, I want to give a list variable a name, like this :
b=list()
names(b)="number"
and it returns error message like this :
Error in names(b) = "number" :
'names' attribute [1] must be the same length as the vector [0]
What I have suppose to do? I do this because I need many list variables. Or, do you have another way so I can make many list variables without playing with its name?
Since #akrun doesn't need any more points, here is an example showing how you can assign names to a list:
lst <- list(a="one", b="two", c=c(1:3))
names(lst)
[1] "a" "b" "c"
names(lst) <- c("x", "y", "z")
> lst
$x
[1] "one"
$y
[1] "two"
$z
[1] 1 2 3
It seems as though you are interested in labeling the object itself rather than the elements in it. The key is that the names attribute for a list object is necessarily assigned to its elements. One option, since you said you have many list objects, is to store the lists in a big list and then you can assign names to the big list, and the elements within the list-objects can be named too.
allLists <- list('number' = list())
> allLists
$number
list()
Another option, you can make use of the label feature in the Hmisc package. It modifies most common objects in R to have a subclass "labelled" and so whenever you print the list it shows the label. It is good for documentation and organizing the workspace a bit better, but caveat it's very easy to accidentally cast labelled objects to a non-labelled class and to confuse methods that don't think to search for more than one class.
library(Hmisc)
p <- list()
label(p) <- 'number'
> p
number
list()
Another option is to make the "name" of your list object an actual element of the list. You'll see in a lot of complex R data structures, this is the preferred way of storing labels, titles, or names when such a need arises and isn't met by the base R data structure.
b <- list('name' = 'number')
The last possibility is that you need a placeholder to store the "names" attribute of the elements you haven't yet populated the list with. If the elements are of known length and of known type, you can allocate such a vector using e.g. numeric(1) a sort-of "primed" vector which can be named. If you don't know the data structure of your output, I would not use this approach since it can be a real memory hog to "build" data structures in R.
Other possibilities are
as.list(a)
# $`number`
# [1] 30
# or
setNames(list(unname(a)),'number')
# $`number`
# [1] 30
# or named list with named vector
setNames(list(a), 'number')
# $`number`
# number
# 30

assign names of elements in a list in a for loop [duplicate]

so we know that R have list() variable, and also know that R has function call names() to give names for variable. For example :
a=30
names(a)="number"
a
# number
# 30
But now, I want to give a list variable a name, like this :
b=list()
names(b)="number"
and it returns error message like this :
Error in names(b) = "number" :
'names' attribute [1] must be the same length as the vector [0]
What I have suppose to do? I do this because I need many list variables. Or, do you have another way so I can make many list variables without playing with its name?
Since #akrun doesn't need any more points, here is an example showing how you can assign names to a list:
lst <- list(a="one", b="two", c=c(1:3))
names(lst)
[1] "a" "b" "c"
names(lst) <- c("x", "y", "z")
> lst
$x
[1] "one"
$y
[1] "two"
$z
[1] 1 2 3
It seems as though you are interested in labeling the object itself rather than the elements in it. The key is that the names attribute for a list object is necessarily assigned to its elements. One option, since you said you have many list objects, is to store the lists in a big list and then you can assign names to the big list, and the elements within the list-objects can be named too.
allLists <- list('number' = list())
> allLists
$number
list()
Another option, you can make use of the label feature in the Hmisc package. It modifies most common objects in R to have a subclass "labelled" and so whenever you print the list it shows the label. It is good for documentation and organizing the workspace a bit better, but caveat it's very easy to accidentally cast labelled objects to a non-labelled class and to confuse methods that don't think to search for more than one class.
library(Hmisc)
p <- list()
label(p) <- 'number'
> p
number
list()
Another option is to make the "name" of your list object an actual element of the list. You'll see in a lot of complex R data structures, this is the preferred way of storing labels, titles, or names when such a need arises and isn't met by the base R data structure.
b <- list('name' = 'number')
The last possibility is that you need a placeholder to store the "names" attribute of the elements you haven't yet populated the list with. If the elements are of known length and of known type, you can allocate such a vector using e.g. numeric(1) a sort-of "primed" vector which can be named. If you don't know the data structure of your output, I would not use this approach since it can be a real memory hog to "build" data structures in R.
Other possibilities are
as.list(a)
# $`number`
# [1] 30
# or
setNames(list(unname(a)),'number')
# $`number`
# [1] 30
# or named list with named vector
setNames(list(a), 'number')
# $`number`
# number
# 30

make R function return a locked/immutable list

I have a function that returns a list.
I would like this list to be immutable, similar to the way that lockBinding prevents overwriting or editing an object.
This would look something like the following:
myfun <- function(x){
out <- list(a = 1, val = x)
make_read_only(out)
out
}
test <- myfun(9)
test$a
[1] 1
test$val
[1] 9
test
$a
[1] 1
$val
[1] 9
test$newval <- 7
Error:
Where make_read_only() is just a standin for a function or some code that accomplishes this task.
I have tried using lockBinding which would work perfectly, however the 'lock' doesn't survive being passed upward into the parent environment after the function returns its output.
I have also looked into trying to lock symbol in the parent environment, but there doesn't seem to be a way to learn what the output will be assigned to from inside the function, which is needed as an argument of lockBinding.
It seems like there might be a way to do this via returning a refernce to an environment and locking the environment using lockEnvironment() or doing something similar, but I would like to hear what options are out there to accomplish this before beginnig.
Similary, it seems like it might be achievable using R6 but I would prefer to avoid using R6 since it is not required for any other part of this codebase and again, I would just like to hear what options are availble to acheive this behaviour.
In summary, the function will return an arbitrary list. This list will be at least a few levels deep.
mylist <- myfun(list(b = list(c = 3), foo = "bar"))
The user should be able to access the sublits/elements in a straightforward way similar to current dollar-sign access
mylist$a$b$c
[1] 3
It should not be possible to edit this object.
mylist$a <- 5
Error:
It should be possible to remove the object via
rm(mylist)
mylist
Error: object 'mylist' not found
So my question is, what available options are there in R to accomplish this?
One way of achieving this could be by creating a new class (locked) along with methods for [<-, [[<-, and $<- for this new class, which will return an error.
For example:
`[[<-.locked` <- function(...) {stop("Can't assign into locked object")}
a<-list(a="a",b=2)
a
$a
[1] "a"
$b
[1] 2
class(a)<-"locked"
a[[1]]<-"moose"
Error in `[[<-.locked`(`*tmp*`, 1, value = "moose") :
Can't assign into locked object
a
$a
[1] "a"
$b
[1] 2
attr(,"class")
[1] "locked"
In that case, all your "make read only" function needs to do is redefine the class for the object as locked:
class(out)<-c("locked",class(out))

Sort a list of nontrivial elements in R

In R, I have a list of nontrivial objects (they aren't simple objects like scalars that R can be expected to be able to define an order for). I want to sort the list. Most languages allow the programmer to provide a function or similar that compares a pair of list elements that is passed to a sort function. How can I sort my list?
To make this is as simple I can, say your objects are lists with two elements, a name and a value. The value is a numeric; that's what we want to sort by. You can imagine having more elements and needing to do something more complex to sort.
The sort help page tells us that sort uses xtfrm; xtfrm in turn tells us it will use == and > methods for the class of x[i].
First I'll define an object that I want to sort:
xx <- lapply(c(3,5,7,2,4), function(i) list(name=LETTERS[i], value=i))
class(xx) <- "myobj"
Now, since xtfrm works on the x[i]'s, I need to define a [ function that returns the desired elements but still with the right class
`[.myobj` <- function(x, i) {
class(x) <- "list"
structure(x[i], class="myobj")
}
Now we need == and > functions for the myobj class; this potentially could be smarter by vectorizing these properly; but for the sort function, we know that we're only going to be passing in myobj's of length 1, so I'll just use the first element to define the relations.
`>.myobj` <- function(e1, e2) {
e1[[1]]$value > e2[[1]]$value
}
`==.myobj` <- function(e1, e2) {
e1[[1]]$value == e2[[1]]$value
}
Now sort just works.
sort(xx)
It might be considered more proper to write a full Ops function for your object; however, to just sort, this seems to be all you need. See p.89-90 in Venables/Ripley for more details about doing this using the S3 style. Also, if you can easily write an xtfrm function for your objects, that would be simpler and most likely faster.
The order function will allow you to determine the sort order for character or numeric aruments and break ties with subsequent arguments. You need to be more specific about what you want. Produce an example of a "non-trivial object" and specify the order you desire in some R object. Lists are probably the most non-vectorial objects:
> slist <- list(cc=list(rr=1), bb=list(ee=2, yy=7), zz="ww")
> slist[order(names(slist))] # alpha order on names()
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7
$cc
$cc$rr
[1] 1
$zz
[1] "ww"
slist[c("zz", "bb", "cc")] # an arbitrary ordering
$zz
[1] "ww"
$bb
$bb$ee
[1] 2
$bb$yy
[1] 7
$cc
$cc$rr
[1] 1
One option is to create a xtfrm method for your objects. Functions like order take multiple columns which works in some cases. There are also some specialized functions for specific cases like mixedsort in the gtools package.

Resources