studio reales proyectos programacion libro introducción incluye herramientas fundamentos fuente español código con avanzado aplicaciones list r dataframe

reales - ¿Cuál es la forma más eficiente de lanzar una lista como un marco de datos?



libro de android studio en español pdf (7)

Muy a menudo quiero convertir una lista en la que cada índice tiene tipos de elementos idénticos a un marco de datos. Por ejemplo, puedo tener una lista:

> my.list [[1]] [[1]]$global_stdev_ppb [1] 24267673 [[1]]$range [1] 0.03114799 [[1]]$tok [1] "hello" [[1]]$global_freq_ppb [1] 211592.6 [[2]] [[2]]$global_stdev_ppb [1] 11561448 [[2]]$range [1] 0.08870838 [[2]]$tok [1] "world" [[2]]$global_freq_ppb [1] 1002043

Quiero convertir esta lista en un marco de datos donde cada elemento de índice es una columna. Lo más natural (para mí) a ir es usar do.call :

> my.matrix<-do.call("rbind", my.list) > my.matrix global_stdev_ppb range tok global_freq_ppb [1,] 24267673 0.03114799 "hello" 211592.6 [2,] 11561448 0.08870838 "world" 1002043

Muy sencillo, pero cuando intento convertir esta matriz como un marco de datos, las columnas siguen siendo elementos de lista, en lugar de vectores:

> my.df<-as.data.frame(my.matrix, stringsAsFactors=FALSE) > my.df[,1] [[1]] [1] 24267673 [[2]] [1] 11561448

Actualmente, para obtener el marco de datos moldeado correctamente, estoy iterando sobre cada columna usando unlist y as.vector , y luego as.vector estructurar el marco de datos como tal:

new.list<-lapply(1:ncol(my.matrix), function(x) as.vector(unlist(my.matrix[,x]))) my.df<-as.data.frame(do.call(cbind, new.list), stringsAsFactors=FALSE)

Esto, sin embargo, parece muy ineficiente. ¿Hay alguna forma mejor de hacer esto?


Aunque esta pregunta se ha respondido hace tiempo, vale la pena señalar que el paquete rbindlist tiene rbindlist que realiza esta tarea muy rápidamente:

library(microbenchmark) library(data.table) l <- replicate(1E4, list(a=runif(1), b=runif(1), c=runif(1)), simplify=FALSE) microbenchmark( times=5, R=as.data.frame(Map(f(l), names(l[[1]]))), dt=data.frame(rbindlist(l)) )

me da

Unit: milliseconds expr min lq median uq max neval R 31.060119 31.403943 32.278537 32.370004 33.932700 5 dt 2.271059 2.273157 2.600976 2.635001 2.729421 5


Creo que quieres:

> do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE)) global_stdev_ppb range tok global_freq_ppb 1 24267673 0.03114799 hello 211592.6 2 11561448 0.08870838 world 1002043.0 > str(do.call(rbind, lapply(my.list, data.frame, stringsAsFactors=FALSE))) ''data.frame'': 2 obs. of 4 variables: $ global_stdev_ppb: num 24267673 11561448 $ range : num 0.0311 0.0887 $ tok : chr "hello" "world" $ global_freq_ppb : num 211593 1002043


El bind_rows del paquete bind_rows es eficiente.

one <- mtcars[1:4, ] two <- mtcars[11:14, ] system.time(dplyr::bind_rows(one, two)) user system elapsed 0.001 0.000 0.001


Esta

f = function(x) function(i) sapply(x, `[[`, i)

es una función que devuelve una función que extrae el elemento i de la x. Asi que

Map(f(mylist), names(mylist[[1]]))

obtiene una lista de vectores nombrados (gracias Mapa!) que se pueden convertir en un marco de datos

as.data.frame(Map(f(mylist), names(mylist[[1]])))

Para velocidad, generalmente es más rápido usar unlist(lapply(...), use.names=FALSE) como

