행별로 목록으로 변환하려는 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,]
}
답변
이렇게 :
xy.list <- split(xy.df, seq(nrow(xy.df)))
행 xy.df
이름이 출력 목록의 이름이되도록하려면 다음을 수행 할 수 있습니다.
xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))
답변
유레카!
xy.list <- as.list(as.data.frame(t(xy.df)))
답변
내가하는 것처럼 data.frame을 완전히 남용하고 $ 기능을 유지하려면 한 가지 방법은 data.frame을 목록에 수집 된 한 줄의 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에 전달하는 함수가이 $ 인덱싱을 사용한다고 가정).
답변
보다 현대적인 솔루션은 다음 만 사용합니다 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
답변
저는 오늘 수백만 개의 관찰과 35 개의 열이있는 data.frame (실제로는 data.table)을 위해이 작업을했습니다. 내 목표는 각각 하나의 행으로 data.frames (data.tables) 목록을 반환하는 것이 었습니다. 즉, 각 행을 별도의 data.frame으로 분할하여 목록에 저장하고 싶었습니다.
여기 split(dat, seq_len(nrow(dat)))
에 해당 데이터 세트 보다 약 3 배 더 빠른 두 가지 방법이 있습니다. 아래에서는 7500 행, 5 열 데이터 세트 ( 홍채가 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
방법은 일반적으로 두 배 이상 빠릅니다.
네 번째 방법은 익스트림 챔피언 lapply
으로 중첩 된 목록을 반환하는 단순 중첩 입니다. 이 방법은 목록에서 data.frame을 구성하는 비용을 예시합니다. 게다가이 data.frame
기능으로 시도한 모든 방법 은 data.table
기술 보다 대략 몇 배 더 느 렸습니다 .
데이터
dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))
답변
현재 버전의 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;
}
이제 caompare 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
답변
몇 가지 추가 옵션 :
와 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
#....
로 split
와row
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))
