Ich habe mich in StackOverflow umgesehen, kann jedoch keine spezifische Lösung für mein Problem finden, bei der Zeilen an einen R-Datenrahmen angehängt werden.
Ich initialisiere einen leeren 2-Spalten-Datenrahmen wie folgt.
df = data.frame(x = numeric(), y = character())
Dann ist es mein Ziel, eine Werteliste zu durchlaufen und in jeder Iteration einen Wert an das Ende der Liste anzuhängen. Ich habe mit dem folgenden Code begonnen.
for (i in 1:10) {
df$x = rbind(df$x, i)
df$y = rbind(df$y, toString(i))
}
Ich habe versucht , auch die Funktionen c
, append
und merge
ohne Erfolg. Bitte lassen Sie mich wissen, wenn Sie Vorschläge haben.
Antworten:
Aktualisieren
Da ich nicht weiß, was Sie versuchen, möchte ich Ihnen noch einen Vorschlag unterbreiten: Ordnen Sie Vektoren des gewünschten Typs für jede Spalte vor, fügen Sie Werte in diese Vektoren ein und erstellen Sie am Ende Ihre
data.frame
.Fortsetzung von Julians
f3
(einer vorab zugewiesenendata.frame
) als bisher schnellste Option, definiert als:Hier ist ein ähnlicher Ansatz, aber einer, bei dem
data.frame
der als letzter Schritt erstellt wird.microbenchmark
Aus dem "microbenchmark" -Paket erhalten wir umfassendere Einblicke alssystem.time
:f1()
(der folgende Ansatz) ist unglaublich ineffizient, da es so oft aufgerufen wirddata.frame
und weil das Wachsen von Objekten auf diese Weise in R. im Allgemeinen langsamf3()
ist. Dies wird aufgrund der Vorbelegung erheblich verbessert, aber diedata.frame
Struktur selbst könnte hier Teil des Engpasses sein.f4()
versucht, diesen Engpass zu umgehen, ohne den gewünschten Ansatz zu beeinträchtigen.Ursprüngliche Antwort
Das ist wirklich keine gute Idee, aber wenn Sie es so machen wollten, können Sie es versuchen:
Beachten Sie, dass es in Ihrem Code ein weiteres Problem gibt:
stringsAsFactors
wenn die Zeichen nicht in Faktoren umgewandelt werden sollen. Verwenden:df = data.frame(x = numeric(), y = character(), stringsAsFactors = FALSE)
quelle
data.frame
der erwarteten Endgrößen zuzuweisen und die Werte beim[
Extrahieren / Ersetzen hinzuzufügen .Vergleichen wir die drei vorgeschlagenen Lösungen:
Die beste Lösung besteht darin, Speicherplatz vorab zuzuweisen (wie in R vorgesehen). Die nächstbeste Lösung ist die Verwendung
list
, und die schlechteste Lösung (zumindest basierend auf diesen Timing-Ergebnissen) scheint zu seinrbind
.quelle
df <- rbind(df, data.frame(x = i, y = toString(i)))
Angenommen, Sie kennen die Größe des data.frame einfach nicht im Voraus. Es können durchaus ein paar Zeilen oder ein paar Millionen sein. Sie benötigen eine Art Container, der dynamisch wächst. Unter Berücksichtigung meiner Erfahrung und aller damit verbundenen Antworten in SO komme ich mit 4 verschiedenen Lösungen:
rbindlist
zum data.frameVerwenden Sie
data.table
die schnelleset
Bedienung und koppeln Sie sie bei Bedarf manuell mit dem Verdoppeln des Tisches.Verwenden Sie
RSQLite
die im Speicher befindliche Tabelle und hängen Sie sie an.data.frame
Die eigene Fähigkeit zu wachsen und eine benutzerdefinierte Umgebung (mit Referenzsemantik) zum Speichern des data.frame zu verwenden, damit er bei der Rückgabe nicht kopiert wird.Hier finden Sie einen Test aller Methoden für kleine und große Anzahl angehängter Zeilen. Jeder Methode sind 3 Funktionen zugeordnet:
create(first_element)
das gibt das entsprechende Hintergrundobjekt mitfirst_element
put in zurück.append(object, element)
das hängt daselement
an das Ende der Tabelle (dargestellt durchobject
).access(object)
bekommt dasdata.frame
mit allen eingefügten Elementen.rbindlist
zum data.frameDas ist ganz einfach und unkompliziert:
data.table::set
+ Verdoppeln Sie den Tisch bei Bedarf manuell.Ich werde die wahre Länge der Tabelle in einem
rowcount
Attribut speichern .SQL sollte für das schnelle Einfügen von Datensätzen optimiert werden, daher hatte ich anfangs große Hoffnungen
RSQLite
LösungDies ist im Grunde ein Kopieren und Einfügen von Karsten W. Antwort auf einen ähnlichen Thread.
data.frame
eigene zeilenanhängende + benutzerdefinierte Umgebung.Die Testsuite:
Der Einfachheit halber werde ich eine Testfunktion verwenden, um sie alle mit indirekten Aufrufen abzudecken. (Ich habe überprüft:
do.call
Wenn Sie die Funktionen nicht direkt aufrufen, wird der Code nicht länger messbar.)Sehen wir uns die Leistung für n = 10 Einfügungen an.
Ich habe auch eine 'Placebo'-Funktion (mit Suffix
0
) hinzugefügt , die nichts ausführt - nur um den Overhead des Testaufbaus zu messen.Für 1E5-Zeilen (Messungen mit Intel (R) Core (TM) i7-4710HQ-CPU bei 2,50 GHz):
Es sieht so aus, als ob die SQLite-basierte Lösung, obwohl sie bei großen Datenmengen wieder an Geschwindigkeit gewinnt, bei weitem nicht in der Nähe von data.table + manuellem exponentiellem Wachstum liegt. Der Unterschied beträgt fast zwei Größenordnungen!
Zusammenfassung
Wenn Sie wissen, dass Sie eine relativ kleine Anzahl von Zeilen anhängen (n <= 100), verwenden Sie die einfachste mögliche Lösung: Weisen Sie die Zeilen einfach dem data.frame in Klammernotation zu und ignorieren Sie die Tatsache, dass es sich um den data.frame handelt nicht vorbestellt.
Für alles andere verwenden
data.table::set
und erweitern Sie die data.table exponentiell (z. B. mit meinem Code).quelle
Update mit purrr, tidyr & dplyr
Da die Frage bereits datiert ist (6 Jahre), fehlt den Antworten eine Lösung mit neueren Paketen tidyr und purrr. Für Leute, die mit diesen Paketen arbeiten, möchte ich eine Lösung zu den vorherigen Antworten hinzufügen - alles sehr interessant, besonders.
Der größte Vorteil von Purrr und Tidyr ist meiner Meinung nach eine bessere Lesbarkeit. purrr ersetzt lapply durch die flexiblere map () -Familie, tidyr bietet die super-intuitive Methode add_row - macht einfach das, was es sagt :)
Diese Lösung ist kurz und intuitiv zu lesen und relativ schnell:
Es skaliert fast linear, sodass die Leistung für 1e5-Zeilen wie folgt lautet:
Dies würde es direkt nach data.table (wenn Sie das Placebo ignorieren) im Benchmark von @Adam Ryczkowski auf den zweiten Platz bringen:
quelle
add_row
. Zum Beispiel :map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) })
.bind_rows(df, map_dfr(1:1e5, function(x) { tibble(x = x, y = toString(x)) }))
anstelle von verwendenadd_row
.Nehmen wir einen Vektorpunkt mit Zahlen von 1 bis 5
point = c(1,2,3,4,5)
Wenn wir eine Zahl 6 irgendwo im Vektor anhängen möchten, kann der folgende Befehl nützlich sein
i) Vektoren
new_var = append(point, 6 ,after = length(point))
ii) Spalten einer Tabelle
new_var = append(point, 6 ,after = length(mtcars$mpg))
Der Befehl
append
akzeptiert drei Argumente:einfach...!! Entschuldigung für den Fall, dass ...!
quelle
Eine allgemeinere Lösung für könnte die folgende sein.
Die Funktion verlängernDf () erweitert einen Datenrahmen mit n Zeilen.
Als Beispiel:
quelle
Meine Lösung ist fast die gleiche wie die ursprüngliche Antwort, aber sie hat bei mir nicht funktioniert.
Also habe ich Namen für die Spalten gegeben und es funktioniert:
quelle