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).
(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
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 ...
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
functionReturningTwoValues <- function() {
results <- list()
results$first <- 1
results$second <-2
return(results)
}
a <- functionReturningTwoValues()
Ich denke das funktioniert.
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 .
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:
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
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
x = returnlist[[1]]
y = returnlist[[2]]
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.
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 assign
get
.
[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
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
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")