2010-08-16 11 views
68

Tengo un data.frame que me gustaría convertir a una lista por filas, lo que significa que cada fila correspondería a sus propios elementos de lista. En otras palabras, me gustaría una lista que sea tan larga como data.frame tenga filas.data.frame filas a una lista

Hasta ahora, he abordado este problema de la siguiente manera, pero me preguntaba si hay una mejor manera de abordar esto.

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

Respuesta

87

Te gusta esto:

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

Y si desea que los rownames de xy.df a ser los nombres de la lista de salida, que puede hacer:

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

Eureka!

xy.list <- as.list(as.data.frame(t(xy.df))) 
+0

Batirme ;-). Aún así, si solo desea recorrer estos valores, mejor use apply. – mbq

+1

¿Desea demostrar cómo usar apply? –

+2

'unlist (apply (xy.df, 1, list), recursive = FALSE)'. Sin embargo, la solución de Flodel es más eficiente que usar 'apply' o' t'. – Arun

11

Si desea abusar por completo el hoja.de.datos (como yo) y como para mantener la funcionalidad $, es una manera de dividir que hoja.de.datos en una línea data.frames se reunieron en una lista :

> 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 

no es sólo la masturbación intelectual, pero permite 'transformar' el hoja.de.datos en una lista de sus líneas, manteniendo la indexación $ que puede ser útil para su posterior uso con lapply (asumiendo la función que pasar a lapply usa esta $ indexación)

+0

¿Cómo los podemos volver a armar? Convierta una lista de 'data.frame's en un' 'data.frame'' –

+3

@AaronMcDaid Puede usar do.call y rbind: df == do.call ("rbind", ldf) –

+0

@AaronMcDaid O data.table :: rbindlist(). Si su marco de datos original era grande, las ganancias de velocidad serán significativas. – Empiromancer

1

Una forma alternativa es convertir el df para una matriz aplicando a continuación la lista aplicar lappy función sobre ella:

2

Otra alternativa usando library(purrr) (que parece ser un poco más rápido en grandes data.frames)

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

'by_row()' ahora se ha movido a 'library (purrrlyr)' – MrHopko

5

Parece una versión actual del paquete purrr (0.2.2) es la solución más rápida:

by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out 

Vamos a comparar las soluciones más interesantes:

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 

También podemos obtener el mismo resultado con 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; 
} 

Ahora caompare con purrr:

benchmark(
    purrr = by_row(x, function(v) list(v)[[1L]], .collate = "list")$.out, 
    rcpp = df2list(x) 
) 

Resultados:

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 
+0

benchmarking en un pequeño conjunto de datos de 150 filas no tiene mucho sentido ya que nadie notará ninguna diferencia en microsegundos y no escala –

+0

Usted ' estas en lo correcto. Respuesta actualizada –

+2

'by_row()' ahora se ha movido a 'library (purrrlyr)' – MrHopko

0

La función by_row del paquete purrrlyr lo hará por ti.

Este ejemplo demuestra

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 

Por defecto, el valor devuelto por myfn se pone en una nueva list column en el df llama .out. El $.out al final de la declaración anterior selecciona inmediatamente esta columna y devuelve una lista de listas.

1

La mejor manera para mí fue:

datos Ejemplo:

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 

que llamamos el BBmisc biblioteca

library(BBmisc) 

data$lists<-convertRowsToList(data[,2:4]) 

Y el resultado será:

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

L ike @flodel escribió: Esto convierte la trama de datos en una lista que tiene el mismo número de elementos como el número de filas de trama de datos:

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

Puede agregar adicionalmente una función para seleccionar sólo aquellos columnas que no son NA en cada elemento de la lista:

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

Una solución más moderna utiliza solamente 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 
Cuestiones relacionadas