wake-up-neo.com

Zeilenweise ein R-Datenframe erstellen

Ich möchte in R ein Zeile für Zeile einen Datenrahmen konstruieren. Ich habe etwas gesucht, und alles, was mir einfiel, ist der Vorschlag, eine leere Liste zu erstellen, einen Listenindex zu behalten und dann jedes Mal zur Liste hinzuzufügen ein einzeiliger Datenrahmen und den Listenindex um eins vorrücken. Zum Schluss do.call(rbind,) in der Liste.

Während dies funktioniert, erscheint es sehr umständlich. Gibt es nicht einen einfacheren Weg, um dasselbe Ziel zu erreichen?

Natürlich beziehe ich mich auf Fälle, in denen ich keine apply-Funktion verwenden kann und explizit den Datenrahmen zeilenweise erstellen muss. Gibt es zumindest eine Möglichkeit, Push in das Ende einer Liste zu gehen, anstatt den zuletzt verwendeten Index explizit zu verfolgen?

98
David B

Sie können sie Zeile für Zeile vergrößern, indem Sie rbind() verwenden oder verwenden. 

Das heißt nicht, dass Sie sollten. Dynamisch wachsende Strukturen sind eine der am wenigsten effizienten Codiermethoden in R.

Wenn Sie können, ordnen Sie Ihren gesamten data.frame vorab zu:

N <- 1e4  # total number of rows to preallocate--possibly an overestimate

DF <- data.frame(num=rep(NA, N), txt=rep("", N),  # as many cols as you need
                 stringsAsFactors=FALSE)          # you don't know levels yet

und dann während Ihrer Operationen Zeile für Zeile einfügen

DF[i, ] <- list(1.4, "foo")

Das sollte für beliebige data.frame funktionieren und wesentlich effizienter sein. Wenn Sie N überschritten haben, können Sie leere Zeilen am Ende immer herausschrumpfen.

88

Man kann Zeilen zu NULL hinzufügen:

df<-NULL;
while(...){
  #Some code that generates new row
  rbind(df,row)->df
}

zum Beispiel

df<-NULL
for(e in 1:10) rbind(df,data.frame(x=e,square=e^2,even=factor(e%%2==0)))->df
print(df)
46
mbq

Dies ist ein dummes Beispiel für die Verwendung von do.call(rbind,) für die Ausgabe von Map() [ähnlich wie lapply()]

> DF <- do.call(rbind,Map(function(x) data.frame(a=x,b=x+1),x=1:3))
> DF
  x y
1 1 2
2 2 3
3 3 4
> class(DF)
[1] "data.frame"

Ich benutze dieses Konstrukt oft.

9
hatmatrix

Der Grund, warum ich Rcpp so mag, ist, dass ich nicht immer weiß, wie R Core denkt, und mit Rcpp muss ich meistens nicht. 

In philosophischer Hinsicht befinden Sie sich in einem Zustand der Sünde in Bezug auf das funktionale Paradigma, das versucht sicherzustellen, dass jeder Wert erscheint unabhängig von jedem anderen Wert ist; Das Ändern eines Werts sollte niemals zu einer sichtbaren Änderung eines anderen Werts führen, wie Sie es mit Zeigern erhalten, die die Darstellung in C teilen. 

Die Probleme treten auf, wenn die funktionale Programmierung dem kleinen Fahrzeug signalisiert, sich aus dem Weg zu räumen, und das kleine Fahrzeug antwortet mit "Ich bin ein Leuchtturm". Wenn Sie eine lange Reihe kleiner Änderungen an einem großen Objekt vornehmen, das Sie in der Zwischenzeit bearbeiten möchten, befinden Sie sich in einem Leuchtturmgebiet. 

In der C++ - STL ist Push_back() eine Lebensweise. Es versucht nicht, funktional zu sein, aber es versucht, die gebräuchlichen Programmiersprachen effizient unterzubringen. 

Mit etwas Geschicklichkeit hinter den Kulissen können Sie manchmal einen Fuß in jeder Welt haben. Snapshot-basierte Dateisysteme sind ein gutes Beispiel (das sich aus Konzepten wie Union-Mounts entwickelte, die auch beide Seiten umfassen). 

