строки data.frame в список - PullRequest
       27

строки data.frame в список

102 голосов
/ 16 августа 2010

У меня есть 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,]
}

Ответы [ 11 ]

132 голосов
/ 17 января 2013

Как это:

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

И если вы хотите, чтобы имена строк xy.df были именами списка вывода, вы можете сделать:

xy.list <- setNames(split(xy.df, seq(nrow(xy.df))), rownames(xy.df))
46 голосов
/ 16 августа 2010

Эврика!

xy.list <- as.list(as.data.frame(t(xy.df)))
13 голосов
/ 16 января 2013

Если вы хотите полностью использовать 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 использует эту индексацию $)

7 голосов
/ 15 марта 2018

Я работал над этим сегодня для data.frame (на самом деле data.table) с миллионами наблюдений и 35 столбцами. Моя цель состояла в том, чтобы возвратить список data.frames (data.tables) каждый с одной строкой. То есть я хотел разделить каждую строку на отдельный data.frame и сохранить их в списке.

Вот два метода, которые я придумал, которые были примерно в 3 раза быстрее, чем split(dat, seq_len(nrow(dat))) для этого набора данных. Ниже я тестирую три метода в наборе данных из 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) 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 голосов
/ 26 марта 2017

Кажется, текущая версия пакета 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;
}

Теперь сопоставим с 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
3 голосов
/ 24 ноября 2017

Более современное решение использует только 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
2 голосов
/ 05 июня 2017

Лучший способ для меня был:

Пример данных:

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) 
2 голосов
/ 28 сентября 2016

Другая альтернатива, использующая library(purrr) (что кажется более быстрым на больших фреймах данных)

flatten(by_row(xy.df, ..f = function(x) flatten_chr(x), .labels = FALSE))
1 голос
/ 18 сентября 2016

Альтернативный способ - преобразовать df в матрицу, а затем применить к нему функцию списка lappy: ldf <- lapply(as.matrix(myDF), function(x)x)

0 голосов
/ 29 сентября 2017

Как @flodel писал: Это преобразует ваш фрейм данных в список, который имеет такое же количество элементов, что и количество строк в фрейме данных:

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

Вы можете дополнительно добавить функцию к , выбирая только те столбцы, которые не являются NA в каждом элементе списка:

NewList2 <- lapply(NewList, function(x) x[,!is.na(x)])
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...