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

답변:


164

이렇게 :

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
사용 방법을 설명하고 싶으십니까?
Roman Luštrik

3
unlist(apply(xy.df, 1, list), recursive = FALSE). 그러나 flodel의 솔루션을 사용하는 것보다 더 효율적입니다 apply또는 t.
Arun

11
문제는 여기에 있다는 것입니다 t변환 data.fameA와 matrix영업 이익이 요청한 때문에이 목록에있는 요소를,리스트 원자 벡터 아니라는 것을. 그것은 일반적 때까지 문제가되지 않습니다 xy.df... 혼합 유형이 포함
Calimo

2
값을 반복하려면을 권장하지 않습니다 apply. 실제로 R에서 구현 된 for 루프 일뿐 lapply입니다. C에서 루프를 수행하므로 훨씬 더 빠릅니다. 이 행 목록 형식은 루프를 많이 수행하는 경우 실제로 바람직합니다.
Liz Sander

1
미래에서 다른 의견을 추가 apply버전입니다.mapply(data.frame, xy.df, NULL)
alexis_laz

15

내가하는 것처럼 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에 전달하는 함수가이 $ 인덱싱을 사용한다고 가정).


다시 조립하려면 어떻게해야합니까? 의 목록을 data.frame단일로 바꾸 data.frame시겠습니까?
아론 McDaid

4
@AaronMcDaid do.call 및 rbind를 사용할 수 있습니다. df == do.call ( "rbind", ldf)
random_forest_fanatic

@AaronMcDaid 또는 data.table :: rbindlist (). 원래 데이터 프레임이 크면 속도 향상이 중요합니다.
Empiromancer

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)을 위해이 작업을했습니다. 내 목표는 각각 하나의 행으로 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))

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;
}

이제 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

150 행의 작은 데이터 세트에 벤치마킹 아무도 마이크로 어떤 차이를 느낄 것만큼 이해가되지 않습니다 그것은 확장되지 않습니다
데이비드 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)(대용량 데이터 프레임에서 조금 더 빠름)

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

3
`by_row ()`지금`라이브러리 (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

기본적으로에서 반환 된 값은 라는 df myfn의 새 목록 열에 저장 됩니다 .out. $.out상기 문장의 끝 바로리스트의 목록을 반환이 항목을 선택한다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.