f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE)

Una variante más general es

f = function(X, FUN) function(...) sapply(X, FUN, ...)

¿Cuándo aparecen las estructuras de la lista de listas? ¿Tal vez hay un paso anterior donde una iteración podría ser reemplazada por algo más vectorizado?


No estoy seguro de dónde se clasifican en cuanto a la eficiencia, pero dependiendo de la estructura de sus listas hay algunas opciones tidyverse . Una ventaja es que funcionan muy bien con listas de tallas desiguales:

l <- list(a = list(var.1 = 1, var.2 = 2, var.3 = 3) , b = list(var.1 = 4, var.2 = 5) , c = list(var.1 = 7, var.3 = 9) , d = list(var.1 = 10, var.2 = 11, var.3 = NA)) df <- dplyr::bind_rows(l) df <- purrr::map_df(l, dplyr::bind_rows) df <- purrr::map_df(l, ~.x) # all create the same data frame: # A tibble: 4 x 3 var.1 var.2 var.3 <dbl> <dbl> <dbl> 1 1 2 3 2 4 5 NA 3 7 NA 9 4 10 11 NA

Y también puedes mezclar vectores y marcos de datos:

library(dplyr) bind_rows( list(a = 1, b = 2), data_frame(a = 3:4, b = 5:6), c(a = 7) ) # A tibble: 4 x 2 a b <dbl> <dbl> 1 1 2 2 3 5 3 4 6 4 7 NA


No puedo decirles que este es el "más eficiente" en términos de memoria o velocidad, pero es bastante eficiente en términos de codificación:

my.df <- do.call("rbind", lapply(my.list, data.frame))

el paso lapply () con data.frame () convierte cada elemento de la lista en un solo marco de datos de fila que luego actúa bien con rbind ()


Otra opción es:

data.frame(t(sapply(mylist, `[`)))

pero esta simple manipulación da como resultado un marco de datos de listas:

> str(data.frame(t(sapply(mylist, `[`)))) ''data.frame'': 2 obs. of 3 variables: $ a:List of 2 ..$ : num 1 ..$ : num 2 $ b:List of 2 ..$ : num 2 ..$ : num 3 $ c:List of 2 ..$ : chr "a" ..$ : chr "b"

Una alternativa a esto, en la misma línea, pero ahora el resultado es el mismo que las otras soluciones, es:

data.frame(lapply(data.frame(t(sapply(mylist, `[`))), unlist))

[ Editar: tiempos incluidos de las dos soluciones de @Martin Morgan, que tienen la ventaja sobre la otra solución que devuelve un marco de datos de vectores.] Algunos tiempos representativos en un problema muy simple:

mylist <- list(list(a = 1, b = 2, c = "a"), list(a = 2, b = 3, c = "b")) > ## @Joshua Ulrich''s solution: > system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame, + stringsAsFactors=FALSE)))) user system elapsed 1.740 0.001 1.750 > ## @JD Long''s solution: > system.time(replicate(1000, do.call(rbind, lapply(mylist, data.frame)))) user system elapsed 2.308 0.002 2.339 > ## my sapply solution No.1: > system.time(replicate(1000, data.frame(t(sapply(mylist, `[`))))) user system elapsed 0.296 0.000 0.301 > ## my sapply solution No.2: > system.time(replicate(1000, data.frame(lapply(data.frame(t(sapply(mylist, `[`))), + unlist)))) user system elapsed 1.067 0.001 1.091 > ## @Martin Morgan''s Map() sapply() solution: > f = function(x) function(i) sapply(x, `[[`, i) > system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) user system elapsed 0.775 0.000 0.778 > ## @Martin Morgan''s Map() lapply() unlist() solution: > f = function(x) function(i) unlist(lapply(x, `[[`, i), use.names=FALSE) > system.time(replicate(1000, as.data.frame(Map(f(mylist), names(mylist[[1]]))))) user system elapsed 0.653 0.000 0.658