data.wiersze ramek do listy

Mam dane.ramka, którą chciałbym przekonwertować na listę według wierszy, co oznacza, że każdy wiersz będzie odpowiadał swoim własnym elementom listy. Innymi słowy, chciałbym listę, która jest tak długa, jak dane.ramka ma rzędy.

Do tej pory rozwiązałem ten problem w następujący sposób, ale zastanawiałem się, czy jest lepszy sposób, aby podejść do tego.

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,]
}
Author: Roman Luštrik, 2010-08-16

11 answers

Tak:

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

I jeśli chcesz, aby nazwy wierszy xy.df były nazwami listy wyjściowej, możesz zrobić:

xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))
 108
Author: flodel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-03 12:02:13

Eureka!

xy.list <- as.list(as.data.frame(t(xy.df)))
 42
Author: Roman Luštrik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 11:22:13

Jeśli chcesz całkowicie nadużywać danych.ramka (tak jak ja) i jak zachować funkcjonalność$, jednym ze sposobów jest podzielenie danych.ramka do danych w jednym wierszu.ramki zebrane na liście:

> 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
Jest to nie tylko intelektualna Masturbacja, ale pozwala na "transformację" danych.ramka do listy linii, zachowując indeksację$, która może być przydatna do dalszego użycia z lapply (zakładając, że funkcja, którą przekazujesz lapply używa tej indeksacji$)
 12
Author: Qiou Bi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-01-16 15:42:37

Wydaje się, że aktualna wersja pakietu purrr (0.2.2) jest najszybszym rozwiązaniem:

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

Porównajmy najciekawsze rozwiązania:

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

Również możemy uzyskać ten sam wynik z 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;
}

Teraz porównaj z purrr:

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

Wyniki:

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
Author: Artem Klevtsov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-08 15:21:21

Pracowałem dziś nad tym dla danych.ramka (właśc.tabela) z milionami obserwacji i 35 kolumnami. Moim celem było zwrócenie listy danych.ramki (dane.tabele) każdy z jednym wierszem. Oznacza to, że chciałem podzielić każdy wiersz na osobne dane.ramka i zapisać je na liście.

Oto dwie metody, które wymyśliłem, które były około 3 razy szybsze niż split(dat, seq_len(nrow(dat))) dla tego zestawu danych. Poniżej porównuję trzy metody na zestawie danych 7500 wierszy, 5 kolumn (iris powtórzony 50 razy).

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
) 

To zwraca

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

Podczas gdy różnice nie są tak duże jak w poprzednim teście, metoda prosta setDF jest znacznie szybsza na wszystkich poziomach rozkładu biegów z max (setDF) attr jest zazwyczaj ponad dwa razy szybsza.

Czwarta metoda to extreme champion, który jest prostym zagnieżdżonym lapply, zwracającym zagnieżdżoną listę. Metoda ta jest przykładem kosztu konstruowania danych.ramka z lista. Co więcej, wszystkie metody, które wypróbowałem z funkcją data.frame były mniej więcej o rząd wielkości wolniejsze niż techniki data.table.

Dane

dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))
 4
Author: lmo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-09 10:51:15

Inna alternatywa przy użyciu library(purrr) (wydaje się być nieco szybsza na dużych danych.ramki)

flatten(by_row(xy.df, ..f = function(x) flatten_chr(x), .labels = FALSE))
 2
Author: MrHopko,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-28 18:46:43

Najlepszym sposobem dla mnie było:

Przykładowe dane:

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

Nazywamy BBmisc biblioteką

library(BBmisc)

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

I wynik będzie:

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
Author: Cro-Magnon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-04 22:27:36

Alternatywnym sposobem jest konwersja DF na macierz, a następnie zastosowanie nad nią funkcji apply lappy: ldf <- lapply(as.matrix(myDF), function(x)x)

 1
Author: user3553260,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-18 18:09:50

Funkcja by_row z pakietu purrrlyr zrobi to za Ciebie.

Ten przykład pokazuje

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

Domyślnie zwracana wartość z myfn jest umieszczana w nowej kolumnie listy W df o nazwie .out. $.out na końcu powyższej instrukcji natychmiast zaznacza tę kolumnę, zwracając listę list.

 0
Author: RobinL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-03 19:22:23

Bardziej nowoczesne rozwiązanie wykorzystuje tylko 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
 0
Author: Mike Stanley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-24 18:23:03

Jak napisał @ flodel: To konwertuje Twój dataframe do listy, która ma taką samą liczbę elementów jak liczba wierszy w dataframe:

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

Możesz dodatkowo dodać funkcję do Wybierz tylko te kolumny, które nie są na w każdym elemencie listy:

NewList2 <- lapply(NewList, function(x) x[,!is.na(x)])
 0
Author: michal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-26 11:55:55