data.frame satırları bir listeye


123

Satırlara göre bir listeye dönüştürmek istediğim bir data.frame var, yani her satır kendi liste öğelerine karşılık gelir. Başka bir deyişle, data.frame'in satırları olduğu sürece bir liste istiyorum.

Şimdiye kadar, bu sorunu aşağıdaki şekilde ele aldım, ancak buna yaklaşmanın daha iyi bir yolu olup olmadığını merak ediyordum.

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

Yanıtlar:


164

Bunun gibi:

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

Ve satır xy.dfadlarının çıktı listesinin adları olmasını istiyorsanız, şunları yapabilirsiniz:

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

4
Kullandıktan sonra, unutmayın splittürüne sahip her eleman data.frame with 1 rows and N columnsyerinelist of length N
Karol Daniluk

Sadece şunu ekleyeceğim: Eğer kullanırsanız splitmuhtemelen yapmanız gerekir, drop=Taksi takdirde faktörler için orijinal seviyeleriniz düşmez
Denis

51

Eureka!

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

1
Nasıl kullanılacağını göstermek ister misiniz?
Roman Luštrik

3
unlist(apply(xy.df, 1, list), recursive = FALSE). Bununla birlikte, flodel'in çözümü applyveya kullanmaktan daha etkilidir t.
Arun

11
Buradaki sorun olduğunu tdönüştürür data.famea matrixOP istendiği gibi öylesine Listenizde elementler, liste atom vektörler olmadığını. xy.dfKarma türleri
içerene

2
Değerler üzerinde döngü yapmak istiyorsanız, tavsiye etmiyorum apply. Aslında sadece R'de uygulanan bir for döngüsü lapply, C de döngüyü gerçekleştirir, bu önemli ölçüde daha hızlıdır. Bu satır listesi biçimi, çok fazla döngü yapıyorsanız, aslında tercih edilir.
Liz Sander

1
Gelecekten başka bir yorum ekleyerek, bir applysürüm.mapply(data.frame, xy.df, NULL)
alexis_laz

15

Data.frame'i tamamen kötüye kullanmak istiyorsanız (benim yaptığım gibi) ve $ işlevselliğini korumak istiyorsanız, bunun bir yolu data.frame'i tek satırlık data.frame'lere bölmektir.

> 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

Bu sadece entelektüel mastürbasyon değildir, aynı zamanda data.frame'in satırlarının bir listesine 'dönüştürülmesine' izin verir ve lapply ile daha fazla kullanım için yararlı olabilecek $ endekslemesini korur (lapply için geçtiğiniz işlevin bu $ endekslemesini kullandığı varsayılırsa)


Onları tekrar nasıl bir araya getireceğiz? Listeleri data.frametek bir e çevirmek data.framemi?
Aaron McDaid

4
@AaronMcDaid do.call ve rbind'i kullanabilirsiniz: df == do.call ("rbind", ldf)
random_forest_fanatic

@AaronMcDaid Veya data.table :: rbindlist (). Orijinal veri çerçeveniz büyükse, hız kazanımları önemli olacaktır.
Empiromancer

8

Daha modern bir çözüm yalnızca purrr::transposeşunları kullanır :

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

Bugün bunun üzerinde milyonlarca gözlem ve 35 sütun içeren bir data.frame (gerçekten data.table) için çalışıyordum. Amacım, her biri tek bir satıra sahip olan data.frames (data.tables) listesini döndürmekti. Yani, her satırı ayrı bir data.frame'e bölmek ve bunları bir listede saklamak istedim.

İşte split(dat, seq_len(nrow(dat)))bu veri kümesinden kabaca 3 kat daha hızlı olan bulduğum iki yöntem . Aşağıda, 7500 satır, 5 sütunlu veri kümesinde üç yöntemi karşılaştırıyorum ( iris 50 kez tekrarlandı).

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
) 

Bu geri dönüyor

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

Farklar önceki setDFtestimdeki kadar büyük olmasa da, düz yöntem, maks (setDF) <min (bölme) ile çalıştırmaların dağıtımının tüm seviyelerinde önemli ölçüde daha hızlıdır ve attryöntem tipik olarak iki katından daha hızlıdır.

Dördüncü yöntem, basit bir iç içe geçmiş lapplyolan ve iç içe geçmiş bir liste döndüren ekstrem şampiyondur . Bu yöntem, bir listeden bir data.frame oluşturmanın maliyetini örnekler. Dahası, data.framefonksiyonla denediğim tüm yöntemler , data.tabletekniklerden kabaca bir kat daha yavaştı .

veri

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

6

purrr(0.2.2) paketinin güncel bir sürümü en hızlı çözüm gibi görünüyor :

by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out

En ilginç çözümleri karşılaştıralım:

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
)

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

Aynı sonucu şununla da alabiliriz 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;
}

Şimdi şununla karşılaştır purrr:

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

Sonuçlar:

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 satırlık küçük bir veri kümesi üzerinde kıyaslama yapmak pek mantıklı değil çünkü kimse mikrosaniye
cinsinden

4
by_row()şimdi taşındılibrary(purrrlyr)
MrHopko

Ve purrrlyr'de olmanın yanı sıra, kullanımdan kaldırılmak üzere. Aynı sonucu elde etmek için tidyr :: nest, dplyr :: mutate purrr :: map'i birleştiren başka yöntemler de var
Mike Stanley

3

Birkaç seçenek daha:

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

ile splitverow

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

veri

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

2

Benim için en iyi yol şuydu:

Örnek veriler:

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

BBmiscKütüphaneye diyoruz

library(BBmisc)

data$lists<-convertRowsToList(data[,2:4])

Ve sonuç şöyle olacak:

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

Alternatif bir yol, df'yi bir matrise dönüştürmek ve ardından listeyi uygula lappyişlevini uygulamaktır :ldf <- lapply(as.matrix(myDF), function(x)x)


1

Kullanan başka bir alternatif library(purrr)(bu, büyük data.frames üzerinde biraz daha hızlı görünüyor)

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

3
`by_row ()` şimdi `` library (purrrlyr) '' klasörüne taşındı
MrHopko

1

@Flodel'in yazdığı gibi: Bu, veri çerçevenizi, veri çerçevesindeki satır sayısıyla aynı sayıda öğeye sahip bir listeye dönüştürür:

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

Ek olarak, listenin her öğesinde yalnızca NA olmayan sütunları seçmek için bir işlev ekleyebilirsiniz :

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

0

by_rowDan fonksiyon purrrlyrpaketinin sizin için yapacaktır.

Bu örnek gösteriyor

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

Varsayılan olarak, döndürülen değer, çağrılan df'deki myfnyeni bir liste sütununa yerleştirilir .out. $.outYukarıdaki ifadenin sonunda hemen listelerin bir listesini dönen, bu sütunu seçer.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.