Jak przypisać z funkcji, która zwraca więcej niż jedną wartość?

Wciąż próbuję wejść w logikę R... jaki jest "najlepszy" sposób rozpakowania (na LHS) wyników funkcji zwracającej wiele wartości?

Nie mogę tego zrobić najwyraźniej:

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

Czy naprawdę muszę wykonać następujące czynności?

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

A może programista R napisze coś bardziej takiego:

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

- - - edytowane w odpowiedzi na pytania Shane ' a- - -

Naprawdę nie trzeba nadawać nazw części wartości wyniku. Stosuję jedną funkcję zbiorczą do pierwszej składnik i drugi do drugiego składnika (min i max. gdyby to była ta sama funkcja dla obu komponentów Nie potrzebowałbym ich dzielenia).

Author: smci, 2009-12-01

12 answers

(1) Lista [...] zamieściłem to ponad dekadę temu na r-help. Od tego czasu został dodany do pakietu gsubfn. Nie wymaga specjalnego operatora, ale wymaga, aby lewa strona była zapisywana za pomocą list[...] w następujący sposób:

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

Jeśli potrzebujesz tylko pierwszego lub drugiego komponentu, wszystkie one również działają:

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

(oczywiście, jeśli potrzebujesz tylko jednej wartości, to functionReturningTwoValues()[[1]] lub functionReturningTwoValues()[[2]] byłyby wystarczające.)

Zobacz cytowany wątek r-help dla więcej przykładów.

(2) z jeśli intencją jest jedynie późniejsze połączenie wielu wartości, a wartości zwracane są nazwane, to prostą alternatywą jest użycie with:

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

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

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

(3) załącz inną alternatywą jest załącz:

attach(myfun())
a + b

Dodano: with i attach

 156
Author: G. Grothendieck,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-10 03:04:35

W jakiś sposób natknąłem się na tego sprytnego hakera w Internecie ... Nie jestem pewien, czy jest paskudny czy piękny, ale pozwala stworzyć" magiczny " operator, który pozwala rozpakować wiele wartości zwrotnych do ich własnej zmiennej. Funkcja := jest zdefiniowana tutaj i dołączona poniżej dla potomności:

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

Mając to w ręku, możesz zrobić to, czego szukasz:

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
Nie wiem, co o tym myślę. Być może okaże się to pomocne w Twojej interaktywnej przestrzeni roboczej. Za pomocą budowanie (ponowne)użytecznych bibliotek (do masowej konsumpcji) może nie być najlepszym pomysłem, ale myślę, że to zależy od Ciebie.

... wiesz, co mówią o odpowiedzialności i władzy ...

 64
Author: Steve Lianoglou,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-16 21:05:35

Zwykle zawijam wyjście w listę, która jest bardzo elastyczna (możesz mieć dowolną kombinację liczb, łańcuchów, wektorów, macierzy, tablic, list, obiektów int he output)

Tak jak:

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
 39
Author: Federico Giorgi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-01 15:01:58
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()
Myślę, że to działa.
 11
Author: Jojo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-23 14:46:36

Nie ma prawidłowej odpowiedzi na to pytanie. Zależy mi na tym, co robisz z danymi. W prostym przykładzie powyżej zdecydowanie sugerowałbym:

  1. trzymaj rzeczy tak proste, jak to tylko możliwe.
  2. tam, gdzie to możliwe, to najlepsza praktyka, aby utrzymać swoje funkcje wektoryzowane. Zapewnia to największą elastyczność i szybkość w dłuższej perspektywie.

Czy ważne jest, aby wartości 1 i 2 powyżej miały nazwy? Innymi słowy, dlaczego jest to ważne w tym przykład, że 1 i 2 mają nazwy a i b, a nie tylko r[1] i r[2]? Ważną rzeczą do zrozumienia w tym kontekście jest to, że a i b są również oba wektory o długości 1. Więc tak naprawdę nie zmieniasz niczego w procesie tworzenia tego przypisania, poza tym, że masz 2 nowe wektory, które nie wymagają referencji indeksów dolnych: {]}

> 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

Możesz również przypisać nazwy do oryginalnego wektora, jeśli wolisz odwoływać się do litery niż do indeks:

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

