1

So essentially I have a list of dataframes that I want to apply as.character() to. To obtain the list of dataframes I have a list of files that I read in using a map() function and a read funtion that I created. I can't use map_df() because there are columns that are being read in as different data types. All of the files are the same and I know that I could hard code the data types in the read function if I wanted, but I want to avoid that if I can.

At this point I throw the list of dataframes in a for loop and apply another map() function to apply the as.character() function. This final list of dataframes is then compressed using bind_rows().

All in all, this seems like an extremely convoluted process, see code below.


audits <- list.files()


my_reader <- function(x) {
  
 my_file <- read_xlsx(x)
  
  
}


audits <- map(audits, my_reader)

for (i in 1:length(audits)) {
  
  audits[[i]] <- map_df(audits[[i]], as.character)
  
  
}

audits <- bind_rows(audits)


Does anybody have any ideas on how I can improve this? Ideally to the point where I can do everything in a single vectorised map() function?

For reproducibility you can use two iris datasets with one of the columns datatypes changed.


iris2 <- iris

iris2[1] <- as.character(iris2[1])

my_list <- list(iris, iris2)

Hansel Palencia
  • 1,006
  • 9
  • 17
  • 3
    Do you need `map(my_list, ~ .x %>% mutate(across(everything(), as.character)))` – akrun Mar 21 '22 at 16:11
  • Sidenote, do you know where I can go to read up a bit more on the way that the map/apply families use the ~. Probably a tidyverse thing to be fair? – Hansel Palencia Mar 21 '22 at 16:16
  • 1
    I would first check the vignettes of tidyverse which would be more uptodate – akrun Mar 21 '22 at 16:17
  • 1
    @HanselPalencia `~` and `.x` are really just replacing the `x` in `function(x)`. So, you could also write it as `map(my_list, function(x) x %>% mutate(across(everything(), as.character)))` – AndrewGB Mar 21 '22 at 16:19
  • @HanselPalencia You will probably also see this shorthand as well: `map(my_list, \(x) x %>% mutate(across(everything(), as.character)))`. Here, the \ just replaces the word `function`. – AndrewGB Mar 21 '22 at 16:29

2 Answers2

2

as.character works on vector whereas data.frame is a list of vectors. An option is to use across if we want only a single use of map

library(dplyr)
library(purrr)
map_dfr(my_list, ~ .x %>%
        mutate(across(everything(), as.character)))
akrun
  • 874,273
  • 37
  • 540
  • 662
2

I wanted to show a base R solution just incase if it helps anyone else. You can use rapply to recursively go through the list and apply a function. you can specify class and if you want to replace or unlist/list the returned object:

iris2 <- iris

iris2[1] <- as.character(iris2[1])

my_list <- list(iris, iris2)


mylist2 <- rapply(my_list, class = "ANY", f = as.character, how = "replace")
bigdf <- do.call(rbind, mylist2)
Mike
  • 3,797
  • 1
  • 11
  • 30