2010-12-22 5 views
42

Sehr oft möchte ich eine Liste konvertieren, in der jeder Index identische Elementtypen zu einem Datenrahmen hat. Zum Beispiel kann ich eine Liste haben:Was ist der effizienteste Weg, eine Liste als Datenrahmen zu erzeugen?

> my.list 
[[1]] 
[[1]]$global_stdev_ppb 
[1] 24267673 

[[1]]$range 
[1] 0.03114799 

[[1]]$tok 
[1] "hello" 

[[1]]$global_freq_ppb 
[1] 211592.6 


[[2]] 
[[2]]$global_stdev_ppb 
[1] 11561448 

[[2]]$range 
[1] 0.08870838 

[[2]]$tok 
[1] "world" 

[[2]]$global_freq_ppb 
[1] 1002043 

Ich möchte diese Liste zu einem Datenrahmen konvertieren, wobei jedes Indexelement eine Spalte. Die natürliche (für mich), was zu gehen ist, verwenden do.call:

> my.matrix<-do.call("rbind", my.list) 
> my.matrix 
    global_stdev_ppb range  tok  global_freq_ppb 
[1,] 24267673   0.03114799 "hello" 211592.6  
[2,] 11561448   0.08870838 "world" 1002043 

einfach genug, aber wenn ich versuche, diese Matrix als Datenrahmen zu werfen, bleiben die Spalten Listenelemente, anstatt die Vektoren:

> my.df<-as.data.frame(my.matrix, stringsAsFactors=FALSE) 
> my.df[,1] 
[[1]] 
[1] 24267673 

[[2]] 
[1] 11561448 

Derzeit den Datenrahmen gegossen richtig erhalte ich über jede Spalte am Iterieren unlist und as.vector verwenden, dann den Datenrahmen als solche Neufassung:

new.list<-lapply(1:ncol(my.matrix), function(x) as.vector(unlist(my.matrix[,x]))) 
my.df<-as.data.frame(do.call(cbind, new.list), stringsAsFactors=FALSE) 

Dies scheint jedoch sehr ineffizient. Gibt es einen besseren Weg, dies zu tun?

+1

siehe '? Data.table :: rbindlist' – marbel

+0

Ab 2017 sollten Sie' verwenden your_list%>% reduce (bind_rows) '' von purrr' – Zafar

Antwort

47

Ich glaube, Sie wollen:

> do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE)) 
    global_stdev_ppb  range tok global_freq_ppb 
1   24267673 0.03114799 hello  211592.6 
2   11561448 0.08870838 world  1002043.0 
> str(do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE))) 
'data.frame': 2 obs. of 4 variables: 
$ global_stdev_ppb: num 24267673 11561448 
$ range   : num 0.0311 0.0887 
$ tok    : chr "hello" "world" 
$ global_freq_ppb : num 211593 1002043 
+10

'plyr :: rbind.fill' ist oft ein wenig schneller als 'rbind.fill', und die ganze Operation ist äquivalent zu' plyr :: ldply (my.list, data.frame) ' – hadley

17

Ich kann Ihnen nicht sagen, das ist die „effizienteste“ in Bezug auf Speicher oder Geschwindigkeit, aber es ist ziemlich effizient in Bezug auf die Codierung:

my.df <- do.call("rbind", lapply(my.list, data.frame)) 

die lapply() Schritt mit data.frame() werden jedes Listenelement in einen einreihigen Datenrahmen wird, die dann mit rbind schön wirkt()

31

eine andere Möglichkeit ist:

data.frame(t(sapply(mylist, `[`))) 

aber diese einfache Manipulation Ergebnisse in einem Datenrahmen von Listen:

> str(data.frame(t(sapply(mylist, `[`)))) 
'data.frame': 2 obs. of 3 variables: 
$ a:List of 2 
    ..$ : num 1 
    ..$ : num 2 
$ b:List of 2 
    ..$ : num 2 
    ..$ : num 3 
$ c:List of 2 
    ..$ : chr "a" 
    ..$ : chr "b" 

Eine Alternative dazu, in die gleiche Richtung, aber nun das Ergebnis gleiche wie die anderen Lösungen ist:

data.frame(lapply(data.frame(t(sapply(mylist, `[`))), unlist)) 

[Edit: enthalten Timings von @Martin Morgans zwei Lösungen, die die Kante über die andere Lösung, die einen Datenrahmen von Vektoren zurückgeben.] Einige repräsentative Timings auf eine sehr einfaches Problem:

mylist <- list(list(a = 1, b = 2, c = "a"), list(a = 2, b = 3, c = "b")) 

> ## @Joshua Ulrich's solution: 
> system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame, 
+          stringsAsFactors=FALSE)))) 
    user system elapsed 
    1.740 0.001 1.750 

