6

So I have a list with me as below, what I want is to split them into three separate dataframes (with names as Banana/Strawberry & apple) as shown in expected output. I have already seen this (Splitting List into dataframe R) but its exact opposite of what I want. I dont want to combine then I want to split them into three dataframe with same name as list header.

list_a <- list(`Banana` = c(8.7), `Strawberry` = c(2.3), `Apple` = c(3.5))

DF1

Banana
8.7

DF2

Strawberry
2.3

DF3

Apple
3.5

Any Solution preferably in Tidyverse would be greatly appreciated. Actual problem has lot more columns in the list.

Vaibhav Singh
  • 1,159
  • 1
  • 10
  • 25

4 Answers4

9

First convert them all to a tibble:

list_a <- list(`Banana` = c(8.7), `Strawberry` = c(2.3), `Apple` = c(3.5))
list_a <- purrr::map(list_a, tibble::as_tibble)

Then send this to the global environment:

list2env(list_a, envir = .GlobalEnv)
al-obrien
  • 1,353
  • 11
  • 29
  • This is amazing, Thanks. I have just one problem I think it arises because header of my lists are actually [[1]], [[2]] & [[3]]. So I when run your code last line it gives me below error any help there ?Error in list2env(., envir = .GlobalEnv) : names(x) must be a character vector of the same length as x – Vaibhav Singh Dec 04 '19 at 05:57
  • 1
    Ronak's answer takes care of this issue I mentioned above – Vaibhav Singh Dec 04 '19 at 05:59
  • Yes if your data doesn't have a named list, you will need to set the names as done in other answers. – al-obrien Dec 04 '19 at 06:01
5

We can use imap to get the names and then use set_names

library(purrr)
library(dplyr)
library(stringr)
imap(list_a, ~ set_names(tibble(.x), .y)) %>%
        set_names(str_c("DF", 1:3)) %>% 
        list2env(.GlobalEnv)

DF1
# A tibble: 1 x 1
#  Banana
#   <dbl>
#1    8.7
DF2
# A tibble: 1 x 1
#  Strawberry
#       <dbl>
#1        2.3
DF3
# A tibble: 1 x 1
#  Apple
#  <dbl>
#1   3.5

If we need separate columns

library(tibble)
enframe(list_a) %>% 
     unnest(c(value)) %>% 
     group_split(rn = row_number(), keep = FALSE) %>%
     set_names(str_c("DF", 1:3)) %>% 
     list2env(.GlobalEnv)
DF1
# A tibble: 1 x 2
#  name   value
#  <chr>  <dbl>
#1 Banana   8.7
DF2
# A tibble: 1 x 2
#  name       value
#  <chr>      <dbl>
#1 Strawberry   2.3
DF3
# A tibble: 1 x 2
#  name  value
#  <chr> <dbl>
#1 Apple   3.5
akrun
  • 874,273
  • 37
  • 540
  • 662
  • Akrun, if we have to get a separate column in each dataframe stating names of the dataframe could that be done too. For ex: Banana df headers would look like Type & value with first row being Banana & 8.7 – Vaibhav Singh Dec 04 '19 at 06:42
  • Hi sir, when I run the first solution, I get this `Error in set_names(): ! The size of nm (3) must be compatible with the size of x (1).` I will say though that my list has different number of tibbles and different number of rows and columns in each tibble than in the `OP` question. I did not change anything in the code other than providing my list name. – Ed_Gravy Sep 22 '22 at 16:26
  • 1
    @Ed_Gravy THE `1:3` assumes that the length to be 3. May be you need `seq_along(.)` – akrun Sep 22 '22 at 16:28
  • @akrun I've reproduced your code, but the 2 new dfs wouldn't keep the column names. Instead 1, 2, 3, 4... is there any modification to keep the column names (the column names are exactly equal for the 2 split dfs) – Javier Hernando Oct 19 '22 at 10:08
  • @JavierHernando probably you don't have a named list – akrun Oct 19 '22 at 14:53
3

A tidyverse way would be

library(tidyverse)

new_list <- set_names(map2(list_a,names(list_a),  
                    ~tibble(!!.y := .x)), str_c("df", 1:3))

and it can be done in base R as well

new_list <- setNames(Map(function(x, y) setNames(data.frame(x), y), 
                    list_a,names(list_a)), paste0("df", 1:3))

Now we can write it into global environment.

list2env(new_list, .GlobalEnv)
Ronak Shah
  • 377,200
  • 20
  • 156
  • 213
  • 1
    This is just genius. I am fan of you Ronak. It took care of naming list as well. Clever ! An issue which I faced below. IDK if this a stupid question but really appreciate prompt response :) – Vaibhav Singh Dec 04 '19 at 05:59
  • @VaibhavSingh yes, to use `list2env` you need a named list. From `?list2env`, `x - a list, where names(x) must not contain empty ("") elements.`. So you need to name the list before using it. – Ronak Shah Dec 04 '19 at 06:03
2

Less straightforward than previous answers but you can get it using a for loop:

for(i in 1:length(list_a))
{
  df <- data.frame(unlist(list_a[[i]]))
  colnames(df) <- names(list_a[i])
  assign(names(list_a[i]),df, .GlobalEnv)
}  
dc37
  • 15,840
  • 4
  • 15
  • 32