Map_dfc complains of data frame when passed a number

I ran into a weird problem/error message when using map_dfc that I cannot understand. The following is a very contrived example, but it illustrates the problem I ran into (the actually functions used are, of course, endlessly more interesting and are part of a bootstrapping exercise, but that is not important).

# Weird error message
library(tidyverse)
#> Registered S3 method overwritten by 'rvest':
#>   method            from
#>   read_xml.response xml2
car_totals <- function(df) {
  df %>% 
  summarise(
    total = sum(mpg),
    average = mean(mpg)
  ) 
}

my_fun <- function(x) {
  if (x < 30) {
    0
  } else {
    x * 2
  }
}

my_fun(0)
#> [1] 0

my_fun(30)
#> [1] 60

map_dfc(c("total", "average"), ~ {
  res <- car_totals(mtcars)
  x1 <- res[.x]
  my_fun(x1)
})
#> Error: Argument 1 can't be a list containing data frames

Created on 2020-03-24 by the reprex package (v0.2.1)

Despite the error message that the list cannot contain data frames, the problem turned out to be that my_fun return 0. If I change the function to a calculation that ends up giving zero, the code runs fine.

# No error message
library(tidyverse)
#> Registered S3 method overwritten by 'rvest':
#>   method            from
#>   read_xml.response xml2
car_totals <- function(df) {
  df %>% 
    summarise(
      total = sum(mpg),
      average = mean(mpg)
    ) 
}

my_fun <- function(x) {
  if (x < 30) {
    round(x/100)
  } else {
    x * 2
  }
}

my_fun(0)
#> [1] 0

my_fun(30)
#> [1] 60

map_dfc(c("total", "average"), ~ {
  res <- car_totals(mtcars)
  x1 <- res[.x]
  my_fun(x1)
})
#>    total average
#> 1 1285.8       0

Created on 2020-03-24 by the reprex package (v0.2.1)

I am probably missing something obvious here, but why does map_dfc not like numbers?

Chasing down the error with debug(), it looks like the issue is that bind_cols() (called by map_dfc()) has a homogeneity constraint -- the objects to be bound have to be of the same type. So two vectors are OK, or two data frames are OK, but a mixture raises the error.

The function that causes (not raises) the error is actually dplyr:::flatten_bindable(), which produces the offending object: a list of one element, which is itself a list of two elements, neither of which is allowed to be a data frame. The first of these two is the 'Argument 1' of the error message.

changing the res[.x] to res[[.x]] is the easiest way to eliminate the error I believe.

This is also possible:

map_dfc(c("total", "average"), ~ {
  res <- car_totals(mtcars)
  x1 <- res[[.x]]
  rlist<- list()
  rlist[[.x]] = my_fun(x1)
  rlist
})

That seems like the way to go :slight_smile: Everything seems built to work smoothly with lists, so names are preserved, too.