What's wrong with my function to load multiple .csv files into single dataframe in R using rbind?

There's a lot of unnecessary code in your function. You can simplify it to:

load_data <- function(path) { 
  files <- dir(path, pattern = '\\.csv', full.names = TRUE)
  tables <- lapply(files, read.csv)
  do.call(rbind, tables)
}

pollutantmean <- load_data("specdata")

Be aware that do.call + rbind is relatively slow. You might find dplyr::bind_rows or data.table::rbindlist to be substantially faster.


To update Prof. Wickham's answer above with code from the more recent purrr library which he coauthored with Lionel Henry:

Tbl <-
    list.files(pattern="*.csv") %>% 
    map_df(~read_csv(.))

If the typecasting is being cheeky, you can force all the columns to be as characters with this.

Tbl <-
    list.files(pattern="*.csv") %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))

If you are wanting to dip into subdirectories to construct your list of files to eventually bind, then be sure to include the path name, as well as register the files with their full names in your list. This will allow the binding work to go on outside of the current directory. (Thinking of the full pathnames as operating like passports to allow movement back across directory 'borders'.)

Tbl <-
    list.files(path = "./subdirectory/",
               pattern="*.csv", 
               full.names = T) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c"))) 

As Prof. Wickham describes here (about halfway down):

map_df(x, f) is effectively the same as do.call("rbind", lapply(x, f)) but under the hood is much more efficient.

and a thank you to Jake Kaupp for introducing me to map_df() here.


This can be done very succinctly with dplyr and purrr from the tidyverse. Where x is a list of the names of your csv files you can simply use:

bind_rows(map(x, read.csv))

Mapping read.csv to x produces a list of dfs that bind_rows then neatly combines!

Tags:

Csv

R

Rbind