data.frame行到列表

Rom*_*rik 112 r list dataframe

我有一个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,]
}
Run Code Online (Sandbox Code Playgroud)

flo*_*del 146

像这样:

xy.list <- split(xy.df, seq(nrow(xy.df)))
Run Code Online (Sandbox Code Playgroud)

如果您希望rownames xy.df成为输出列表的名称,您可以执行以下操作:

xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))
Run Code Online (Sandbox Code Playgroud)

  • 注意,在使用`split`之后,每个元素的类型为`data.frame with 1 rows and N column'而不是`list of length N` (2认同)

Rom*_*rik 49

找到了!

xy.list <- as.list(as.data.frame(t(xy.df)))
Run Code Online (Sandbox Code Playgroud)

  • 这里的问题是`t`将`data.fame`转换为`matrix`,这样列表中的元素就是原子向量,而不是OP请求的列表.在你的`xy​​.df`包含混合类型之前通常不会有问题...... (10认同)
  • `unlist(apply(xy.df,1,list),recursive = FALSE)`.然而,flodel的解决方案比使用`apply`或`t`更有效. (3认同)
  • 如果你想循环这些值,我不建议`apply`.它实际上只是在R.中实现的for循环."lapply"在C中执行循环,这明显更快.如果您进行大量循环,那么这种行列表格式实际上更可取. (2认同)

小智 14

如果你想完全滥用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
Run Code Online (Sandbox Code Playgroud)

它不仅是智能手淫,而且允许将data.frame"转换"为其行的列表,保留$ indexation,这对于lapply的进一步使用是有用的(假设你传递给lapply的函数使用这个$ indexation)

  • @AaronMcDaid你可以使用do.call和rbind:df == do.call("rbind",ldf) (4认同)

lmo*_*lmo 8

我今天正在研究一个包含数百万个观测数据和35个列的data.frame(实际上是一个data.table).我的目标是返回一个data.frames(data.tables)列表,每个列都有一行.也就是说,我想将每一行拆分成一个单独的data.frame并将它们存储在一个列表中.

以下是我提出的两种方法,大约比split(dat, seq_len(nrow(dat)))该数据集快3倍.下面,我对7500行,5列数据集(虹膜重复50次)的三种方法进行基准测试.

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
) 
Run Code Online (Sandbox Code Playgroud)

这回来了

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
Run Code Online (Sandbox Code Playgroud)

虽然差异不像我之前的测试那么大,但是在setDFmax(setDF)<min(split)的运行分布的所有级别上,直接方法明显更快,并且该attr方法通常快两倍以上.

第四种方法是极端冠军,它是一个简单的嵌套lapply,返回一个嵌套列表.此方法举例说明了从列表构造data.frame的成本.此外,我尝试使用该data.frame函数的所有方法都比data.table技术慢大约一个数量级.

数据

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


Ron*_*hah 8

还有几个选项:

asplit

asplit(xy.df, 1)
#[[1]]
#     x      y 
#0.1137 0.6936 

#[[2]]
#     x      y 
#0.6223 0.5450 

#[[3]]
#     x      y 
#0.6093 0.2827 
#....
Run Code Online (Sandbox Code Playgroud)

随着splitrow

split(xy.df, row(xy.df)[, 1])

#$`1`
#       x      y
#1 0.1137 0.6936

#$`2`
#       x     y
#2 0.6223 0.545

#$`3`
#       x      y
#3 0.6093 0.2827
#....
Run Code Online (Sandbox Code Playgroud)

数据

set.seed(1234)
xy.df <- data.frame(x = runif(10),  y = runif(10))
Run Code Online (Sandbox Code Playgroud)


Art*_*sov 6

似乎当前版本的purrr(0.2.2)软件包是最快的解决方案:

by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out
Run Code Online (Sandbox Code Playgroud)

让我们比较最有趣的解决方案:

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
)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我们也可以得到相同的结果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;
}
Run Code Online (Sandbox Code Playgroud)

现在与purrr:

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

结果:

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
Run Code Online (Sandbox Code Playgroud)

  • `by_row()`现在已经转移到`library(purrrlyr)` (4认同)

Mik*_*ley 5

A more modern solution uses only 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
Run Code Online (Sandbox Code Playgroud)