270

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).

smci
  • 32,567
  • 20
  • 113
  • 146
mariotomo
  • 9,438
  • 8
  • 47
  • 66

16 Answers16

217

(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

G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • 29
    I accepted your answer because of the "with", but I can't reproduce what you describe for the left hand side usage of "list", all I get is "object 'a' not found" – mariotomo Mar 23 '13 at 15:42
  • 4
    It works for me. What did you try? Did you read the linked post and follow it? Did you define `list` and `[<-.result` as shown there? – G. Grothendieck Mar 23 '13 at 16:00
  • 2
    The first one is brilliant. This should be embedded into R base – Bob Jul 10 '13 at 21:39
  • 1
    Just came to +1 and note that I have been a happy user of your utility function for many years now! :-) – vijucat Jan 02 '14 at 06:37
  • 3
    Really nice, thanks for sharing. It is a pity that it is not embedded into R base. – gc5 Apr 10 '14 at 15:28
  • 13
    @G.Grothendieck, Would you mind if I put the content of your link into your answer? I think it would make it easier for for people to use it. – merlin2011 Apr 14 '14 at 07:29
  • 15
    I agree with @merlin2011; as written it seems like this syntax is embedded into R base. – knowah Jun 03 '14 at 22:52
  • 1
    the list[...] trick doesn't seem to work for me. R tells me that the output filed is missing: `list[train,test,out] = load_train_test_out('train.txt','test.txt','1.inst.txt') Error in list[train, test, out] = load_train_test_out("train.txt", "test.txt", : object 'train' not found` – Hanan Shteingart Jun 12 '14 at 07:50
  • 1
    @Hanan, You likely did not run the code in the link first. – G. Grothendieck Jun 12 '14 at 11:23
  • 7
    @G.Grothendieck I agree with merlin2011 and knowah - it would be best if the actual code that is important here (the code referenced in the link) is in the answer. It might not be a bad idea to mention that the result object doesn't need to be named list. That confused me for a little while before reading your actual code. As mentioned the answer says that you need to run the code in the link but most people aren't going to read that code right away unless it's in the answer directly - this gives the impression that this syntax is in base R. – Dason Jul 22 '14 at 17:08
  • 1
    The problem with the first one is that it puts an object called `list` in every environment you use it in. The problem with the second one is that it is not readable, especially if you want to assign the result to a local variable. The third one uses `attach`, which is not recommended. – Gabor Csardi Feb 15 '15 at 18:30
  • @Gabor Csardi, The first point is a minor disadvantage but unimportant in practice. If you are using it in a function then the object disappears when the function exits so in most cases it leaves no trace. The second seems quite readable to me. I don't like `attach` and don't normally use it but some people do and it needs to be included in any reasonably comprehensive reply. – G. Grothendieck Feb 15 '15 at 19:36
  • @G.Grothendieck I agree the first is usually a minor disadvantage, but it can lead to very obscure bugs if you carry around an object without knowing. But I agree that this could be the best solution if you want to use the `<-` for assignment. I used this, too, before finding your answer, with the single dot as variable name. But then decided that it was too risky. https://github.com/gaborcsardi/ratlab/blob/8daf400f3425bf02f3374e9a5ae4fa8df0f99167/R/multiass.R – Gabor Csardi Feb 16 '15 at 04:15
  • But it’s still useful to see the implementation, @G.Grothendieck, without having to go anywhere else. I see that it’s been the second time someone edited to include the code, this time with unison approval from the reviewers. Please, reconsider your stance on this. – Palec May 20 '15 at 08:27
  • 3
    I strongly recommend against using `attach()`. I ran into multiple problems (not errors just very ominous warnings!). See this [discussion](http://www.r-bloggers.com/to-attach-or-not-attach-that-is-the-question/) that recommends against using `attach()`. Specifically, Google R Style manual provides this advice against `attach()`: "The possibilities for creating errors when using attach are numerous. Avoid it". – Ankur Jun 22 '15 at 21:42
  • I have been using this for quite some time now and added to a package for personal use. Problem is, another package I use depends on the original package but cannot see the list. Is there a known remedy for that? – OganM Sep 01 '16 at 01:00
  • @Carton, Just install and load the gsubfn development package from github (as per the instructions in the post which I just added). – G. Grothendieck Sep 02 '16 at 11:37
  • 2
    Are there any plans to push it to the CRAN version? – OganM Sep 10 '16 at 20:27
  • list[] not working, even after installing and loading the package. – wordsforthewise Dec 06 '17 at 01:46
  • I just tried the code in the answer on both Windows and Linux Ubuntu and it worked for me. – G. Grothendieck Dec 06 '17 at 02:54
  • 1
    `list[a, b] <- myfun()` returns the error "`object 'a' not found`" – JHBonarius Dec 12 '17 at 20:14
  • It works for me. Make sure you have installed the software. Also try it on a fresh instance of R. – G. Grothendieck Dec 12 '17 at 21:07
  • 1
    `list<-` is now in the CRAN version of gsubfn 0.7-0. – G. Grothendieck Mar 16 '18 at 10:51
  • How should I change the definition to achieve `list<<-`? So assign the outputs of a function to global variables all at once? – newbie Apr 13 '18 at 12:13
  • 4
    @newbie, Replace <- with <<- in the `substitute` near the end of the source. – G. Grothendieck Apr 13 '18 at 12:20
  • @G.Grothendieck your answer is still confusing as written. Why not make it extra clear that it is not part of base R? – adn bps May 10 '18 at 01:20
  • I have my own implementation of this in a personal package and @JHBonarius's error occasionally happens to me too for no discernible reason. I am yet to find a reliable way to replicate it – OganM Jul 24 '18 at 23:27
  • @highBandWidth, I can't reproduce this. It works consistenly for me. Did you issue `library(gsubfn)` first? Suggest you try repeating it with a fresh session of R. – G. Grothendieck Dec 06 '18 at 19:20
  • @G.Grothendieck Yes, it works now. I think there was a problem with the installation. Thank you! – highBandWidth Dec 12 '18 at 00:10
  • **`attach` whill do a big mess in for loop for example!** Mentioning it in the answer without any warning is simply wrong and dangerous! Anyway, thanks for the `list[...]<-`. – Tomas Nov 05 '19 at 00:36
  • 1
    The `list[] <- function()` suggestion didn't work for me. I've installed `gsubfn` and I am on Mac OS. Any suggestion? – vpk Jul 02 '20 at 17:44
76

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 ...

Steve Lianoglou
  • 7,183
  • 2
  • 25
  • 21
  • 12
    Also I'd discourage it a lot more now than when I originally posted this answer since the [data.table](http://cran.r-project.org/web/packages/data.table/index.html) package uses the `:=` operator mucho in a much handier way :-) – Steve Lianoglou Mar 12 '13 at 00:58
59

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
Federico Giorgi
  • 10,495
  • 9
  • 42
  • 56
  • 2
    What if instead of output<-func2(5) I want to have the result in two objects? I've tried with list("a","b") <-func2(5) but it doesn't work. – skan Jul 05 '17 at 13:49
22

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.

nteetor
  • 1,175
  • 11
  • 13
17
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

I think this works.

Jojo
  • 337
  • 3
  • 8
10

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:

  1. Keep things as simple as possible.
  2. 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
Shane
  • 98,550
  • 35
  • 224
  • 217
  • edited the question in order to include your remarks. thanks. giving names to things like `r[1]` can help to make things more clear (all right, not if names like `a` come in their place). – mariotomo Dec 01 '09 at 15:02
7

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
rafa.pereira
  • 13,251
  • 6
  • 71
  • 109
6

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]]
Jos Vinke
  • 2,704
  • 3
  • 26
  • 45
