Ich habe einen Datenrahmen mit mehreren Spalten. Für jede Zeile im Datenrahmen möchte ich eine Funktion in der Zeile aufrufen, und die Eingabe der Funktion verwendet mehrere Spalten aus dieser Zeile. Nehmen wir zum Beispiel an, ich habe diese Daten und diese testFunc, die zwei Argumente akzeptiert:
> df <- data.frame(x=c(1,2), y=c(3,4), z=c(5,6))
> df
x y z
1 1 3 5
2 2 4 6
> testFunc <- function(a, b) a + b
Angenommen, ich möchte diesen TestFunc auf die Spalten x und z anwenden. Für Zeile 1 möchte ich 1 + 5 und für Zeile 2 möchte ich 2 + 6. Gibt es eine Möglichkeit, dies zu tun, ohne eine for-Schleife zu schreiben, möglicherweise mit der Apply-Funktionsfamilie?
Ich habe es versucht:
> df[,c('x','z')]
x z
1 1 5
2 2 6
> lapply(df[,c('x','z')], testFunc)
Error in a + b : 'b' is missing
Aber Fehler, irgendwelche Ideen?
BEARBEITEN: Die eigentliche Funktion, die ich aufrufen möchte, ist keine einfache Summe, sondern power.t.test. Ich habe a + b nur zum Beispiel verwendet. Das Endziel ist es, in der Lage zu sein, so etwas zu tun (in Pseudocode geschrieben):
df = data.frame(
delta=c(delta_values),
power=c(power_values),
sig.level=c(sig.level_values)
)
lapply(df, power.t.test(delta_from_each_row_of_df,
power_from_each_row_of_df,
sig.level_from_each_row_of_df
))
wobei das Ergebnis ein Vektor von Ausgaben für power.t.test für jede Zeile von df ist.
dplyr
Weg.Antworten:
Sie können
apply
eine Teilmenge der Originaldaten anwenden .oder wenn Ihre Funktion nur eine Summe ist, verwenden Sie die vektorisierte Version:
Wenn Sie verwenden möchten
testFunc
BEARBEITEN Um auf Spalten nach Namen und nicht nach Index zuzugreifen, können Sie Folgendes tun:
quelle
apply
für Big Data verwenden. Frames kopiert das gesamte Objekt (um es in eine Matrix zu konvertieren). Dies führt auch zu Problemen, wenn Sie unterschiedliche Klassenobjekte im data.frame haben.A
data.frame
ist alist
, also ...Für vektorisierte Funktionen
do.call
ist normalerweise eine gute Wahl. Aber die Namen der Argumente kommen ins Spiel. Hier wird IhrtestFunc
mit den Argumenten x und y anstelle von a und b aufgerufen. Das...
ermöglicht es irrelevant args , ohne dass es einen Fehler übergeben werden:Funktioniert für nicht vektorisierte Funktionen ,
mapply
Sie müssen jedoch die Reihenfolge der Argumente anpassen oder sie explizit benennen:Manchmal
apply
funktioniert es - als ob alle Argumente vom gleichen Typ sind, was das erzwingtdata.frame
einer Matrix keine Probleme durch Ändern des Datentyps verursacht. Ihr Beispiel war von dieser Art.Wenn Ihre Funktion innerhalb einer anderen Funktion aufgerufen werden soll, an die alle Argumente übergeben werden, gibt es eine viel einfachere Methode als diese. Studieren Sie die ersten Zeilen des Körpers,
lm()
wenn Sie diesen Weg gehen möchten.quelle
Vectorize
als Wrappermapply
Funktionen zu vektorisierenVerwenden
mapply
quelle
Neue Antwort mit
dplyr
PaketWenn die Funktion, die Sie anwenden möchten, vektorisiert ist, können Sie die
mutate
Funktion aus demdplyr
Paket verwenden:Alte Antwort mit
plyr
PaketMeiner bescheidenen Meinung nach stammt das für die Aufgabe am besten geeignete Tool
mdply
aus demplyr
Paket.Beispiel:
Wie Bertjan Broeksema betonte, schlägt dieser Ansatz leider fehl, wenn Sie nicht alle Spalten des Datenrahmens im
mdply
Aufruf verwenden. Beispielsweise,quelle
dplyr::mutate_each
. Zum Beispiel :iris %>% mutate_each(funs(half = . / 2),-Species)
.Andere haben richtig darauf hingewiesen, dass
mapply
dies zu diesem Zweck gemacht wurde, aber der Vollständigkeit halber besteht eine konzeptionell einfachere Methode darin, nur einefor
Schleife zu verwenden.quelle
Viele Funktionen sind bereits vektorisiert, sodass keine Iterationen erforderlich sind (weder
for
Schleifen noch*pply
Funktionen). IhrtestFunc
ist ein solches Beispiel. Sie können einfach anrufen:Im Allgemeinen würde ich empfehlen, zuerst solche Vektorisierungsansätze auszuprobieren und zu prüfen, ob sie Ihnen die beabsichtigten Ergebnisse liefern.
Wenn Sie alternativ mehrere Argumente an eine nicht vektorisierte Funktion übergeben müssen, ist dies
mapply
möglicherweise das, wonach Sie suchen:quelle
Hier ist ein alternativer Ansatz. Es ist intuitiver.
Ein wichtiger Aspekt, den einige der Antworten meines Erachtens nicht berücksichtigt haben, auf den ich für die Nachwelt hinweise, ist, dass Sie mit apply () problemlos Zeilenberechnungen durchführen können, jedoch nur für Matrixdaten (alle numerischen Daten)
Operationen an Spalten sind für Datenrahmen weiterhin möglich:
Um Zeilen zu bearbeiten, machen wir zuerst die Transponierung.
Der Nachteil ist, dass ich glaube, dass R eine Kopie Ihrer Datentabelle erstellen wird. Welches könnte ein Speicherproblem sein. (Dies ist wirklich traurig, da es für tdf programmatisch einfach ist, nur ein Iterator für das ursprüngliche df zu sein, wodurch Speicherplatz gespart wird, aber R keine Zeiger- oder Iteratorreferenzierung zulässt.)
Eine verwandte Frage ist auch, wie jede einzelne Zelle in einem Datenrahmen bearbeitet werden soll.
quelle
Ich kam hierher, um nach dem Namen der Tidyverse- Funktion zu suchen - von dem ich wusste, dass er existiert. Hinzufügen für (meine) zukünftige Referenz und für
tidyverse
Enthusiasten:purrrlyr:invoke_rows
(purrr:invoke_rows
in älteren Versionen).Bei Verbindung mit Standardstatistikmethoden wie in der ursprünglichen Frage würde das Besenpaket wahrscheinlich helfen.
quelle
Die Antwort von @ user20877984 ist ausgezeichnet. Da sie es weitaus besser zusammengefasst haben als meine vorherige Antwort, ist hier mein (möglicherweise immer noch mieser) Versuch, das Konzept anzuwenden:
Unter Verwendung
do.call
in einer grundlegenden Art und Weise:Arbeiten an einem vollständigen Datensatz:
lapply
diepower.t.test
Funktion für jede der Zeilen mit den angegebenen Werten:quelle
2
, warum bewirbst du dich nicht einfach über1
?data.table
hat auch eine sehr intuitive Möglichkeit, dies zu tun:Der
:=
Operator kann in Klammern aufgerufen werden, um mithilfe einer Funktion eine neue Spalte hinzuzufügenMit dieser Methode können Konstanten auch leicht als Argumente akzeptiert werden:
quelle
Wenn data.frame-Spalten unterschiedliche Typen haben,
apply()
liegt ein Problem vor. Eine Feinheit in Bezug auf die Zeileniteration ist, wieapply(a.data.frame, 1, ...)
die implizite Typkonvertierung in Zeichentypen erfolgt, wenn Spalten unterschiedliche Typen sind. z.B. ein Faktor und eine numerische Spalte. Hier ist ein Beispiel, bei dem ein Faktor in einer Spalte zum Ändern einer numerischen Spalte verwendet wird:Die Subtraktion schlägt fehl, da die Spalten in Zeichentypen konvertiert werden.
Eine Lösung besteht darin, die zweite Spalte in eine Zahl umzuwandeln:
Die Konvertierungen können jedoch vermieden werden, indem die Spalten getrennt bleiben und Folgendes verwendet wird
mapply()
:mapply()
wird benötigt, weil[[ ]]
kein Vektorargument akzeptiert wird. Die Spalteniteration könnte also vor der Subtraktion durchgeführt werden, indem ein Vektor an[]
einen etwas hässlicheren Code übergeben wird:quelle
Eine wirklich schöne Funktion hierfür ist
adply
vonplyr
, insbesondere wenn Sie das Ergebnis an den ursprünglichen Datenrahmen anhängen möchten. Diese Funktion und ihr Cousinddply
haben mir viele Kopfschmerzen und Codezeilen erspart!Alternativ können Sie die gewünschte Funktion aufrufen.
quelle