data.frame Zeilen zu einer Liste

Ich habe einen data.frame, den ich in eine Liste nach Zeilen konvertieren möchte, was bedeutet, dass jede Zeile ihren eigenen Listenelementen entspricht. Mit anderen Worten, ich möchte eine Liste, die so lang ist wie der data.frame Zeilen hat.

Bis jetzt habe ich dieses Problem auf folgende Weise angegangen, aber ich habe mich gefragt, ob es einen besseren Weg gibt, sich diesem Problem zu nähern.

xy.df <- data.frame(x = runif(10), y = runif(10)) # pre-allocate a list and fill it with a loop xy.list <- vector("list", nrow(xy.df)) for (i in 1:nrow(xy.df)) { xy.list[[i]] <- xy.df[i,] } 

So was:

 xy.list < - split(xy.df, seq(nrow(xy.df))) 

Wenn Sie möchten, dass die Namen von xy.df die Namen der Ausgabeliste sind, können Sie xy.df tun:

 xy.list < - setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df)) 

Eureka!

 xy.list < - as.list(as.data.frame(t(xy.df))) 

Wenn Sie den data.frame vollständig missbrauchen wollen und die $ -functionalität behalten möchten, besteht eine Möglichkeit darin, data.frame in einzeilige data.frames zu zerlegen, die in einer Liste zusammengefasst sind:

 > df = data.frame(x=c('a','b','c'), y=3:1) > df xy 1 a 3 2 b 2 3 c 1 # 'convert' into a list of data.frames ldf = lapply(as.list(1:dim(df)[1]), function(x) df[x[1],]) > ldf [[1]] xy 1 a 3 [[2]] xy 2 b 2 [[3]] xy 3 c 1 # and the 'coolest' > ldf[[2]]$y [1] 2 

Es ist nicht nur intellektuelle Masturbation, sondern erlaubt es, den data.frame in eine Liste seiner Linien zu transformieren, wobei die $ indexation beibehalten wird, die für die weitere Verwendung mit lapply nützlich sein kann (vorausgesetzt, die function, die Sie an lapply übergeben, verwendet diese $ indexation).

Scheint eine aktuelle Version des purrr (0.2.2) -Pakets ist die schnellste Lösung:

 by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out 

Vergleichen wir die interessantesten Lösungen:

 data("Batting", package = "Lahman") x < - Batting[1:10000, 1:10] library(benchr) library(purrr) benchmark( split = split(x, seq_len(.row_names_info(x, 2L))), mapply = .mapply(function(...) structure(list(...), class = "data.frame", row.names = 1L), x, NULL), purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out ) 

Ergebnisse:

 Benchmark summary: Time units : milliseconds expr n.eval min lw.qu median mean up.qu max total relative split 100 983.0 1060.0 1130.0 1130.0 1180.0 1450 113000 34.3 mapply 100 826.0 894.0 963.0 972.0 1030.0 1320 97200 29.3 purrr 100 24.1 28.6 32.9 44.9 40.5 183 4490 1.0 

Außerdem können wir mit Rcpp dasselbe Ergebnis Rcpp :

 #include  using namespace Rcpp; // [[Rcpp::export]] List df2list(const DataFrame& x) { std::size_t nrows = x.rows(); std::size_t ncols = x.cols(); CharacterVector nms = x.names(); List res(no_init(nrows)); for (std::size_t i = 0; i < nrows; ++i) { List tmp(no_init(ncols)); for (std::size_t j = 0; j < ncols; ++j) { switch(TYPEOF(x[j])) { case INTSXP: { if (Rf_isFactor(x[j])) { IntegerVector t = as(x[j]); RObject t2 = wrap(t[i]); t2.attr("class") = "factor"; t2.attr("levels") = t.attr("levels"); tmp[j] = t2; } else { tmp[j] = as(x[j])[i]; } break; } case LGLSXP: { tmp[j] = as(x[j])[i]; break; } case CPLXSXP: { tmp[j] = as(x[j])[i]; break; } case REALSXP: { tmp[j] = as(x[j])[i]; break; } case STRSXP: { tmp[j] = as(as(x[j])[i]); break; } default: stop("Unsupported type '%s'.", type2name(x)); } } tmp.attr("class") = "data.frame"; tmp.attr("row.names") = 1; tmp.attr("names") = nms; res[i] = tmp; } res.attr("names") = x.attr("row.names"); return res; } 

Jetzt mit purrr :

 benchmark( purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out, rcpp = df2list(x) ) 

Ergebnisse:

 Benchmark summary: Time units : milliseconds expr n.eval min lw.qu median mean up.qu max total relative purrr 100 25.2 29.8 37.5 43.4 44.2 159.0 4340 1.1 rcpp 100 19.0 27.9 34.3 35.8 37.2 93.8 3580 1.0 

Ich habe heute an einem data.frame gearbeitet (wirklich eine data.table) mit Millionen von Beobachtungen und 35 Spalten. Mein Ziel war es, eine Liste von data.frames (data.tables) mit jeweils einer einzelnen Zeile zurückzugeben. Das heißt, ich wollte jede Zeile in einen separaten data.frame aufteilen und diese in einer Liste speichern.

