guyabel
guyabel

Reputation: 8366

Binding data frames from a list with different column types

Trying to figure out a way in purrr to bind rows over different elements of lists where the column types are not consistent. For example, my data looks a little like this...

d0 <- list(
  data_frame(x1 = c(1, 2), x2 = c("a", "b")),
  data_frame(x1 = c("P1"), x2 = c("c"))
)

d0
# [[1]]
# # A tibble: 2 x 2
#      x1    x2
#   <dbl> <chr>
# 1     1     a
# 2     2     b
# 
# [[2]]
# # A tibble: 1 x 2
#      x1    x2
#   <chr> <chr>
# 1    P1     c

I can use a for loop and then map_df with bind_rows to get the output I want (map_df will not work if the columns are of different types)...

for(i in 1:length(d0)){
  d0[[i]] <- mutate_if(d0[[i]], is.numeric, as.character)
}

map_df(d0, bind_rows)
# # A tibble: 3 x 2
#      x1    x2
#   <chr> <chr>
# 1     1     a
# 2     2     b
# 3    P1     c

but I think I am missing a trick somewhere that would allow me to avoid the for loop. My attempts along these lines...

d0 %>%
  map(mutate_if(., is.numeric, as.character)) %>%
  map_df(.,bind_rows)
# Error in UseMethod("tbl_vars") : 
#   no applicable method for 'tbl_vars' applied to an object of class "list"

... do not seem to work (still getting my head around purrr)

Upvotes: 2

Views: 2953

Answers (4)

moodymudskipper
moodymudskipper

Reputation: 47300

It's a good opportunity to use purrr::modify_depth :

library(purrr)
library(dplyr)
bind_rows(modify_depth(d0,2,as.character))

# # A tibble: 3 x 2
#      x1    x2
#   <chr> <chr>
# 1     1     a
# 2     2     b
# 3    P1     c

Upvotes: 0

zacdav
zacdav

Reputation: 4671

You can use rbindlist() from data.table in this case

data.table::rbindlist(d0) %>%
  dplyr::as_data_frame()

# A tibble: 3 x 2
  x1    x2   
  <chr> <chr>
1 1     a    
2 2     b    
3 P1    c

There may be circumstances where you will want to make sure the fill argument is TRUE

Documentation reference:

If column i of input items do not all have the same type; e.g, a data.table may be bound with a list or a column is factor while others are character types, they are coerced to the highest type (SEXPTYPE).

Upvotes: 4

akrun
akrun

Reputation: 887008

With tidyverse, the option would be

library(tidyverse)    
d0 %>%
     map_df(~ .x %>% 
              mutate_if(is.numeric, as.character))
# A tibble: 3 x 2
#  x1    x2   
#  <chr> <chr>
#1 1     a    
#2 2     b    
#3 P1    c    

Upvotes: 0

Prem
Prem

Reputation: 11955

How about this?

library(purrr)
map_df(lapply(d0, function(x) data.frame(lapply(x, as.character))), bind_rows)

Output is:

  x1 x2
1  1  a
2  2  b
3 P1  c

Sample data:

d0 <- list(structure(list(x1 = c(1, 2), x2 = c("a", "b")), .Names = c("x1", 
"x2"), row.names = c(NA, -2L), class = c("tbl_df", "tbl", "data.frame"
)), structure(list(x1 = "P1", x2 = "c"), .Names = c("x1", "x2"
), row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame"
)))

Upvotes: 2

Related Questions