[r] 둘 이상의 값을 반환하는 함수에서 할당하는 방법은 무엇입니까?

여전히 R 논리에 들어 가려고 시도하는 중 … 여러 값을 반환하는 함수의 결과를 압축 해제하는 가장 좋은 방법은 무엇입니까?

나는 분명히 이것을 할 수 없다 :

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의 질문에 답변하도록 편집 —

실제로 결과 값 부분에 이름을 지정할 필요는 없습니다. 하나의 집계 함수를 첫 번째 구성 요소에 적용하고 다른 집계 함수를 두 번째 구성 요소에 적용 min하고 max있습니다 ( 및 . 두 구성 요소 에 대해 동일한 기능인 경우 분할 할 필요가 없습니다).



답변

(1) list […] <- 나는 이것을 10 년 전에 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


답변

나는 어떻게 든 인터넷 에서이 영리한 해킹을 우연히 발견했습니다 … 불쾌하거나 아름다운지 확실하지 않지만 여러 반환 값을 자신의 변수에 풀 수있는 “매직”연산자를 만들 수 있습니다. 이 :=함수 는 여기정의 되어 있으며 후손을 위해 아래에 포함되어 있습니다.

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

나는 그것에 대해 어떻게 느끼는지 모르겠다. 대화식 작업 공간에서 도움이 될 수 있습니다. 그것을 사용하여 (재사용 가능한) 라이브러리 (대량 소 비용)를 만드는 것이 가장 좋은 아이디어는 아니지만 그것이 당신에게 달려 있다고 생각합니다.

… 당신은 그들이 책임과 힘에 대해 말하는 것을 알고 있습니다 …


답변

일반적으로 출력을 목록으로 래핑합니다. 매우 유연합니다 (숫자, 문자열, 벡터, 행렬, 배열, 목록, 출력 된 객체의 조합을 가질 수 있음)

그처럼:

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


답변

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

나는 이것이 효과가 있다고 생각한다.


답변

이 문제를 해결하기 위해 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, ..)

자세한 내용과 예 는 패키지 비 네트 를 확인하십시오 .


답변

이 질문에 대한 정답은 없습니다. 나는 정말로 당신이 데이터로 무엇을하고 있는지에 달려 있습니다. 위의 간단한 예에서는 다음과 같이 강력하게 제안합니다.

  1. 가능한 한 간단하게 유지하십시오.
  2. 가능하면 함수를 벡터화하는 것이 가장 좋습니다. 이는 장기적으로 최대의 유연성과 속도를 제공합니다.

위의 값 1과 2에 이름이 있어야합니까? 다시 말해,이 예제에서 1과 2가 r [1]과 r [2]가 아닌 a와 b로 명명되는 것이 중요한 이유는 무엇입니까? 이 문맥에서 이해해야 할 한 가지 중요한 점은 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


답변

목록은이 목적에 완벽 해 보입니다. 예를 들어 함수 내에서

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