webdevqa.jp.net

リストに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,]
}
106
Roman Luštrik

このような:

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

また、xy.dfの行名を出力リストの名前にしたい場合は、次のようにします。

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

ユーレカ!

xy.list <- as.list(as.data.frame(t(xy.df)))
47
Roman Luštrik

Data.frameを完全に悪用したい場合(私と同じように)、$機能を維持したい場合、data.frameをリストに集められた1行の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でさらに使用するのに役立ちます

14
Qiou Bi

今日、これに取り組んでいたのは、数百万の観測値と35列のdata.frame(実際にはdata.table)です。私の目標は、それぞれが1行のdata.frames(data.tables)のリストを返すことでした。つまり、各行を個別のdata.frameに分割し、これらをリストに保存したかったのです。

以下に、そのデータセットのsplit(dat, seq_len(nrow(dat)))よりも約3倍速い2つの方法を思い付きました。以下では、7500行、5列のデータセット(irisが50回繰り返される)で3つのメソッドをベンチマークします。

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

前回のテストほど差は大きくありませんが、max(setDF)<min(split)およびsetDFメソッドを使用すると、実行の分布のすべてのレベルで直線のattrメソッドが大幅に高速になります通常は2倍以上の速度です。

4番目の方法は極端なチャンピオンで、単純なネストされたlapplyで、ネストされたリストを返します。このメソッドは、リストからdata.frameを構築するコストを例示しています。さらに、私がdata.frame関数で試したすべての方法は、data.table手法よりもおよそ1桁遅くなりました。

データ

dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))
7
lmo

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で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
6
Artem Klevtsov

より現代的なソリューションは、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
3
Mike Stanley

私にとって最善の方法は次のとおりです。

サンプルデータ:

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) 
2
Cro-Magnon

別の方法は、dfをマトリックスに変換し、リストを適用して、その上にlappy関数を適用することです:ldf <- lapply(as.matrix(myDF), function(x)x)

1
user3553260

library(purrr)を使用する別の代替手段(大規模なdata.framesの方が少し速いようです)

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

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からの戻り値は、.outというdfの新しい リスト列 に入れられます。上記のステートメントの最後にある$.outは、すぐにこの列を選択し、リストのリストを返します。

0
RobinL

@flodelが書いたように:これは、データフレームを、データフレーム内の行数と同じ数の要素を持つリストに変換します。

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

リストの各要素のNA以外の列のみを選択に関数を追加できます:

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