> ## @JD Long's solution: 
> system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame)))) 
    user system elapsed 
    2.308 0.002 2.339 

> ## my sapply solution No.1: 
> system.time(replicate(1000, data.frame(t(sapply(mylist, `[`))))) 
    user system elapsed 
    0.296 0.000 0.301 

> ## my sapply solution No.2: 
> system.time(replicate(1000, data.frame(lapply(data.frame(t(sapply(mylist, `[`))), 
+            unlist)))) 
    user system elapsed 
    1.067 0.001 1.091 

> ## @Martin Morgan's Map() sapply() solution: 
> f = function(x) function(i) sapply(x, `[[`, i) 
> system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) 
    user system elapsed 
    0.775 0.000 0.778 

> ## @Martin Morgan's Map() lapply() unlist() solution: 
> f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE) 
> system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) 
    user system elapsed 
    0.653 0.000 0.658 
+0

Hrm .. die' replicate() 'Verwendung in dieser Antwort ist ein bisschen seltsam. Sie testen, wie effizient es ist, eine kleine Liste oft in einen Datenrahmen zu konvertieren. Das scheint nur selten nützlich zu sein. Wäre es nicht sinnvoller, die Effizienz der Umwandlung einer * großen * Liste von Listen zu testen? – naught101

+0

@ naught101 möglicherweise; Sie haben den Code, probieren Sie es aus ;-) (melden Sie Ergebnisse zurück --- Sie können sie in meine Antwort bearbeiten, wenn Sie möchten) –

+0

@ naught101 Ich habe eine [means] (http://stackoverflow.com/questions/26534438/intradataframe-analyse-creating-a-derivate-datenframe-from-another-data-fram/26535386) zum Erstellen einer solchen Liste gegeben jemand hat einen großen Datenrahmen zu knacken die Zahlen auf. – jxramos

13

Diese

f = function(x) function(i) sapply(x, `[[`, i) 

ist eine Funktion, die eine Funktion, die das i-te Element von x extrahiert zurückgibt. So

Map(f(mylist), names(mylist[[1]])) 

bekommt einen Namen (danke Karte!) Liste von Vektoren, die

as.data.frame(Map(f(mylist), names(mylist[[1]]))) 

Für Geschwindigkeit in einen Datenrahmen gemacht werden können, ist es in der Regel schneller unlist(lapply(...), use.names=FALSE) als

f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE) 

Eine allgemeinere Variante

ist
f = function(X, FUN) function(...) sapply(X, FUN, ...) 

zu verwenden, wenn das zu tun List-of-Lists-Strukturen kommen auf? Vielleicht gibt es einen früheren Schritt, bei dem eine Iteration durch etwas Vektorisierteres ersetzt werden könnte?

+2

+1 für die Abbildung von 'Map'. Ich muss 'Map',' Reduce' et einbauen. al in meine Alltagsroutinen ... –

+0

Wie benutzt man diese Dinge? Die 'as.data.frame (Map (f (mylist), Namen (Meine Liste)))' Version funktioniert nicht für mich für die Art von Daten @DrewConway und ich verwendet, da die Listen keine Namen haben; Ich bekomme das stattdessen 'Datenrahmen mit 0 Spalten und 0 Zeilen'. Selbst mit Namen kann ich das in meiner Antwort nicht für "Meine Liste" verwenden. Ich bin wirklich neugierig, da ich 'Map' und andere überhaupt nicht verwendet habe, also daran interessiert bin, wie sie arbeiten, was sie tun, wann sie am besten eingesetzt werden. –

+0

Hoppla, sollten Namen sein (mylist [[1] ]), dh, holen Sie sich die Namen der Unterelemente vom ersten Element. –

14

Obwohl diese Frage seit längst beantwortet, es lohnt sich, die data.table Paket unter Hinweis darauf hat rbindlist die sehr diese Aufgabe erfüllt schnell:

library(microbenchmark) 
library(data.table) 
l <- replicate(1E4, list(a=runif(1), b=runif(1), c=runif(1)), simplify=FALSE) 

microbenchmark(times=5, 
    R=as.data.frame(Map(f(l), names(l[[1]]))), 
    dt=data.frame(rbindlist(l)) 
) 

mich gibt

Unit: milliseconds 
expr  min  lq median  uq  max neval 
    R 31.060119 31.403943 32.278537 32.370004 33.932700  5 
    dt 2.271059 2.273157 2.600976 2.635001 2.729421  5 
2

Die dplyr Pakets bind_rows ist effizient.

one <- mtcars[1:4, ] 
two <- mtcars[11:14, ] 
system.time(dplyr::bind_rows(one, two)) 
    user system elapsed 
    0.001 0.000 0.001 
Verwandte Themen