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



답변

이렇게 :

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

xy.df이름이 출력 목록의 이름이되도록하려면 다음을 수행 할 수 있습니다.

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


답변

유레카!

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


답변

내가하는 것처럼 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에 전달하는 함수가이 $ 인덱싱을 사용한다고 가정).


답변

보다 현대적인 솔루션은 다음 만 사용합니다 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


답변

저는 오늘 수백만 개의 관찰과 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))


답변

현재 버전의 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;
}

이제 caompare 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


답변

몇 가지 추가 옵션 :

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