Arnold
  • 153
  • 1
  • 9
  • 4
    How can you assign both variables in a single comand, such as list("x","y") <-returnlist() ? I say that because if you many elements in the list you would need to run the entire function several times and that costs a time. – skan Jul 05 '17 at 13:50
4

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.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
3

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.

Steve Pitchers
  • 7,088
  • 5
  • 41
  • 41
  • ... but this requires you to know the names of the receiving variables in that environment – smci Mar 20 '15 at 11:55
  • @smci Yes. That is why the "named list" method in the question is generally better: `r <- function() { return(list(first=1, second=2)) }` and reference the results using `r$first` and `r$second`. – Steve Pitchers Mar 23 '15 at 10:09
  • 2
    Once you have your function how can you assign both variables in a single comand, such as list("x","y") <- functionReturningTwoValues('a', 'b') ? I say that because if you have many elements in the list you would need to run the entire function several times and that costs a time – skan Jul 05 '17 at 13:54
2

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
ZygD
  • 22,092
  • 39
  • 79
  • 102
1

[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

Prakhar Agrawal
  • 1,002
  • 12
  • 21
1

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)
Comfort Eagle
  • 2,112
  • 2
  • 22
  • 44
0

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:

  1. 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)].
  2. 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:

  1. Discarding the median for the calculation of the quartiles when the number of elements of the numeric vector Xis odd.
  2. Keeping the median for the calculation of the quartiles when the number of elements of the numeric vector Xis odd.
  3. 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.

Diego
  • 328
  • 2
  • 9
-1

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"
radumanolescu
  • 4,059
  • 2
  • 31
  • 44
-2

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")
Andrew Eaves
  • 168
  • 12