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


223

여전히 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있습니다 ( 및 . 두 구성 요소 에 대해 동일한 기능인 경우 분할 할 필요가 없습니다).


7
참고로 여러 값을 반환하는 또 다른 방법은 반환 값을 설정하는 것 attr입니다.
Jonathan Chang

이것은 파이썬의 튜플 풀림과 같습니다.
smci

답변:


186

(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


25
"with"때문에 답변을 수락했지만 "list"의 왼쪽 사용법에 대해 설명한 내용을 재현 할 수 없습니다. "object '
a'not

4
그것은 나를 위해 작동합니다. 무엇을 시도 했습니까? 링크 된 게시물을 읽고 팔로우 했습니까? 당신은 정의나요 list[<-.result이 같이?
G. Grothendieck

12
@ G.Grothendieck, 링크의 내용을 답변에 넣을 수 있습니까? 사람들이 더 쉽게 사용할 수 있다고 생각합니다.
merlin2011

12
@ merlin2011에 동의합니다. 작성된 것처럼이 구문은 R베이스에 포함 된 것처럼 보입니다.
knowah

6
@ G.Grothendieck 나는 merlin2011과 knowah에 동의합니다-여기에 중요한 실제 코드 (링크에서 참조 된 코드)가 답에 있으면 가장 좋습니다. 결과 개체의 이름을 list로 지정할 필요가 없다고 언급하는 것은 좋지 않습니다. 실제 코드를 읽기 전에 잠시 동안 혼란 스러웠습니다. 언급 한 바와 같이 링크에서 코드를 실행해야하지만 대부분의 사람들은 해당 코드가 직접 응답하지 않는 한 해당 코드를 즉시 읽을 수는 없습니다. 이는이 구문이 기본 R에 있다는 인상을줍니다.
Dason

68

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

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

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

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


12
또한 나는 data.table 패키지가 :=연산자 mucho를 훨씬 더
손쉬운

47

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

그처럼:

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)으로 시도했지만 작동하지 않습니다.
skan

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

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


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

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


이 방법을 사용하여 여러 플롯을 출력으로 저장하는 특수 구문이 있습니까?
mrpargeter

2
특별한 구문이 필요하지 않으므로 숫자 목록과 마찬가지로 플롯 객체 목록을 지정할 수 있습니다.
nteetor

10

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

  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

귀하의 의견을 포함시키기 위해 질문을 편집했습니다. 감사. 같은 것들에 이름을 부여 r[1]하면 더 명확하게하는 데 도움이 될 수 있습니다 (같은 이름 a이 제자리에 오더라도 괜찮 습니다).
mariotomo 2009

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

4
list ( "x", "y") <-returnlist ()와 같이 단일 쉼표로 두 변수를 어떻게 할당 할 수 있습니까? 목록에 많은 요소가 있으면 전체 기능을 여러 번 실행해야하며 시간이 걸리기 때문입니다.
skan

4

두 번째와 세 번째 질문에 그렇습니다. 과제의 왼쪽에 여러 개의 'lvalue'를 가질 수 없기 때문에해야 할 일입니다.


3

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.


... 그러나이를 위해서는 해당 환경에서 수신 변수의 이름을 알아야합니다.
smci

@smci 예. : 질문에 "라는 이름의 목록"방법은 일반적으로 더 나은 이유입니다 r <- function() { return(list(first=1, second=2)) }및 사용 결과를 참조 r$first하고 r$second.
Steve Pitchers

2
함수가 있으면 list ( "x", "y") <-functionReturningTwoValues ​​( 'a', 'b')와 같이 단일 쉼표로 두 변수를 어떻게 할당 할 수 있습니까? 리스트에 많은 요소가 있다면 전체 기능을 여러 번 실행해야하고 시간이
걸리기

3

함수의 출력을 전역 환경으로 되돌리려면 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)

이 기능은 글로벌 환경에서 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

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'의 경우 res [, 1], res [, 2] 또는 res [, "Foo"], res [, "Bar"]로 foo 및 bar에 액세스합니다. 행렬 대신 데이터 프레임을 반환하는 것이 좋습니다.

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

res $ Foo, res $ Bar로 액세스하십시오. foo와 bar의 길이는 같지만 같은 유형이 아닌 경우에도 잘 작동합니다 (예 : foo는 숫자로 구성된 벡터, 문자열로 구성된 벡터).

[C] foo와 bar가 위와 같이 편리하게 결합되지 않을 정도로 충분히 다른 경우, 반드시 목록을 반환해야합니다.

예를 들어, 함수가 선형 모델에 적합하고 예측 된 값을 계산할 수 있으므로

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

그런 다음 return list(Foo=foo,Bar=bar)res $ Bar로 예측 된 값인 res $ Foo로 요약에 액세스합니다.

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


-1

함수에서 여러 출력을 가져와 원하는 형식으로 유지하려면 함수 내에서 출력을 작업 디렉토리의 하드 디스크에 저장 한 다음 함수 외부에서로드 할 수 있습니다.

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

-1

R 3.6.1을 사용하면 다음을 수행 할 수 있습니다

fr2v <- function() { c(5,3) }
a_b <- fr2v()
(a_b[[1]]) # prints "5"
(a_b[[2]]) # prints "3"
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.