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

回答:


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
アルン

11
ここでの問題はtdata.fameをaに変換して、matrixリスト内の要素が、要求されたOPのリストではなく、アトミックベクトルになるようにすることです。xy.df混合型が含まれるまでは、通常は問題ありません...
Calimo

2
値をループする場合は、お勧めしませんapply。これは、実際にはRに実装されているforループにすぎませんlapply。Cでループを実行するため、非常に高速です。ループが多い場合は、この行リスト形式が実際に適しています。
Liz Sander、2015

1
未来からの別のコメントを追加すると、applyバージョンは.mapply(data.frame, xy.df, NULL)
alexis_laz '24

15

(私がするように)data.frameを完全に悪用し、$機能を維持したい場合、1つの方法は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でさらに使用するのに役立つ$インデックスを保持できるようにします(lapplyに渡す関数がこの$インデックスを使用するとします)


どのようにしてそれらを再び元に戻すのですか?data.framesのリストを単一に変換しdata.frameますか?
アーロンマクデイド2014年

4
@AaronMcDaid do.callとrbindを使用できます:df == do.call( "rbind"、ldf)
random_forest_fanatic

@AaronMcDaidまたはdata.table :: rbindlist()。元のデータフレームが大きい場合は、速度が大幅に向上します。
エンピロマンサー2016

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)のためにこれに取り組んでいました。私の目標は、それぞれが1行のdata.frames(data.tables)のリストを返すことでした。つまり、各行を個別のdata.frameに分割し、これらをリストに格納したかったのです。

ここで私が思いついた2つの方法はsplit(dat, seq_len(nrow(dat)))、そのデータセットよりも約3倍高速でした。以下では、7500行、5列のデータセットで3つの方法をベンチマークします(アイリスが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通常メソッドは2倍以上高速です。

4番目の方法は、極端なチャンピオンです。これはlapply、ネストされたリストを返す単純なネストされたです。このメソッドは、リストからdata.frameを作成するコストの例です。さらに、私がこのdata.frame関数で試した方法はすべて、およそ1桁遅い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

そして、うんざりすることに加えて、それは廃止されようとしています。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関数を適用する方法です。ldf <- lapply(as.matrix(myDF), function(x)x)


1

別の代替方法library(purrr)(大きなdata.framesでは少し速いようです)

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

3
`by_row()`は `library(purrrlyr)`に移動しました
MrHopko

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上記の文の終わりには、すぐにリストのリストを返す、この列を選択します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.