如何从返回多个值的函数中分配?

时间:2009-12-01 14:27:24

标签: r return-value variable-assignment assign multiple-results

仍然试图进入R逻辑...解压缩(在LHS上)返回多个值的函数的结果的“最佳”方法是什么?

我显然不能这样做:

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

我必须真的做到以下几点吗?

R> r <- functionReturningTwoValues()
R> a <- r[1]; b <- r[2]

或者R程序员会写更多这样的东西:

R> functionReturningTwoValues <- function() {return(list(first=1, second=2))}
R> r <- functionReturningTwoValues()
R> r$first
[1] 1
R> r$second
[1] 2

---编辑回答Shane的问题---

我真的不需要给结果值部分命名。我将一个聚合函数应用于第一个组件,将另一个聚合函数应用于第二个组件(minmax。如果两个组件的功能相同,则不需要拆分它们。

13 个答案:

答案 0 :(得分:168)

(1)list [...]&lt; - 十多年前我在r-help发布了这个帖子。从那以后它被添加到gsubfn包中。它不需要特殊的操作符,但需要使用list[...]来编写左侧:

library(gsubfn)  # need 0.7-0 or later
list[a, b] <- functionReturningTwoValues()

如果您只需要第一个或第二个组件,这些组件也都可以使用:

list[a] <- functionReturningTwoValues()
list[a, ] <- functionReturningTwoValues()
list[, b] <- functionReturningTwoValues()

(当然,如果您只需要一个值,那么functionReturningTwoValues()[[1]]functionReturningTwoValues()[[2]]就足够了。)

有关更多示例,请参阅引用的r-help线程。

(2)with 如果意图仅仅是随后组合多个值并且返回值被命名,则一个简单的替代方法是使用with

myfun <- function() list(a = 1, b = 2)

list[a, b] <- myfun()
a + b

# same
with(myfun(), a + b)

(3)附加另一种选择是附加:

attach(myfun())
a + b

增加:withattach

答案 1 :(得分:68)

我在互联网上偶然发现了这个聪明的黑客...我不确定它是否令人讨厌或漂亮,但它可以让你创建一个“神奇”的运算符,它允许你将多个返回值解包到它们自己的变量中。 :=函数is defined here,包含在后面的后代:

':=' <- 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)) 
}

有了这些,你可以做你想要的事情:

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

我不知道我对此感觉如何。也许您可能会发现它在交互式工作区中很有用。使用它来构建(重新)可用的库(用于大众消费)可能不是最好的主意,但我想这取决于你。

......你知道他们对责任和权力的看法......

答案 2 :(得分:42)

通常我将输出包装成一个非常灵活的列表(你可以输出数字,字符串,向量,矩阵,数组,列表,对象的任意组合)

所以喜欢:

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

答案 3 :(得分:14)

functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

我认为这很有效。

答案 4 :(得分:11)

我整理了一个R包zeallot来解决这个问题。 zeallot包含多个赋值或解包赋值运算符%<-%。运算符的LHS是要分配的任意数量的变量,使用c()调用构建。运算符的RHS是向量,列表,数据框,日期对象或具有已实现的destructure方法的任何自定义对象(请参阅?zeallot::destructure)。

以下是基于原始帖子的一些示例

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

查看包vignette以获取更多信息和示例。

答案 5 :(得分:10)

这个问题没有正确的答案。我真的取决于你在做什么数据。在上面的简单示例中,我强烈建议:

  1. 让事情尽可能简单。
  2. 尽可能保持功能矢量化是最佳做法。从长远来看,这提供了最大的灵活性和速度。
  3. 上面的值1和2是否具有名称是否重要?换句话说,为什么在这个例子中重要的是1和2被命名为a和b,而不仅仅是r [1]和r [2]?在这种情况下要理解的一件重要事情是a和b 两个长度为1的向量。因此除了有2个新向量之外,你在制作这个赋值的过程中并没有真正改变任何东西。不需要引用的下标:

    > 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
    

    如果您希望引用字母而不是索引,也可以将名称指定给原始矢量:

    > names(r) <- c("a","b")
    > names(r)
    [1] "a" "b"
    > r["a"]
    a 
    1 
    

    [编辑] 鉴于您将分别对每个向量应用min和max,我建议使用矩阵(如果a和b将是相同的长度和相同的数据类型)或数据框(如果a和b的长度相同但可以是不同的数据类型),或者使用类似于上一个示例的列表(如果它们可以具有不同的长度和数据类型)。

    > 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
    

答案 6 :(得分:5)

列表看起来很完美。例如,你将拥有的功能

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

主程序

x = returnlist[[1]]

y = returnlist[[2]]

答案 7 :(得分:3)

对你的第二和第三个问题是肯定的 - 这就是你需要做的事情,因为在作业的左边不能有多个'左值'。

答案 8 :(得分:2)

如何使用assign?

functionReturningTwoValues <- function(a, b) {
  assign(a, 1, pos=1)
  assign(b, 2, pos=1)
}

您可以通过引用传递要传递的变量的名称。

> functionReturningTwoValues('a', 'b')
> a
[1] 1
> b
[1] 2

如果您需要访问现有值,assign的相反值为get

答案 9 :(得分:1)

[A] 如果foo和bar中的每一个都是单个数字,则c(foo,bar)没有任何问题;你也可以命名组件:c(Foo = foo,Bar = bar)。所以你可以访问结果'res'的组件res [1],res [2];或者,在命名的情况下,res [“Foo”],res [“BAR”]。

[B] 如果foo和bar是相同类型和长度的向量,那么返回cbind(foo,bar)或rbind(foo,bar)并没有错;同样有名。在'cbind'的情况下,你可以访问foo和bar作为res [,1],res [,2]或res [,“Foo”],res [,“Bar”]。您可能还希望返回数据帧而不是矩阵:

data.frame(Foo=foo,Bar=bar)

并以res $ Foo,res $ Bar的形式访问它们。如果foo和bar具有相同的长度但不是相同的类型(例如foo是数字向量,bar是字符串的向量),这也可以很好地工作。

[C] 如果foo和bar完全不同,不能像上面那样方便地组合,那么你肯定会返回一个列表。

例如,您的函数可能适合线性模型和 也计算预测值,所以你可以

LM<-lm(....) ; foo<-summary(LM); bar<-LM$fit

然后你会return list(Foo=foo,Bar=bar)然后以res $ Foo的身份访问摘要,预测值为res $ Bar

来源:http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html

答案 10 :(得分:1)

如果要将函数的输出返回到全局环境,可以使用list2env,如下例所示:

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)

此功能将在您的全局环境中创建三个对象:

> my_obj1
  [1] 1 2 3

> my_obj2
  [1] 5 4 3

> myDF
    a b
  1 1 5
  2 2 4
  3 3 3

答案 11 :(得分:0)

要从函数获取多个输出并将其保持为所需的格式,您可以从函数中将输出保存到硬盘(在工作目录中),然后从函数外部加载它们:

myfun <- function(x) {
                      df1 <- ...
                      df2 <- ...
                      save(df1, file = "myfile1")
                      save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")

答案 12 :(得分:-1)

使用R 3.6.1,我可以执行以下操作

fr2v <- function() { c(5,3) }
a_b <- fr2v()
(a_b[[1]]) # prints "5"
(a_b[[2]]) # prints "3"