data.frame行到列表


123

我有一个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,]
}

Answers:


163

像这样:

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

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

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

4
请注意,在使用split每个元素后都使用类型data.frame with 1 rows and N columns而不是list of length N
Karol Daniluk,

我只补充说,如果您使用的split话,可能应该这样做,drop=T否则您的原始水平不会降低
Denis

51

尤里卡!

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

1
关心演示如何使用申请?
RomanLuštrik2010年

3
unlist(apply(xy.df, 1, list), recursive = FALSE)。但是,flodel的解决方案比使用apply或更为有效t
阿伦(Arun)

11
这里的问题是t将转换为data.famematrix以便列表中的元素是原子向量,而不是按请求的OP列出。除非您xy.df包含混合类型,
否则

2
如果要遍历这些值,则不建议使用apply。实际上,这只是在R中实现的for循环。lapply在C中执行循环,这明显更快。如果您要进行很多循环,则实际上最好使用这种行列表格式。
丽兹·桑德

1
从未来添加另一个评论,一个apply版本是.mapply(data.frame, xy.df, NULL)
alexis_laz

15

如果您想完全滥用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的函数使用此$索引)


我们如何将它们重新放在一起?将data.frames 列表变成一个data.frame
2014年

4
@AaronMcDaid您可以使用do.call和rbind:df == do.call(“ rbind”,ldf)
random_forest_fanatic

@AaronMcDaid或data.table :: rbindlist()。如果您的原始数据帧很大,那么速度将是可观的。
Empiromancer '16

8

更现代的解决方案仅使用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

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
) 

这返回

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

尽管差异没有我以前的测试中的大,但是setDF在max(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))

6

似乎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
)

结果:

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

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

在150行的微小数据集上进行基准测试没有多大意义,因为没有人会注意到微秒的任何差异,并且无法扩展
David Arenburg

4
by_row()现在已移至library(purrrlyr)
MrHopko '17

而且,除了陷入困境之外,它还将被弃用。现在还有其他结合tidyr :: nest,dplyr :: mutate purrr :: map的方法来达到相同的结果
Mike Stanley

3

还有更多选择:

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 
#....

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
#....

数据

set.seed(1234)
xy.df <- data.frame(x = runif(10),  y = runif(10))

2

对我来说最好的方法是:

示例数据:

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) 

1

另一种方法是将df转换为矩阵,然后lappy在其上应用list apply 函数:ldf <- lapply(as.matrix(myDF), function(x)x)


1

另一种使用方法library(purrr)(在大数据帧上似乎更快一些)

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

3
`by_row()`现在移到`library(purrrlyr)`
MrHopko '17

1

就像@flodel写道:这会将您的数据框转换为具有与数据框中的行数相同的元素数的列表:

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

您可以另外添加一个函数,以仅选择列表中每个元素中不属于NA的那些列

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

0

包中的by_row功能purrrlyr将为您完成此任务。

这个例子说明

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会放入名为的df中的新列表列.out。将$.out在上述语句的结束立即选择此列,返回一个列表的列表。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.