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,]
}
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))
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)))
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$)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
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))
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))
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)
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)
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.
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
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)])
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