wake-up-neo.com

Wie kann ich eine Funktion zuweisen, die mehr als einen Wert zurückgibt?

Versuchen Sie immer noch, sich mit der R-Logik zu befassen ... Was ist der "beste" Weg, um die Ergebnisse einer Funktion, die mehrere Werte zurückgibt, (auf LHS) zu entpacken?

Ich kann das anscheinend nicht tun:

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

muss ich wirklich folgendes tun?

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

oder würde der R-Programmierer so etwas schreiben:

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

--- bearbeitet um Shane's Fragen zu beantworten ---

Ich muss den Ergebniswertteilen keine Namen geben. Ich wende eine Aggregatfunktion auf die erste Komponente und eine andere auf die zweite Komponente an (min und max. Wenn es die gleiche Funktion für beide Komponenten wäre, müsste ich sie nicht aufteilen).

199
mariotomo

(1) list [...] <- Ich hatte dies vor über einem Jahrzehnt auf r-help gepostet. Seitdem wurde es dem gsubfn-Paket hinzugefügt. Es ist kein spezieller Operator erforderlich, jedoch muss die linke Seite mit list[...] Wie folgt geschrieben werden:

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

Wenn Sie nur die erste oder zweite Komponente benötigen, funktionieren diese auch:

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

(Wenn Sie nur einen Wert benötigen, ist natürlich functionReturningTwoValues()[[1]] oder functionReturningTwoValues()[[2]] ausreichend.)

Weitere Beispiele finden Sie im angegebenen r-help-Thread.

(2) mit Wenn nur die Mehrfachwerte nacheinander kombiniert und die Rückgabewerte benannt werden sollen, ist es eine einfache Alternative, with:

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

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

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

(3) anhängen Eine andere Alternative ist anhängen:

attach(myfun())
a + b

HINZUGEFÜGT: with und attach

170
G. Grothendieck

Ich bin irgendwie auf diesen cleveren Hack im Internet gestoßen ... Ich bin mir nicht sicher, ob er böse oder schön ist, aber damit können Sie einen "magischen" Operator erstellen, mit dem Sie mehrere Rückgabewerte in ihre eigene Variable entpacken können. Das := function wird hier definiert und unten für die Nachwelt eingeschlossen:

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

Damit können Sie tun, wonach Sie suchen:

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

Ich weiß nicht, wie ich mich dabei fühle. Vielleicht finden Sie es in Ihrem interaktiven Arbeitsbereich hilfreich. Es ist vielleicht nicht die beste Idee, damit (wiederverwendbare) Bibliotheken (für den Massenkonsum) zu erstellen, aber ich denke, das liegt an Ihnen.

... Sie wissen, was sie über Verantwortung und Macht sagen ...

68
Steve Lianoglou

Normalerweise verpacke ich die Ausgabe in eine Liste, die sehr flexibel ist (Sie können eine beliebige Kombination von Zahlen, Zeichenfolgen, Vektoren, Matrizen, Arrays, Listen, Objekten in der Ausgabe haben).

so wie:

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
43
Federico Giorgi
functionReturningTwoValues <- function() { 
  results <- list()
  results$first <- 1
  results$second <-2
  return(results) 
}
a <- functionReturningTwoValues()

Ich denke das funktioniert.

14
Jojo

Ich habe ein R-Paket zusammengestellt zeallot um dieses Problem anzugehen. zeallot enthält einen Mehrfachzuweisungs- oder Entpackungszuweisungsoperator, %<-%. Die LHS des Operators ist eine beliebige Anzahl zuzuweisender Variablen, die mit Aufrufen von c() erstellt wurden. Die RHS des Operators ist ein Vektor, eine Liste, ein Datenrahmen, ein Datumsobjekt oder ein benutzerdefiniertes Objekt mit einer implementierten destructure -Methode (siehe ?zeallot::destructure).

Hier sind einige Beispiele, die auf dem Originalbeitrag basieren:

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

Weitere Informationen und Beispiele finden Sie in der Packung Vignette .

11
nteetor

Es gibt keine richtige Antwort auf diese Frage. Ich komme wirklich darauf an, was Sie mit den Daten machen. In dem einfachen obigen Beispiel würde ich stark empfehlen:

  1. Halte die Dinge so einfach wie möglich.
  2. Wann immer möglich, ist es eine bewährte Methode, Ihre Funktionen vektorisiert zu halten. Das bietet auf lange Sicht die größte Flexibilität und Geschwindigkeit.

