如何从一个返回多个值的函数赋值?

还在试着进入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

——被编辑来回答谢恩的问题——

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

304593 次浏览

是的,你的第二个和第三个问题-这是你需要做的,因为你不能在赋值的左边有多个“左值”。

这个问题没有正确答案。这取决于你如何处理数据。在上面的简单例子中,我强烈建议:

  1. 让事情尽可能简单。
  2. 在任何可能的情况下,保持函数向量化是一个最佳实践。从长远来看,这提供了最大程度的灵活性和速度。

上面的值1和2有名字很重要吗?换句话说,为什么在这个例子中,1和2被命名为a和b很重要,而不是r[1]和r[2]?在这个上下文中需要理解的一件重要的事情是,a和b是,都是长度为1的向量。所以在赋值的过程中你并没有改变任何东西,除了有两个不需要下标引用的新向量:

> 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

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

就像:

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

我在网上偶然发现了这个聪明的黑客…我不确定它是令人讨厌的还是漂亮的,但它允许您创建一个“神奇的”操作符,允许您将多个返回值解压缩到它们自己的变量中。:=函数定义在这里,并包含在下面供后人使用:

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

我不知道我是怎么想的。也许你会发现它对你的交互式工作空间很有帮助。使用它来构建(可重用的)可用的库(用于大众消费)可能不是最好的主意,但我想这取决于您。

... 你知道人们是怎么说责任和权力的吗?

列表似乎非常适合这个目的。例如,在函数中

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

十多年前,我在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:

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


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


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

另一个替代方法是attach:

attach(myfun())
a + b

添加:withattach

那么使用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

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

我认为这是可行的。

< p >(一个) 如果foo和bar都是一个数字,那么c(foo,bar)没有问题;你也可以命名这些组件:c(Foo= Foo,Bar= Bar)。所以你可以访问结果res的组件res[1] res[2];或在命名的情况下,如res["Foo"], res["BAR"]。< / p > < p > [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是一个字符串向量),这也可以很好地工作。

< p > [C] 如果foo和bar完全不同,不能像上面那样方便地组合,那么你肯定应该返回一个列表。< / p > 例如,您的函数可能适合线性模型和 也计算预测值,所以你可以有

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

如果你想将函数的输出返回给全局环境,你可以使用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

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

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

我把一个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, ..)

查看包装饰图案以获得更多信息和示例。

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

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

我将发布一个函数,以向量的方式返回多个对象:

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

这是我用来计算中位数的函数。我知道R中有一个名为median()的内置函数,但尽管如此,我编程它来构建其他函数,通过使用我刚刚编程的Median()函数来计算数值数据集的四分位数。Median()函数是这样工作的:

  1. 如果数值向量X有偶数个元素(即length(X)%%2==0),则中位数是通过对元素sort(X)[length(X)/2]sort(X)[(length(X)/2+1)]求平均值来计算的。
  2. 如果__abc0没有偶数个元素,则中位数为sort(X)[(length(X)+1)/2]

对于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)
}

该函数通过以下三种方法返回数值向量的四分位数:

  1. 当数值向量__abc0的元素数为奇数时,丢弃中位数用于计算四分位数。
  2. 当数值向量__abc0的元素数为奇数时,保留四分位数计算的中位数。
  3. 对方法1和方法2得到的结果求平均值。

当数值向量X中的元素数为偶数时,这三个方法重合。

QuartilesFunction()的结果是一个向量,它描述了使用上述三种方法计算出的第一和第三四分位数。

2021年,我经常用这个词。

tidyverse包有一个名为lst的函数,它在创建列表时为列表元素赋name。 Post,我使用list2env()来分配变量或直接使用列表

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

这只是为了完整,而不是因为我个人喜欢它。你可以用管道%>%输出结果,用花括号{}求值,并使用双箭头<<-将变量写入父环境。

library(tidyverse)
functionReturningTwoValues() %>% {a <<- .[1]; b <<- .[2]}

< >强更新: 你也可以使用zeallot包中的多重赋值操作符::%<-%

c(a, b) %<-% list(0, 1)