假设我们有一个包含多个data.csv文件的文件夹,每个文件包含相同数量的变量,但每个变量来自不同的时间。 在R中是否有一种方法可以同时导入它们而不是逐个导入?
我的问题是我有大约2000个数据文件要导入,并且只能通过使用代码单独导入它们:
read.delim(file="filename", header=TRUE, sep="\t")
效率不高。
假设我们有一个包含多个data.csv文件的文件夹,每个文件包含相同数量的变量,但每个变量来自不同的时间。 在R中是否有一种方法可以同时导入它们而不是逐个导入?
我的问题是我有大约2000个数据文件要导入,并且只能通过使用代码单独导入它们:
read.delim(file="filename", header=TRUE, sep="\t")
效率不高。
当前回答
使用purrr并将文件id作为列:
library(tidyverse)
p <- "my/directory"
files <- list.files(p, pattern="csv", full.names=TRUE) %>%
set_names()
merged <- files %>% map_dfr(read_csv, .id="filename")
如果没有set_names(), .id=将使用整数指示符,而不是实际的文件名。
如果你想要一个短的文件名而不是完整的路径:
merged <- merged %>% mutate(filename=basename(filename))
其他回答
使用plyr::ldply,在读取400个csv文件时,通过启用.parallel选项,大约可以提高50%的速度,每个文件大约30-40 MB。示例包括一个文本进度条。
library(plyr)
library(data.table)
library(doSNOW)
csv.list <- list.files(path="t:/data", pattern=".csv$", full.names=TRUE)
cl <- makeCluster(4)
registerDoSNOW(cl)
pb <- txtProgressBar(max=length(csv.list), style=3)
pbu <- function(i) setTxtProgressBar(pb, i)
dt <- setDT(ldply(csv.list, fread, .parallel=TRUE, .paropts=list(.options.snow=list(progress=pbu))))
stopCluster(cl)
下面是一些使用R base将.csv文件转换为data.frame的选项,以及一些可用来读取R文件的包。
这比下面的选项要慢。
# Get the files names
files = list.files(pattern="*.csv")
# First apply read.csv, then rbind
myfiles = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
编辑:-更多的额外选择使用数据。表和读取器
一个fread()版本,它是数据的函数。表方案。这是目前为止R中最快的选项。
library(data.table)
DT = do.call(rbind, lapply(files, fread))
# The same using `rbindlist`
DT = rbindlist(lapply(files, fread))
使用readr,这是另一个读取csv文件的包。它比fread慢,比base R快,但功能不同。
library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()
有人要求我将此功能添加到stackoverflow R包中。鉴于它是一个tinyverse包(不能依赖于第三方包),以下是我想到的:
#' Bulk import data files
#'
#' Read in each file at a path and then unnest them. Defaults to csv format.
#'
#' @param path a character vector of full path names
#' @param pattern an optional \link[=regex]{regular expression}. Only file names which match the regular expression will be returned.
#' @param reader a function that can read data from a file name.
#' @param ... optional arguments to pass to the reader function (eg \code{stringsAsFactors}).
#' @param reducer a function to unnest the individual data files. Use I to retain the nested structure.
#' @param recursive logical. Should the listing recurse into directories?
#'
#' @author Neal Fultz
#' @references \url{https://stackoverflow.com/questions/11433432/how-to-import-multiple-csv-files-at-once}
#'
#' @importFrom utils read.csv
#' @export
read.directory <- function(path='.', pattern=NULL, reader=read.csv, ...,
reducer=function(dfs) do.call(rbind.data.frame, dfs), recursive=FALSE) {
files <- list.files(path, pattern, full.names = TRUE, recursive = recursive)
reducer(lapply(files, reader, ...))
}
通过参数化读卡器和减速器功能,人们可以使用数据。table或dplyr(如果他们选择的话),或者只使用适用于较小数据集的基本R函数。
我喜欢使用list.files(), lapply()和list2env()(或fs::dir_ls(), purrr::map()和list2env())的方法。这看起来既简单又灵活。
或者,您可以尝试小包{tor} (to-R):默认情况下,它将文件从工作目录导入到列表(list_*()变量)或全局环境(load_*()变量)。
例如,这里我使用tor::list_csv()将我工作目录中的所有.csv文件读入一个列表:
library(tor)
dir()
#> [1] "_pkgdown.yml" "cran-comments.md" "csv1.csv"
#> [4] "csv2.csv" "datasets" "DESCRIPTION"
#> [7] "docs" "inst" "LICENSE.md"
#> [10] "man" "NAMESPACE" "NEWS.md"
#> [13] "R" "README.md" "README.Rmd"
#> [16] "tests" "tmp.R" "tor.Rproj"
list_csv()
#> $csv1
#> x
#> 1 1
#> 2 2
#>
#> $csv2
#> y
#> 1 a
#> 2 b
现在我用tor::load_csv()将这些文件加载到我的全局环境中:
# The working directory contains .csv files
dir()
#> [1] "_pkgdown.yml" "cran-comments.md" "CRAN-RELEASE"
#> [4] "csv1.csv" "csv2.csv" "datasets"
#> [7] "DESCRIPTION" "docs" "inst"
#> [10] "LICENSE.md" "man" "NAMESPACE"
#> [13] "NEWS.md" "R" "README.md"
#> [16] "README.Rmd" "tests" "tmp.R"
#> [19] "tor.Rproj"
load_csv()
# Each file is now available as a dataframe in the global environment
csv1
#> x
#> 1 1
#> 2 2
csv2
#> y
#> 1 a
#> 2 b
如果您需要读取特定的文件,您可以使用regexp, ignore匹配它们的文件路径。大小写颠倒。
为了获得更大的灵活性,请使用list_any()。它允许您通过参数.f提供reader函数。
(path_csv <- tor_example("csv"))
#> [1] "C:/Users/LeporeM/Documents/R/R-3.5.2/library/tor/extdata/csv"
dir(path_csv)
#> [1] "file1.csv" "file2.csv"
list_any(path_csv, read.csv)
#> $file1
#> x
#> 1 1
#> 2 2
#>
#> $file2
#> y
#> 1 a
#> 2 b
通过…传递附加参数或者在函数内部。
path_csv %>%
list_any(readr::read_csv, skip = 1)
#> Parsed with column specification:
#> cols(
#> `1` = col_double()
#> )
#> Parsed with column specification:
#> cols(
#> a = col_character()
#> )
#> $file1
#> # A tibble: 1 x 1
#> `1`
#> <dbl>
#> 1 2
#>
#> $file2
#> # A tibble: 1 x 1
#> a
#> <chr>
#> 1 b
path_csv %>%
list_any(~read.csv(., stringsAsFactors = FALSE)) %>%
map(as_tibble)
#> $file1
#> # A tibble: 2 x 1
#> x
#> <int>
#> 1 1
#> 2 2
#>
#> $file2
#> # A tibble: 2 x 1
#> y
#> <chr>
#> 1 a
#> 2 b
除了使用lapply或R中的其他循环构造,您还可以将CSV文件合并到一个文件中。
在Unix中,如果文件没有头文件,那么很简单:
cat *.csv > all.csv
或者如果有标题,你可以找到一个字符串匹配标题,只有标题(即假设标题行都以“年龄”开头),你会这样做:
cat *.csv | grep -v ^Age > all.csv
我认为在Windows中,你可以通过DOS命令框中的COPY和SEARCH(或FIND或其他什么)来做到这一点,但为什么不安装cygwin并获得Unix命令shell的强大功能呢?