Tengo una lista que contiene múltiples data.frames. Quiero seleccionar cada enésimo data.frame de la lista y combinarlos en un solo data.frame que se pueda escribir en un csv.

Aquí hay un ejemplo de la estructura de la lista:

one.title <- data.frame(id = '1a', title = 'first title')

one.author <- data.frame(first_name = c('Susan', 'Alice'),
                     last_name  = c('Smith', 'Johnson') )

second.title <- data.frame(id = '2b', title = 'second_title')

second.author <- data.frame(first_name = c('Sarah', 'Mary'),
                        last_name  = c('Davis', 'Proctor') )

one.list <- list()

one.list[[1]]$title <- one.title
one.list[[1]]$author <- one.author
one.list[[2]]$title <- second.title
one.list[[2]]$author <- second.author

Aquí está mi solución actual que produce un único marco de datos para los campos de 'autores':

build_author_table <- function(result.l){

  list_to_df <- function(i){

  x <- result.l[[i]]$author

  return(x)
}


authors_df_l <-(lapply(1:length(result.l), FUN = list_to_df))

authors_df <- do.call("rbind", lapply(authors_df_l, as.data.frame))

return(authors_df)
}

Esto produce la salida que quiero:

    first_name last_name
1      Susan     Smith
2      Alice   Johnson
3      Sarah     Davis
4       Mary   Proctor

Pero, como probablemente pueda imaginar, cuando se escala a miles de registros con campos de texto mucho más grandes en el marco de datos, es muy lento.

¿Alguien puede sugerir una forma más rápida y eficiente de producir el data.frame final?

3
Matt 29 may. 2020 a las 17:19

3 respuestas

La mejor respuesta

Aquí hay una mejor solución (benchmarked):

data.table::rbindlist(lapply(one.list, "[[", "author"))

La solución de ronroneo es bonita, pero no tan rápida. Resultados de referencia:

microbenchmark(build_author_table(one.list),
    data.table::rbindlist(lapply(one.list, "[[", "author")),
    map_dfr(one.list, "author"))
Unit: microseconds
                                                    expr     min       lq      mean   median       uq        max neval cld
                            build_author_table(one.list) 170.693 190.9460  239.2987 206.4505 272.3815    494.477   100   a
 data.table::rbindlist(lapply(one.list, "[[", "author"))  69.562  88.5590  270.4926  99.1750 152.6735  15068.116   100   a
                             map_dfr(one.list, "author") 214.832 245.2825 2374.5980 281.3210 340.1270 206562.846   100   a
1
csgroen 29 may. 2020 a las 14:35

Prueba esto:



one.title <- data.frame(id = '1a', title = 'first title')

one.author <- data.frame(first_name = c('Susan', 'Alice'),
                         last_name  = c('Smith', 'Johnson') )

second.title <- data.frame(id = '2b', title = 'second_title')

second.author <- data.frame(first_name = c('Sarah', 'Mary'),
                            last_name  = c('Davis', 'Proctor') )

one.list <- list(
  list(title = one.title, author =  one.author),
  list(title = second.title, author =  second.author)
)



authors_df_l = lapply(one.list, function(item) item$author)

do.call("rbind",authors_df_l)
0
Дмитрий Пасько 29 may. 2020 a las 14:31

Su código de construcción no funcionó, pero construí uno que creo que se asemeja a lo que está disparando.

List of 2
 $ :List of 2
  ..$ title :'data.frame':  1 obs. of  2 variables:
  .. ..$ id   : Factor w/ 1 level "1a": 1
  .. ..$ title: Factor w/ 1 level "first title": 1
  ..$ author:'data.frame':  2 obs. of  2 variables:
  .. ..$ first_name: Factor w/ 2 levels "Alice","Susan": 2 1
  .. ..$ last_name : Factor w/ 2 levels "Johnson","Smith": 2 1
 $ :List of 2
  ..$ title :'data.frame':  1 obs. of  2 variables:
  .. ..$ id   : Factor w/ 1 level "2b": 1
  .. ..$ title: Factor w/ 1 level "second_title": 1
  ..$ author:'data.frame':  2 obs. of  2 variables:
  .. ..$ first_name: Factor w/ 2 levels "Mary","Sarah": 2 1
  .. ..$ last_name : Factor w/ 2 levels "Davis","Proctor": 1 2

Si esto es lo que estabas pensando, esto funciona espléndidamente, obtienes una advertencia porque las cadenas de caracteres son factores. Estos pueden ser ignorados, o cuando construya el marco de datos inicial use stringAsFactors = F como argumento

library(purrr) 
map_dfr(one.list, "author")
2
NotThatKindODr 29 may. 2020 a las 14:30