2010-08-16 65 views
68

我有一個data.frame,我想按行轉換爲列表,這意味着每一行將對應於它自己的列表元素。換句話說,只要data.frame有行,我想要一個列表。data.frame行到列表

到目前爲止,我已經按照以下方式解決了這個問題,但我想知道是否有更好的方法來解決這個問題。

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,] 
} 

回答

87

喜歡這個:

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

如果你想的xy.df的rownames是輸出列表的名稱,你可以這樣做:

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

尤里卡!

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

打我;-)。不過,如果您只想循環使用這些值,最好使用apply。 – mbq 2010-08-16 13:16:20

+1

小心演示如何使用apply? – 2010-08-17 06:04:45

+2

'unlist(apply(xy.df,1,list),recursive = FALSE)'。然而flodel的解決方案比使用'apply'或't'更有效率。 – Arun 2013-05-14 09:13:24

11

如果您想徹底濫用data.frame(像我一樣),喜歡保持$功能,一個辦法就是拆你data.frame成單行data.frames聚集在一個列表:

> df = data.frame(x=c('a','b','c'), y=3:1) 
> df 
    x y 
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]] 
x y 
1 a 3  
[[2]] 
x y 
2 b 2 
[[3]] 
x y 
3 c 1 

# and the 'coolest' 
> ldf[[2]]$y 
[1] 2 

它不僅是智力自慰,但允許以「改造」的data.frame到其行的列表,保持$指數可以是與lapply進一步使用有用的(假設你的函數通過lapply使用這個$索引)

+0

我們如何重新組合它們?將'data.frame'列表變成一個'data.frame'? – 2014-10-07 13:21:45

+3

@AaronMcDaid您可以使用do.call和rbind:df == do.call(「rbind」,ldf) – 2015-03-04 08:42:35

+0

@AaronMcDaid或data.table :: rbindlist()。如果您的原始數據幀很大,速度增益將會很大。 – Empiromancer 2016-07-12 22:04:55

1

的另一種方法是將DF轉化爲矩陣然後應用列表適用lappy功能在它:

2

使用library(purrr)另一種方法(這似乎是有點快上大data.frames)

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

'by_row()'現在已經轉移到'library(purrrlyr)' – MrHopko 2017-08-17 09:35:41

5

似乎purrr(0.2.2)包的當前版本是最快的解決方案:

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

讓我們比較一下最有趣的解決方案:

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 
) 

Rsults:

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 

另外我們可以用Rcpp得到同樣的結果:

#include <Rcpp.h> 
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<IntegerVector>(x[j]); 
         RObject t2 = wrap(t[i]); 
         t2.attr("class") = "factor"; 
         t2.attr("levels") = t.attr("levels"); 
         tmp[j] = t2; 
        } else { 
         tmp[j] = as<IntegerVector>(x[j])[i]; 
        } 
        break; 
       } 
       case LGLSXP: { 
        tmp[j] = as<LogicalVector>(x[j])[i]; 
        break; 
       } 
       case CPLXSXP: { 
        tmp[j] = as<ComplexVector>(x[j])[i]; 
        break; 
       } 
       case REALSXP: { 
        tmp[j] = as<NumericVector>(x[j])[i]; 
        break; 
       } 
       case STRSXP: { 
        tmp[j] = as<std::string>(as<CharacterVector>(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; 
} 

現在用purrr caompare:

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

結果:

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 
+0

在一個150行的小數據集上進行基準測試沒有多大意義,因爲沒有人會注意到微秒的任何差異,並且它不會縮放 – 2017-03-26 06:56:13

+0

你'再右吧。答案已更新。 – 2017-03-26 09:39:42

+2

'by_row()'現在已經轉移到'library(purrrlyr)' – MrHopko 2017-05-26 16:20:08

0

purrrlyr包的by_row功能會爲你做到這一點。

這個例子演示了

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 

默認情況下,從myfn返回值被放入一個新的list column在DF稱爲.out。上述語句末尾的$.out立即選擇此列,並返回列表列表。

1

對我來說,最好的辦法是:

示例數據:

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 

我們稱之爲BBmisc

library(BBmisc) 

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

而結果將是:

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) 
0

大號IKE @flodel寫道: 這是你數據幀轉換爲具有相同數量的數據幀元素的行數的列表:

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

可以additionaly添加功能只選擇那些沒有NA列列表中的每個元素:

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

一個更現代的解決方案只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