Hier sind zwei Methoden, die ich gefunden habe, die ungefähr 3 Mal schneller waren als split(dat, seq_len(nrow(dat))) für diesen Datensatz. Im Folgenden benenne ich die drei Methoden auf einem 5-Spalten-Datensatz mit 7500 Zeilen (die Iris wurde 50-mal wiederholt).

 library(data.table) library(microbenchmark) microbenchmark( split={dat1 < - split(dat, seq_len(nrow(dat)))}, setDF={dat2 <- lapply(seq_len(nrow(dat)), function(i) setDF(lapply(dat, "[", i)))}, attrDT={dat3 <- lapply(seq_len(nrow(dat)), function(i) { tmp <- lapply(dat, "[", i) attr(tmp, "class") <- c("data.table", "data.frame") setDF(tmp) })}, datList = {datL <- lapply(seq_len(nrow(dat)), function(i) lapply(dat, "[", i))}, times=20 ) 

Dies kehrt zurück

 Unit: milliseconds expr min lq mean median uq max neval split 861.8126 889.1849 973.5294 943.2288 1041.7206 1250.6150 20 setDF 459.0577 466.3432 511.2656 482.1943 500.6958 750.6635 20 attrDT 399.1999 409.6316 461.6454 422.5436 490.5620 717.6355 20 datList 192.1175 201.9896 241.4726 208.4535 246.4299 411.2097 20 

Obwohl die Unterschiede nicht so groß wie in meinem vorherigen Test sind, ist die Methode setDF auf allen Ebenen der Verteilung von Läufen mit max (setDF) attr ist typischerweise mehr als doppelt so schnell.

Eine vierte Methode ist der extreme Champion, bei dem es sich um ein einfaches verschachteltes lapply , das eine verschachtelte Liste lapply . Diese Methode veranschaulicht die Kosten für die Erstellung eines Datenrahmens aus einer Liste. Außerdem waren alle Methoden, die ich mit der function data.frame , ungefähr eine Größenordnung langsamer als die data.table Techniken.

Daten

 dat < - vector("list", 50) for(i in 1:50) dat[[i]] <- iris dat <- setDF(rbindlist(dat)) 

Eine weitere Alternative, die eine library(purrr) (die auf großen data.frames etwas schneller zu sein scheint)

 flatten(by_row(xy.df, ..f = function(x) flatten_chr(x), .labels = FALSE)) 

Der beste Weg für mich war:

Beispieldaten:

 Var1< -c("X1",X2","X3") Var2<-c("X1",X2","X3") Var3<-c("X1",X2","X3") Data<-cbind(Var1,Var2,Var3) ID Var1 Var2 Var3 1 X1 X2 X3 2 X4 X5 X6 3 X7 X8 X9 

Wir rufen die BBmisc Bibliothek auf

 library(BBmisc) data$lists< -convertRowsToList(data[,2:4]) 

Und das Ergebnis wird sein:

 ID Var1 Var2 Var3 lists 1 X1 X2 X3 list("X1", "X2", X3") 2 X4 X5 X6 list("X4","X5", "X6") 3 X7 X8 X9 list("X7,"X8,"X9) 

Eine alternative Möglichkeit besteht darin, das df in eine Matrix zu konvertieren und dann die Liste lappy function darauf ldf < - lapply(as.matrix(myDF), function(x)x) : ldf < - lapply(as.matrix(myDF), function(x)x)

Die by_row function aus dem purrrlyr Paket wird dies für Sie tun.

Dieses Beispiel demonstriert

 myfn < - function(row) { #row is a tibble with one row, and the same number of columns as the original df l <- as.list(row) return(l) } list_of_lists <- purrrlyr::by_row(df, myfn, .labels=FALSE)$.out 

Standardmäßig wird der zurückgegebene Wert von myfn in eine neue .out im df namens .out . Das $.out am Ende der obigen statement wählt diese Spalte sofort aus und gibt eine Liste mit Listen zurück.

Eine modernere Lösung verwendet nur purrr::transpose :

 library(purrr) iris[1:2,] %>% purrr::transpose() #> [[1]] #> [[1]]$Sepal.Length #> [1] 5.1 #> #> [[1]]$Sepal.Width #> [1] 3.5 #> #> [[1]]$Petal.Length #> [1] 1.4 #> #> [[1]]$Petal.Width #> [1] 0.2 #> #> [[1]]$Species #> [1] 1 #> #> #> [[2]] #> [[2]]$Sepal.Length #> [1] 4.9 #> #> [[2]]$Sepal.Width #> [1] 3 #> #> [[2]]$Petal.Length #> [1] 1.4 #> #> [[2]]$Petal.Width #> [1] 0.2 #> #> [[2]]$Species #> [1] 1 

Wie @flodel hat geschrieben: Dies konvertiert Ihren Datenrahmen in eine Liste, die die gleiche Anzahl von Elementen wie die Anzahl der Zeilen im Dataframe hat:

 NewList < - split(df, f = seq(nrow(df))) 

Sie können außerdem eine function hinzufügen, um nur die Spalten auszuwählen, die nicht in jedem Element der Liste enthalten sind:

 NewList2 < - lapply(NewList, function(x) x[,!is.na(x)])