[Edit] biorąc pod uwagę, że będziesz stosować min i max do każdego wektora osobno, sugerowałbym albo użycie macierzy (Jeśli a i b będą tej samej długości i tego samego typu danych) lub ramki danych (Jeśli a i b będą tej samej długości, ale mogą być różnymi typami danych) albo użycie listy, jak w poprzednim przykładzie (jeśli mogą być różnej długości i typów danych).

> 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
 10
Author: Shane,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-01 15:03:46

Przygotowałem Pakiet R zeallot , aby rozwiązać ten problem. zeallot zawiera operator przypisania wielokrotnego lub rozpakowującego, %<-%. LHS operatora to dowolna liczba zmiennych do przypisania, zbudowana za pomocą wywołań do c(). RHS operatora to wektor, lista, ramka danych, obiekt daty lub dowolny obiekt niestandardowy z zaimplementowaną metodą destructure (Zobacz ?zeallot::destructure).

Oto garść przykładów opartych na oryginalnym poście,

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

Zobacz pakiet winieta aby uzyskać więcej informacji i przykładów.

 6
Author: nteetor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-29 16:06:26

Listy wydają się idealne do tego celu. Na przykład w ramach funkcji masz

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

Program główny

x = returnlist[[1]]

y = returnlist[[2]]
 5
Author: Arnold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-28 16:05:44

Tak dla drugiego i trzeciego pytania-to jest to, co musisz zrobić, ponieważ nie możesz mieć wielu "lvalues" po lewej stronie zadania.

 3
Author: Dirk Eddelbuettel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-01 14:41:39

Jak o użyciu przypisać?

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

Możesz przekazać nazwy zmiennej, którą chcesz przekazać przez odniesienie.

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

Jeśli chcesz uzyskać dostęp do istniejących wartości, konwersją assign jest get.

 2
Author: Steve Pitchers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-04 18:17:19

[A] Jeśli każdy z foo i bar jest pojedynczą liczbą, to nie ma nic złego w c (foo,bar); i możesz również nazwać komponenty: c (Foo=foo,Bar=bar). Można więc uzyskać dostęp do składników wyniku ' res ' jako res[1], res[2]; lub, w nazwanym przypadku, jako res ["Foo"], RES ["BAR"].

[B] Jeśli foo i bar są wektorami tego samego typu i długości, to nie ma nic złego w zwracaniu cbind (foo,bar) lub rbind (foo,bar); podobnie nameable. W przypadku "cbind" można uzyskać dostęp do foo i bar jako res[,1], res[,2] lub jako res[,"Foo"], res [, "Bar"]. Możesz również preferować zwracanie ramki danych zamiast matrycy:

data.frame(Foo=foo,Bar=bar)
I dostęp do nich jako res $ Foo, Res$Bar. Byłoby to również dobre, gdyby foo i bar były tej samej długości, ale nie tego samego typu (np. foo jest wektorem liczb, bar wektorem ciągów znaków).

[C] Jeśli foo i bar są wystarczająco różne, aby nie łączyć się wygodnie jak wyżej, to na pewno zwrócisz listę.

Dla przykład, twoja funkcja może pasować do modelu liniowego i również obliczyć przewidywane wartości, więc można mieć

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

I wtedy będziesz return list(Foo=foo,Bar=bar) i uzyskasz dostęp do podsumowania jako res$Foo, przewidywane wartości jako Res$Bar

Źródło: http://r.789695.n4.nabble.com/How-to-return-multiple-values-in-a-function-td858528.html
 1
Author: Prakhar Agrawal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-08-14 17:49:35

Jeśli chcesz zwrócić wyjście swojej funkcji do środowiska globalnego, możesz użyć list2env, jak w tym przykładzie:

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)

Ta funkcja utworzy trzy obiekty w Twoim globalnym środowisku:

> 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
Author: rafa.pereira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-30 17:18:01

Aby uzyskać wiele wyjść z funkcji i zachować je w pożądanym formacie, można zapisać wyjścia na dysku twardym (w katalogu roboczym) z poziomu funkcji, a następnie załadować je spoza funkcji:

myfun <- function(x) {
                      df1 <- ...
                      df2 <- ...
                      save(df1, file = "myfile1")
                      save(df2, file = "myfile2")
}
load("myfile1")
load("myfile2")
 0
Author: Andrew Eaves,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-17 09:32:41