ChatGPT解决这个技术问题 Extra ChatGPT

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

仍在尝试进入 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。如果两个组件的函数相同,我不需要拆分它们)。

仅供参考,返回多个值的另一种方法是在返回值上设置 attr
这相当于 Python 的元组解包。

G
G. Grothendieck

(1) list[...]<- 十多年前,我在 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 另一种选择是 attach:

attach(myfun())
a + b

添加:withattach


由于“与”,我接受了您的回答,但我无法重现您对“列表”左侧用法的描述,我得到的只是“找不到对象'a'”
这个对我有用。你尝试了什么?您是否阅读了链接的帖子并关注了它?您是否按照此处所示定义了 list[<-.result
@G.Grothendieck,您介意我将您的链接内容放入您的答案中吗?我认为这将使人们更容易使用它。
我同意@merlin2011;正如所写的那样,这种语法似乎嵌入到了 R 基础中。
@G.Grothendieck 我同意 merlin2011 和 knowah - 如果这里重要的实际代码(链接中引用的代码)在答案中,那将是最好的。提到结果对象不需要命名为列表可能不是一个坏主意。在阅读您的实际代码之前,这让我困惑了一会儿。如前所述,答案说您需要运行链接中的代码,但大多数人不会立即阅读该代码,除非它直接在答案中 - 这给人的印象是这种语法在基础 R 中。
S
Steve Lianoglou

我不知何故在互联网上偶然发现了这个聪明的黑客......我不确定它是讨厌还是漂亮,但它可以让您创建一个“神奇”运算符,允许您将多个返回值解压缩到它们自己的变量中。 := 函数 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

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

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


此外,我现在比最初发布此答案时更不鼓励它,因为 data.table 包以更方便的方式使用 := 运算符 mucho :-)
F
Federico Giorgi

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

就像:

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

如果不是 output<-func2(5) 我想将结果放在两个对象中怎么办?我试过 list("a","b") <-func2(5) 但它不起作用。
n
nteetor

我整理了一个 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 以获取更多信息和示例。


是否有一种特殊的语法可以使用这种方法将多个图存储为输出?
不需要特殊语法,您可以像分配数字列表一样分配绘图对象列表。
J
Jojo
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

我认为这行得通。


S
Shane

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

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

上面的值 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

编辑问题以包含您的评论。谢谢。给像 r[1] 这样的东西起名字有助于使事情更清楚(好吧,如果像 a 这样的名字出现在他们的位置上,那就不行了)。
J
Jos Vinke

列表似乎非常适合此目的。例如,在您将拥有的功能中

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

如何在单个命令中分配两个变量,例如 list("x","y") <-returnlist() ?我这么说是因为如果列表中有很多元素,则需要多次运行整个函数,而这会花费时间。
r
rafa.pereira

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

D
Dirk Eddelbuettel

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


S
Steve Pitchers

使用分配怎么样?

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


...但这需要您知道该环境中接收变量的名称
@smci 是的。这就是为什么问题中的“命名列表”方法通常更好:r <- function() { return(list(first=1, second=2)) } 并使用 r$firstr$second 引用结果。
一旦有了函数,如何在一个命令中分配两个变量,例如 list("x","y") <- functionReturningTwoValues('a', 'b') ?我这么说是因为如果列表中有很多元素,则需要多次运行整个函数,这会花费时间
P
Prakhar Agrawal

[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


D
Diego

我将发布一个通过向量返回多个对象的函数:

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() 函数的工作方式如下:

如果数值向量 X 有偶数个元素(即 length(X)%%2==0),则通过对元素 sort(X)[length(X)/2] 和 sort(X )[(长度(X)/2+1)]。如果 X 没有偶数个元素,则中位数为 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)
}

此函数使用三种方法返回数值向量的四分位数:

当数值向量 Xi 的元素个数为奇数时,舍弃中位数进行四分位数的计算。当数值向量 X 的元素个数为奇数时,保持中位数用于计算四分位数。平均使用方法 1 和 2 获得的结果。

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

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


Z
ZygD

2021 年,这是我经常使用的东西。

tidyverse 包有一个名为 lst 的函数,它在创建列表时为列表元素分配名称。我使用 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

C
Comfort Eagle

这只是为了完整性而不是因为我个人更喜欢它。您可以使用管道 %>% 结果,使用花括号 {} 对其进行评估,并使用双箭头 <<- 将变量写入父环境。

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

更新:您还可以使用 zeallot 包中的多重赋值运算符::%<-%

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

r
radumanolescu

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

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

A
Andrew Eaves

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

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

关注公众号,不定期副业成功案例分享
关注公众号

不定期副业成功案例分享

领先一步获取最新的外包任务吗?

立即订阅