данные.фрейм строк в список


У меня есть данные.фрейм, который я хотел бы преобразовать в список по строкам, то есть каждая строка будет соответствовать своим собственным элементам списка. Другими словами, Мне бы хотелось, чтобы список был таким же длинным, как и данные.рамка имеет ряды.

До сих пор я рассматривал эту проблему следующим образом, но мне было интересно, есть ли лучший способ подойти к этому.
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   82  

11 ответов:

Вот так:

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)))

Если вы хотите полностью злоупотребить данными.фрейм (как и я) и хотел бы сохранить функциональность$, один из способов-разделить ваши данные.кадр в однострочные данные.кадры собраны в список:

> 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
Это не только интеллектуальная мастурбация, но и позволяет "трансформировать" данные.кадр в список его строк, сохраняя $ индексацию , которая может быть полезна для дальнейшего использования с lapply (предполагая, что функция, которую вы передаете lapply, использует эту $ индексацию)

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

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

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

Данные

dat <- vector("list", 50)
for(i in 1:50) dat[[i]] <- iris
dat <- setDF(rbindlist(dat))

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

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

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

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

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) 

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

Функция 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

По умолчанию возвращаемое значение из myfn помещается в новый столбец списка в df под названием .out. $.out в конце приведенного выше утверждения немедленно выбирает этот столбец, возвращая список списков.

Более современное решение использует только 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

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

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

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

NewList2 <- lapply(NewList, function(x) x[,!is.na(x)])