Ist es wichtig, dass die obigen Werte 1 und 2 Namen haben? Mit anderen Worten, warum ist es in diesem Beispiel wichtig, dass 1 und 2 a und b heißen und nicht nur r [1] und r [2]? In diesem Zusammenhang ist es wichtig zu verstehen, dass a und b auch beide Vektoren der Länge 1 sind. Sie ändern also nichts, außer zwei neue Vektoren die keine Indizes benötigen, auf die verwiesen werden soll:

> 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

Sie können die Namen auch dem ursprünglichen Vektor zuweisen, wenn Sie lieber den Buchstaben als den Index referenzieren möchten:

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

[Edit] Da Sie Min und Max für jeden Vektor separat anwenden, würde ich vorschlagen, entweder eine Matrix (wenn a und b dieselbe Länge und denselben Datentyp haben) oder einen Datenrahmen zu verwenden (Wenn a und b gleich lang sind, aber unterschiedliche Datentypen haben können) oder verwenden Sie eine Liste wie in Ihrem letzten Beispiel (wenn sie unterschiedliche Längen und Datentypen haben können).

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

Listen scheinen für diesen Zweck perfekt zu sein. Zum Beispiel innerhalb der Funktion, die Sie hätten

x = desired_return_value_1 # (vector, matrix, etc)

y = desired_return_value_2 # (vector, matrix, etc)

returnlist = list(x,y...)

}  # end of function

hauptprogramm

x = returnlist[[1]]

y = returnlist[[2]]
5
Arnold

Ja zu Ihrer zweiten und dritten Frage - das ist es, was Sie tun müssen, da Sie nicht mehrere 'I-Werte' links von einer Aufgabe haben können.

4

Wie wäre es mit zuweisen?

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

Sie können die Namen der zu übergebenden Variablen als Referenz übergeben.

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

Wenn Sie auf die vorhandenen Werte zugreifen müssen, lautet die Umkehrung von assignget.

2
Steve Pitchers

[A] Wenn jedes von foo und bar eine einzelne Zahl ist, dann ist mit c nichts falsch (foo, bar); und Sie können die Komponenten auch benennen: c (Foo = foo, Bar = bar). Sie können also auf die Komponenten des Ergebnisses 'res' als res [1], res [2] zugreifen. oder im genannten Fall als res ["Foo"], res ["BAR"].

[B] Wenn foo und bar Vektoren des gleichen Typs und der gleichen Länge sind, ist es wiederum nichts Falsches, cbind (foo, bar) oder rbind (foo, bar) zurückzugeben. ebenfalls benennbar. Im Fall 'cbind' würden Sie auf foo und bar als res [ 1], res [ 2] oder als res [ "Foo"], res [ "Bar"] zugreifen. Möglicherweise möchten Sie auch lieber einen Datenrahmen als eine Matrix zurückgeben:

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

und greife auf sie zu als res $ Foo, res $ Bar. Dies würde auch gut funktionieren, wenn foo und bar von gleicher Länge, aber nicht vom gleichen Typ wären (z. B. foo ist ein Vektor von Zahlen, bar ein Vektor von Zeichenketten).

[C] Wenn foo und bar so unterschiedlich sind, dass sie nicht wie oben beschrieben kombiniert werden können, sollten Sie auf jeden Fall eine Liste zurückgeben.

Beispielsweise könnte Ihre Funktion in ein lineares Modell passen und auch vorhergesagte Werte berechnen, was Sie haben könnten

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

und dann würden Sie return list(Foo=foo,Bar=bar) und dann auf die Zusammenfassung als res $ Foo zugreifen, die vorhergesagten Werte als res $ Bar

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

1
Prakhar Agrawal

Wenn Sie die Ausgabe Ihrer Funktion an die globale Umgebung zurückgeben möchten, können Sie list2env, wie in diesem Beispiel:

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)

Diese Funktion erstellt drei Objekte in Ihrer globalen Umgebung:

> 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
rafa.pereira

Um mehrere Ausgaben einer Funktion zu erhalten und im gewünschten Format zu halten, können Sie die Ausgaben von innerhalb der Funktion auf Ihrer Festplatte (im Arbeitsverzeichnis) speichern und dann von außerhalb der Funktion laden:

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