Wenn R Core dies tun wollte, könnte der darunterliegende Vektorspeicher wie ein Union-Mount funktionieren. Eine Referenz auf den Vektorspeicher kann für Subskripte 1:N gelten, während eine andere Referenz auf denselben Speicher für Subskripte 1:(N+1) gilt. Es könnte reservierten Speicher geben, der noch nicht gültig von etwas anderem als für eine schnelle Push_back() geeignet referenziert wurde. Sie verletzen das Funktionskonzept nicht, wenn Sie außerhalb des Bereichs hängen, den eine vorhandene Referenz für gültig hält. 

Durch das schrittweise Anhängen von Zeilen ist der reservierte Speicher erschöpft. Sie müssen neue Kopien von allem erstellen, wobei der Speicher mit einem Zuwachs multipliziert wird. Die STL-Implementierungen, die ich verwende, neigen dazu, beim Erweitern der Zuweisung den Speicher mit 2 zu multiplizieren. Ich dachte, ich habe in R Internals gelesen, dass es eine Speicherstruktur gibt, bei der der Speicher um 20% erhöht wird. In jedem Fall treten Wachstumsoperationen mit logarithmischer Häufigkeit relativ zu der Gesamtzahl der angefügten Elemente auf. Amortisiert ist dies in der Regel akzeptabel. 

Als Tricks hinter den Kulissen gehen, habe ich schlimmeres gesehen. Jedes Mal, wenn Sie eine neue Zeile in den Datenrahmen Push_back() einfügen, muss eine Indexstruktur der obersten Ebene kopiert werden. Die neue Zeile könnte an eine gemeinsam genutzte Darstellung angehängt werden, ohne die alten funktionalen Werte zu beeinflussen. Ich glaube nicht einmal, dass es den Müllsammler sehr erschweren würde. da ich nicht Push_front() vorschlage, sind alle Referenzen Präfixverweise auf die Vorderseite des zugewiesenen Vektorspeichers. 

8
Allan Stokes

Die Antwort von Dirk Eddelbuettel ist die beste; Ich möchte hier nur anmerken, dass Sie die Datenframe-Dimensionen oder Datentypen nicht vorab spezifizieren können, was manchmal nützlich ist, wenn Sie mehrere Datentypen und viele Spalten haben:

row1<-list("a",1,FALSE) #use 'list', not 'c' or 'cbind'!
row2<-list("b",2,TRUE)  

df<-data.frame(row1,stringsAsFactors = F) #first row
df<-rbind(d,row2) #now this works as you'd expect.
1
John

Wenn Sie Vektoren haben, die zu Zeilen werden sollen, verketten Sie sie mithilfe von c(), übergeben Sie sie Zeile für Zeile an eine Matrix und konvertieren Sie diese Matrix in ein Datenframe.

Zum Beispiel Zeilen

dummydata1=c(2002,10,1,12.00,101,426340.0,4411238.0,3598.0,0.92,57.77,4.80,238.29,-9.9)
dummydata2=c(2002,10,2,12.00,101,426340.0,4411238.0,3598.0,-3.02,78.77,-9999.00,-99.0,-9.9)
dummydata3=c(2002,10,8,12.00,101,426340.0,4411238.0,3598.0,-5.02,88.77,-9999.00,-99.0,-9.9)

kann in einen Datenrahmen umgewandelt werden:

dummyset=c(dummydata1,dummydata2,dummydata3)
col.len=length(dummydata1)
dummytable=data.frame(matrix(data=dummyset,ncol=col.len,byrow=TRUE))

Zugegebenermaßen sehe ich zwei wesentliche Einschränkungen: (1) Dies funktioniert nur mit Singlemode-Daten, und (2) Sie müssen Ihre letzten # Spalten kennen, damit dies funktioniert (dh ich gehe davon aus, dass Sie nicht mit einer zerlumptes Array, dessen größte Zeilenlänge unbekannt ist a priori ).

Diese Lösung scheint einfach zu sein, aber aus meiner Erfahrung mit Typkonvertierungen in R bin ich sicher, dass sie neue Herausforderungen schafft. Kann sich jemand dazu äußern?

0
Keegan Smith

Ich habe auf diese Weise gefunden, Datenframes mit RAW ohne Matrix zu erstellen.

Mit automatischem Spaltennamen

df<-data.frame(
        t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
        ,row.names = NULL,stringsAsFactors = FALSE
    )

Mit dem Spaltennamen

df<-setNames(
        data.frame(
            t(data.frame(c(1,"a",100),c(2,"b",200),c(3,"c",300)))
            ,row.names = NULL,stringsAsFactors = FALSE
        ), 
        c("col1","col2","col3")
    )
0
phili_b