180

How can I return multiple objects in an R function? In Java, I would make a Class, maybe Person which has some private variables and encapsulates, maybe, height, age, etc.

But in R, I need to pass around groups of data. For example, how can I make an R function return both an list of characters and an integer?

Hack-R
  • 22,422
  • 14
  • 75
  • 131
CodeGuy
  • 28,427
  • 76
  • 200
  • 317
  • 7
    I think maybe its not intuitive to folks coming from other languages, but lists are the way to do this. So you'd have a list containing two elements: a list and a single integer. – joran Jan 20 '12 at 02:41
  • 9
    This question is very similar to [this one](http://stackoverflow.com/questions/1826519/function-returning-more-than-one-value). There are some different answers over there. – rakensi Aug 26 '15 at 11:05
  • 2
    I much prefer the *question title*, *question body text*, and *answers* here than [the other question](http://stackoverflow.com/questions/1826519/function-returning-more-than-one-value) this was closed as a duplicate of. – Mateen Ulhaq Jul 11 '20 at 07:43

6 Answers6

279

Unlike many other languages, R functions don't return multiple objects in the strict sense. The most general way to handle this is to return a list object. So if you have an integer foo and a vector of strings bar in your function, you could create a list that combines these items:

foo <- 12
bar <- c("a", "b", "e")
newList <- list("integer" = foo, "names" = bar)

Then return this list.

After calling your function, you can then access each of these with newList$integer or newList$names.

Other object types might work better for various purposes, but the list object is a good way to get started.

ChadBDot
  • 3,068
  • 1
  • 15
  • 12
  • 5
    To assign the elements in the returned list at once, look at: http://stackoverflow.com/a/15140507 – papirrin Sep 07 '14 at 01:41
37

Similarly in Java, you can create a S4 class in R that encapsulates your information:

setClass(Class="Person",
         representation(
            height="numeric",
            age="numeric"
          )
)

Then your function can return an instance of this class:

myFunction = function(age=28, height=176){
  return(new("Person",
          age=age,
          height=height))
}

and you can access your information:

aPerson = myFunction()

aPerson@age
aPerson@height
Jurn Ho
  • 69
  • 1
  • 5
RockScience
  • 17,932
  • 26
  • 89
  • 125
  • Just learning here... when would it be useful to return something like `aPerson@age` or `aPerson@height` when you obviously already have defined the age and height with `myFunction = function(age=28, height=176){...`. I just don't get it (i.e., why this is useful). So you've instantiated your class, and then you want to know details about the instance. But since you've instantiated the class, don't you already know these details? – warship Feb 16 '16 at 03:18
  • @warship it is just a dummy example to explain how to return an object. The way I build the object doesn't matter here. We could also have myFunction computing the age and the height from other parameters. – RockScience Feb 16 '16 at 03:59
  • Okay, thanks a lot. I'm just trying to learn a little bit more about doing OOP in R (S3, S4, etc), and when it would be useful. – warship Feb 16 '16 at 05:09
24

Is something along these lines what you are looking for?

x1 = function(x){
  mu = mean(x)
  l1 = list(s1=table(x),std=sd(x))
  return(list(l1,mu))
}

library(Ecdat)
data(Fair)
x1(Fair$age)
aatrujillob
  • 4,738
  • 3
  • 19
  • 32
8

You can also use super-assignment.

Rather than "<-" type "<<-". The function will recursively and repeatedly search one functional level higher for an object of that name. If it can't find one, it will create one on the global level.

Daniel Morgan
  • 179
  • 1
  • 5
5

You could use for() with assign() to create many objects. See the example from assign():

for(i in 1:6) { #-- Create objects  'r.1', 'r.2', ... 'r.6' --
    nam <- paste("r", i, sep = ".")
    assign(nam, 1:i)

Looking the new objects

ls(pattern = "^r..$")
fvfaleiro
  • 183
  • 1
  • 6
4

One way to handle this is to put the information as an attribute on the primary one. I must stress, I really think this is the appropriate thing to do only when the two pieces of information are related such that one has information about the other.

For example, I sometimes stash the name of "crucial variables" or variables that have been significantly modified by storing a list of variable names as an attribute on the data frame:

attr(my.DF, 'Modified.Variables') <- DVs.For.Analysis$Names.of.Modified.Vars
return(my.DF)

This allows me to store a list of variable names with the data frame itself.

HoneyBuddha
  • 748
  • 8
  • 15