假设我们有一个包含多个data.csv文件的文件夹,每个文件包含相同数量的变量,但每个变量来自不同的时间。 在R中是否有一种方法可以同时导入它们而不是逐个导入?
我的问题是我有大约2000个数据文件要导入,并且只能通过使用代码单独导入它们:
read.delim(file="filename", header=TRUE, sep="\t")
效率不高。
假设我们有一个包含多个data.csv文件的文件夹,每个文件包含相同数量的变量,但每个变量来自不同的时间。 在R中是否有一种方法可以同时导入它们而不是逐个导入?
我的问题是我有大约2000个数据文件要导入,并且只能通过使用代码单独导入它们:
read.delim(file="filename", header=TRUE, sep="\t")
效率不高。
当前回答
除了使用lapply或R中的其他循环构造,您还可以将CSV文件合并到一个文件中。
在Unix中,如果文件没有头文件,那么很简单:
cat *.csv > all.csv
或者如果有标题,你可以找到一个字符串匹配标题,只有标题(即假设标题行都以“年龄”开头),你会这样做:
cat *.csv | grep -v ^Age > all.csv
我认为在Windows中,你可以通过DOS命令框中的COPY和SEARCH(或FIND或其他什么)来做到这一点,但为什么不安装cygwin并获得Unix命令shell的强大功能呢?
其他回答
使用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)
有人要求我将此功能添加到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函数。
使用readr 2.0.0以后,您可以一次读取多个文件,只需提供文件参数的路径列表。下面是一个使用readr::read_csv()的示例。
packageVersion("readr")
#> [1] '2.0.1'
library(readr)
library(fs)
# create files to read in
write_csv(read_csv("1, 2 \n 3, 4", col_names = c("x", "y")), file = "file1.csv")
write_csv(read_csv("5, 6 \n 7, 8", col_names = c("x", "y")), file = "file2.csv")
# get a list of files
files <- dir_ls(".", glob = "file*csv")
files
#> file1.csv file2.csv
# read them in at once
# record paths in a column called filename
read_csv(files, id = "filename")
#> # A tibble: 4 × 3
#> filename x y
#> <chr> <dbl> <dbl>
#> 1 file1.csv 1 2
#> 2 file1.csv 3 4
#> 3 file2.csv 5 6
#> 4 file2.csv 7 8
由reprex包于2021-09-16创建(v2.0.1)
如下所示,每个数据帧都应该作为单个列表中的单独元素:
temp = list.files(pattern="*.csv")
myfiles = lapply(temp, read.delim)
这里假设您将这些csv文件放在一个目录(您当前的工作目录)中,并且它们都具有小写扩展名.csv。
如果你想把这些数据帧组合成一个单一的数据帧,请参考其他答案中的解决方案,如do.call(rbind,…),dplyr::bind_rows()或data.table::rbindlist()。
如果你真的想要每个数据帧在一个单独的对象中,即使这通常是不可取的,你可以使用assign执行以下操作:
temp = list.files(pattern="*.csv")
for (i in 1:length(temp)) assign(temp[i], read.csv(temp[i]))
或者,不带赋值,并演示(1)如何清理文件名以及(2)如何使用list2env,您可以尝试以下方法:
temp = list.files(pattern="*.csv")
list2env(
lapply(setNames(temp, make.names(gsub("*.csv$", "", temp))),
read.csv), envir = .GlobalEnv)
但是,最好还是把它们放在一个列表中。
使用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))