2010-08-16 68 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