Ich versuche eine Funktion zu schreiben, um ein data.frame ( x
) und ein column
davon zu akzeptieren . Die Funktion führt einige Berechnungen für x durch und gibt später einen anderen data.frame zurück. Ich bleibe bei der Best-Practice-Methode, um den Spaltennamen an die Funktion zu übergeben.
Die beide minimal Beispiele fun1
und fun2
unter dem gewünschten Ergebnis zu führen in der Lage , Operationen an x$column
, indem max()
als Beispiel. Beide verlassen sich jedoch auf das scheinbar (zumindest für mich) Unelegante
- Anruf bei
substitute()
und möglicherweiseeval()
- die Notwendigkeit, den Spaltennamen als Zeichenvektor zu übergeben.
fun1 <- function(x, column){
do.call("max", list(substitute(x[a], list(a = column))))
}
fun2 <- function(x, column){
max(eval((substitute(x[a], list(a = column)))))
}
df <- data.frame(B = rnorm(10))
fun1(df, "B")
fun2(df, "B")
Ich möchte die Funktion beispielsweise als aufrufen können fun(df, B)
. Andere Optionen, die ich in Betracht gezogen, aber nicht ausprobiert habe:
- Übergeben Sie
column
als Ganzzahl der Spaltennummer. Ich denke das würde vermeidensubstitute()
. Idealerweise könnte die Funktion beides akzeptieren. with(x, get(column))
, aber selbst wenn es funktioniert, denke ich, würde dies immer noch erfordernsubstitute
- Nutzen Sie
formula()
undmatch.call()
, mit denen ich keine Erfahrung habe.
Unterfrage : Wird do.call()
bevorzugt eval()
?
B
wird davon ausgegangen, dass B selbst ein Objekt ist.[[
Lösung die einzige war, die für mich funktionierte.Diese Antwort wird viele der gleichen Elemente wie vorhandene Antworten abdecken, aber dieses Problem (Übergabe von Spaltennamen an Funktionen) tritt häufig genug auf, dass ich wollte, dass es eine Antwort gibt, die die Dinge etwas umfassender behandelt.
Angenommen, wir haben einen sehr einfachen Datenrahmen:
und wir möchten eine Funktion schreiben, die eine neue Spalte erstellt
z
, die die Summe der Spaltenx
und isty
.Ein sehr häufiger Stolperstein ist, dass ein natürlicher (aber falscher) Versuch oft so aussieht:
Das Problem hierbei ist, dass
df$col1
der Ausdruck nicht ausgewertet wirdcol1
. Es wird einfach nach einer Spaltedf
gesucht, die buchstäblich aufgerufen wirdcol1
. Dieses Verhalten wird im?Extract
Abschnitt "Rekursive (listähnliche) Objekte" beschrieben.Die einfachste und am häufigsten empfohlene Lösung besteht darin, einfach von
$
zu zu wechseln[[
und die Funktionsargumente als Zeichenfolgen zu übergeben:Dies wird oft als "Best Practice" bezeichnet, da es die Methode ist, die am schwierigsten zu vermasseln ist. Das Übergeben der Spaltennamen als Zeichenfolgen ist so eindeutig wie möglich.
Die folgenden zwei Optionen sind weiter fortgeschritten. Viele beliebte Pakete verwenden diese Art von Techniken, aber ihre gute Verwendung erfordert mehr Sorgfalt und Geschick, da sie subtile Komplexitäten und unerwartete Fehlerquellen mit sich bringen können. Dieser Abschnitt von Hadleys Advanced R-Buch ist eine hervorragende Referenz für einige dieser Probleme.
Wenn Sie den Benutzer wirklich vor der Eingabe all dieser Anführungszeichen bewahren möchten, besteht eine Option möglicherweise darin, nackte, nicht in Anführungszeichen gesetzte Spaltennamen in Zeichenfolgen zu konvertieren, indem Sie Folgendes verwenden
deparse(substitute())
:Das ist, ehrlich gesagt, wahrscheinlich ein bisschen albern, da wir wirklich das Gleiche tun wie in
new_column1
, nur mit ein paar zusätzlichen Arbeiten, um nackte Namen in Zeichenfolgen umzuwandeln.Wenn wir wirklich ausgefallen sein möchten , können wir uns entscheiden, dass wir flexibler sein und andere Kombinationen von zwei Variablen zulassen möchten , anstatt die Namen von zwei hinzuzufügenden Spalten zu übergeben. In diesem Fall würden wir wahrscheinlich
eval()
auf einen Ausdruck zurückgreifen, der die beiden Spalten umfasst:Nur zum Spaß verwende ich immer noch
deparse(substitute())
den Namen der neuen Spalte. Hier funktioniert alles:Die kurze Antwort lautet also im Grunde: Übergeben Sie die Spaltennamen von data.frame als Zeichenfolgen und
[[
wählen Sie einzelne Spalten aus. Fangen Sie erst aneval
, sichsubstitute
mit usw. zu beschäftigen, wenn Sie wirklich wissen, was Sie tun.quelle
Persönlich finde ich es ziemlich hässlich, die Spalte als String zu übergeben. Ich mache gerne so etwas wie:
was ergeben wird:
Beachten Sie, dass die Angabe eines data.frame optional ist. Sie können sogar mit Funktionen Ihrer Spalten arbeiten:
quelle
Ein anderer Weg ist die Verwendung des
tidy evaluation
Ansatzes. Es ist ziemlich einfach, Spalten eines Datenrahmens entweder als Zeichenfolgen oder als bloße Spaltennamen zu übergeben. Weitere Informationen finden Sietidyeval
hier .Verwenden Sie Spaltennamen als Zeichenfolgen
Verwenden Sie bloße Spaltennamen
Erstellt am 01.03.2019 durch das reprex-Paket (v0.2.1.9000)
quelle
Wenn ein zusätzlicher Gedanke erforderlich ist, um den nicht zitierten Spaltennamen an die benutzerdefinierte Funktion zu übergeben, kann dies möglicherweise
match.call()
auch in diesem Fall als Alternative zu Folgendem nützlich seindeparse(substitute())
:Wenn der Spaltenname einen Tippfehler enthält, ist es sicherer, mit einem Fehler aufzuhören:
Erstellt am 2019-01-11 vom reprex-Paket (v0.2.1)
Ich glaube nicht, dass ich diesen Ansatz verwenden würde, da es mehr Typisierung und Komplexität gibt, als nur den zitierten Spaltennamen zu übergeben, wie in den obigen Antworten angegeben, aber gut, ist ein Ansatz.
quelle