假设我们有一个包含多个data.csv文件的文件夹,每个文件包含相同数量的变量,但每个变量来自不同的时间。
R中是否有一种方法可以同时导入所有变量,而不必分别导入所有变量? br />
我的问题是我要导入大约2000个数据文件,而仅使用以下代码即可分别导入它们:

read.delim(file="filename", header=TRUE, sep="\t")


不是非常有效。

#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]))


或者,如果没有assign,并演示(1)如何清除文件名和(2)显示如何使用list2env,则可以尝试以下操作:

temp = list.files(pattern="*.csv")
list2env(
  lapply(setNames(temp, make.names(gsub("*.csv$", "", temp))), 
         read.csv), envir = .GlobalEnv)


但是,再次将它们放在一个列表中通常会更好。

评论


谢谢!这很好用...我将如何命名刚导入的每个文件,以便可以轻松调用它们?

–小野Jojo
2012年7月11日在13:34

如果您可以向我们显示一些文件的前几行,我们可能会提供一些建议-为此编辑您的问题!

– Spacedman
2012年7月11日14:13

上面的代码非常适合将它们作为单个对象导入,但是当我尝试从数据集中调用列时,它无法识别它,因为它只是单个对象而不是数据框,即我上面的代码版本是:setwd( 'C:/ Users / new / Desktop / Dives / 0904_003')temp <-list.files(pattern =“ *。csv”)ddives <-lapply(temp,read.csv)所以现在每个文件都称为ddives [n ],但是我将如何编写一个循环以使它们成为所有数据帧而不是单个对象?我可以使用data.frame运算符单独实现此功能,但是不确定如何循环执行此操作。 @mrdwab

–小野Jojo
2012年7月11日15:07



@JosephOnoufriou,请参阅我的更新。但是总的来说,如果要对所有数据帧进行类似的计算,我发现使用列表更容易。

– A5C1D2H2I1M1N2O1R2T1
2012年7月11日15:56

对于任何试图编写一个函数来使用Assign ...来更新此答案的人,如果您希望分配的值驻留在全局环境中,请确保设置了Inherits = T。

–dnlbrky
2014年4月30日在2:06

#2 楼

一个快速而简洁的tidyverse解决方案:
(比Base R的read.csv快两倍以上)

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


和data.table的fread()甚至可以缩短加载时间再减一半(对于基本R时间的1/4)

library(data.table)

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


stringsAsFactors = FALSE参数使数据帧因子保持自由状态,(marbel指出,这是fread的默认设置)

如果类型转换很俗气,则可以使用col_types参数将所有列强制为字符。

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


如果想要进入子目录以构建最终绑定的文件列表,请确保包括路径名,并在列表中注册具有其全名的文件。这将使绑定工作可以在当前目录之外进行。 (将完整路径名视为像护照一样操作,以允许在目录“边界”之间移动。)

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


如哈德利在此处所述(大约一半):


map_df(x, f)实际上与do.call("rbind", lapply(x, f)) .....


奖金功能-根据下面的注释在Niks功能请求的记录中添加文件名:
*将原始filename添加到每个记录。

代码说明:在表的初始读取过程中,创建一个将文件名附加到每个记录的函数。然后使用该函数而不是简单的read_csv()函数。

read_plus <- function(flnm) {
    read_csv(flnm) %>% 
        mutate(filename = flnm)
}

tbl_with_sources <-
    list.files(pattern = "*.csv", 
               full.names = T) %>% 
    map_df(~read_plus(.))


(也可以按照以下方法在read_plus()函数内部处理类型转换和子目录处理方法上面建议的第二和第三个变体。)

### Benchmark Code & Results 
library(tidyverse)
library(data.table)
library(microbenchmark)

### Base R Approaches
#### Instead of a dataframe, this approach creates a list of lists
#### removed from analysis as this alone doubled analysis time reqd
# lapply_read.delim <- function(path, pattern = "*.csv") {
#     temp = list.files(path, pattern, full.names = TRUE)
#     myfiles = lapply(temp, read.delim)
# }

#### `read.csv()`
do.call_rbind_read.csv <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))
}

map_df_read.csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read.csv(., stringsAsFactors = FALSE))
}


### *dplyr()*
#### `read_csv()`
lapply_read_csv_bind_rows <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    lapply(files, read_csv) %>% bind_rows()
}

map_df_read_csv <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~read_csv(., col_types = cols(.default = "c")))
}

### *data.table* / *purrr* hybrid
map_df_fread <- function(path, pattern = "*.csv") {
    list.files(path, pattern, full.names = TRUE) %>% 
    map_df(~fread(.))
}

### *data.table*
rbindlist_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    rbindlist(lapply(files, function(x) fread(x)))
}

do.call_rbind_fread <- function(path, pattern = "*.csv") {
    files = list.files(path, pattern, full.names = TRUE)
    do.call(rbind, lapply(files, function(x) fread(x, stringsAsFactors = FALSE)))
}


read_results <- function(dir_size){
    microbenchmark(
        # lapply_read.delim = lapply_read.delim(dir_size), # too slow to include in benchmarks
        do.call_rbind_read.csv = do.call_rbind_read.csv(dir_size),
        map_df_read.csv = map_df_read.csv(dir_size),
        lapply_read_csv_bind_rows = lapply_read_csv_bind_rows(dir_size),
        map_df_read_csv = map_df_read_csv(dir_size),
        rbindlist_fread = rbindlist_fread(dir_size),
        do.call_rbind_fread = do.call_rbind_fread(dir_size),
        map_df_fread = map_df_fread(dir_size),
        times = 10L) 
}

read_results_lrg_mid_mid <- read_results('./testFolder/500MB_12.5MB_40files')
print(read_results_lrg_mid_mid, digits = 3)

read_results_sml_mic_mny <- read_results('./testFolder/5MB_5KB_1000files/')
read_results_sml_tny_mod <- read_results('./testFolder/5MB_50KB_100files/')
read_results_sml_sml_few <- read_results('./testFolder/5MB_500KB_10files/')

read_results_med_sml_mny <- read_results('./testFolder/50MB_5OKB_1000files')
read_results_med_sml_mod <- read_results('./testFolder/50MB_5OOKB_100files')
read_results_med_med_few <- read_results('./testFolder/50MB_5MB_10files')

read_results_lrg_sml_mny <- read_results('./testFolder/500MB_500KB_1000files')
read_results_lrg_med_mod <- read_results('./testFolder/500MB_5MB_100files')
read_results_lrg_lrg_few <- read_results('./testFolder/500MB_50MB_10files')

read_results_xlg_lrg_mod <- read_results('./testFolder/5000MB_50MB_100files')


print(read_results_sml_mic_mny, digits = 3)
print(read_results_sml_tny_mod, digits = 3)
print(read_results_sml_sml_few, digits = 3)

print(read_results_med_sml_mny, digits = 3)
print(read_results_med_sml_mod, digits = 3)
print(read_results_med_med_few, digits = 3)

print(read_results_lrg_sml_mny, digits = 3)
print(read_results_lrg_med_mod, digits = 3)
print(read_results_lrg_lrg_few, digits = 3)

print(read_results_xlg_lrg_mod, digits = 3)

# display boxplot of my typical use case results & basic machine max load
par(oma = c(0,0,0,0)) # remove overall margins if present
par(mfcol = c(1,1)) # remove grid if present
par(mar = c(12,5,1,1) + 0.1) # to display just a single boxplot with its complete labels
boxplot(read_results_lrg_mid_mid, las = 2, xlab = "", ylab = "Duration (seconds)", main = "40 files @ 12.5MB (500MB)")
boxplot(read_results_xlg_lrg_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 50MB (5GB)")

# generate 3x3 grid boxplots
par(oma = c(12,1,1,1)) # margins for the whole 3 x 3 grid plot
par(mfcol = c(3,3)) # create grid (filling down each column)
par(mar = c(1,4,2,1)) # margins for the individual plots in 3 x 3 grid
boxplot(read_results_sml_mic_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 5KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_tny_mod, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "100 files @ 50KB (5MB)", xaxt = 'n')
boxplot(read_results_sml_sml_few, las = 2, xlab = "", ylab = "Duration (milliseconds)", main = "10 files @ 500KB (5MB)",)

boxplot(read_results_med_sml_mny, las = 2, xlab = "", ylab = "Duration (microseconds)        ", main = "1000 files @ 50KB (50MB)", xaxt = 'n')
boxplot(read_results_med_sml_mod, las = 2, xlab = "", ylab = "Duration (microseconds)", main = "100 files @ 500KB (50MB)", xaxt = 'n')
boxplot(read_results_med_med_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 5MB (50MB)")

boxplot(read_results_lrg_sml_mny, las = 2, xlab = "", ylab = "Duration (seconds)", main = "1000 files @ 500KB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_med_mod, las = 2, xlab = "", ylab = "Duration (seconds)", main = "100 files @ 5MB (500MB)", xaxt = 'n')
boxplot(read_results_lrg_lrg_few, las = 2, xlab = "", ylab = "Duration (seconds)", main = "10 files @ 50MB (500MB)")


用例



较大的用例



各种用例

行:文件数(1000,100,10)
列:最终数据帧大小(5MB ,50MB,500MB)(点击图片可查看原始大小)


对于最小的用例,基本的R结果会更好,在这种情况下,使purrr和dplyr的C库的开销要超过执行较大规模的处理任务时观察到的性能提升。

要运行自己的测试,您可能会发现此bash脚本会有所帮助。

for ((i=1; i<=; i++)); do 
  cp "" "${1:0:8}_${i}.csv";
done


bash what_you_name_this_script.sh "fileName_you_want_copied" 100将创建100个按顺序编号的文件副本(文件名的前8个字符和下划线)。

属性和赞赏

特别感谢:




泰勒·林克和阿克伦展示了microbenchmark。
Jake Kaupp在这里向我介绍了map_df()
David McLaughlin提供了有关改进可视化效果并讨论/确认在小文件,小数据帧分析结果中观察到的性能反转的有用反馈。
marbel指出了fread()的默认行为。 (我需要研究data.table。)


评论


您的解决方案对我有用。在此,我要存储该文件名以区分它们。可以吗?

–尼克
17年5月5日在6:19

@Niks-当然!只需写入和交换一个小功能,它不仅可以读取文件,而且还可以将文件名立即附加到每个读取的记录中。就像这样readAddFilename <-function(flnm){read_csv(flnm)%>%mutate(filename = flnm)}然后将其放入map_df中,而不是现在的简单只读read_csv()中。我可以更新上面的条目以显示该功能以及如果您仍有疑问或认为有帮助的话,它如何适合管道。

–́leerssej
17-12-10在1:04



实践中的问题是read_csv比fread慢得多。如果您要说更快的话,我会提供一个基准。一个想法是创建30个1GB的文件并读取它们,这在性能上很重要。

– marbel
18 Mar 16 '18 3:26



@marbel:谢谢你的建议!在530 MB和更小的目录(最多100个文件)上,我发现data.table的fread()和dplyr的read_csv()之间的性能提高了25%:14.2 vs 19.9秒。 TBH,我只比较了R和dplyr,因为read_csv()比read.csv()快2-4倍,所以基准测试似乎没有必要。但是,给fread()旋转并暂停以检查更完整的基准测试结果很有趣。再次感谢!

–́leerssej
18年4月24日在20:04

另一个好点。我想当我写这篇文章时,我对保护data.table活动不要对数据进行适当的修改过于谨慎(这会影响数据的下一次和所有后续运行的性能)。在这种情况下,那当然是没有意义的。谢谢。 :-D期待很快地再次运行数字,而无需使用函数,并且使用较大的计算机使用较大的数据集。

–́leerssej
19年7月10日在19:37



#3 楼

这是一些使用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)))


编辑:-使用data.tablereadr的更多其他选择 fread()版本,它是data.table软件包的功能。这是迄今为止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()


评论


相对于Reduce(rbind,lapply(...))只是学习R但我的猜测表现不佳

–aaron
14年7月18日在16:37

我添加了一个data.table版本,可以提高性能。

– marbel
2015年5月18日,0:55



是否可以只读取特定文件? e.x.名称中包含“天气”的文件?

–废弃
16年8月8日在16:41

在这里找到它:stackoverflow.com/questions/10353540/…谢谢。

–废弃
16年8月8日在17:24

+1似乎产生一个数据框-所有CSV文件的SQL UNION-最容易使用。由于OP没有指定他们要1个数据帧还是多个数据帧,因此我假设1个数据帧是最佳的,因此我很惊讶被接受的答案没有做任何“ UNION”。我喜欢这个答案,这与do.call的解释一致

–红豌豆
16 Sep 4 '16:36



#4 楼

除了在R中使用lapply或其他循环构造之外,您还可以将CSV文件合并到一个文件中。

在Unix中,如果文件没有标题,则操作起来很简单:

cat *.csv > all.csv


或者如果有标头,并且您可以找到一个仅与标头匹配的字符串(例如,标头行均以“ Age”开头),您可以这样做:<我认为在Windows中,您可以使用DOS命令框中的COPYSEARCH(或FIND等)来执行此操作,但是为什么不安装cygwin并获得电源Unix命令shell?

评论


甚至与随Git安装一起推出的Git Bash一起去?

–́leerssej
17-4-10在23:03



以我的经验,如果文件开始变得很大,这不是最快的解决方案。

–阿米尔
18-10-10在8:58

#5 楼

这是我开发的用于将所有csv文件读入R的代码。它将为每个csv文件分别创建一个数据框,并为该文件的标题命名该文件的原始名称(删除空格和.csv),希望对您有用! >
path <- "C:/Users/cfees/My Box Files/Fitness/"
files <- list.files(path=path, pattern="*.csv")
for(file in files)
{
perpos <- which(strsplit(file, "")[[1]]==".")
assign(
gsub(" ","",substr(file, 1, perpos-1)), 
read.csv(paste(path,file,sep="")))
}


#6 楼

@ A5C1D2H2I1M1N2O1R2T1,@ leerssej和@marbel的前三个答案基本上是相同的:将fread应用于每个文件,然后rbind / rbindlist得到的data.tables。我通常使用rbindlist(lapply(list.files("*.csv"),fread))形式。

这比其他R-internal替代品更好,并且对于少数大型csv来说很好,但是对于速度重要的大量小型csv则不是最好的。在这种情况下,第一次使用cat可能会更快,就像@Spacedman在排名第4的答案中所建议的那样。我将在R中添加有关如何执行此操作的详细信息:

x = fread(cmd='cat *.csv', header=F)


但是,如果每个csv都有标题,该怎么办?

x = fread(cmd="awk 'NR==1||FNR!=1' *.csv", header=T)


如果文件太多而导致*.csv shell glob失败怎么办?

x = fread(cmd='find . -name "*.csv" | xargs cat', header=F)


如果所有文件都带有标头并且文件太多?

header = fread(cmd='find . -name "*.csv" | head -n1 | xargs head -n1', header=T)
x = fread(cmd='find . -name "*.csv" | xargs tail -q -n+2', header=F)
names(x) = names(header)


如果结果串联的csv对于系统内存来说太大了怎么办?

system('find . -name "*.csv" | xargs cat > combined.csv')
x = fread('combined.csv', header=F)


带有标题吗?

system('find . -name "*.csv" | head -n1 | xargs head -n1 > combined.csv')
system('find . -name "*.csv" | xargs tail -q -n+2 >> combined.csv')
x = fread('combined.csv', header=T)


最后,如果您不希望目录中的所有.csv而是特定的文件集,该怎么办? (而且它们都有标头。)(这是我的用例。)

fread(text=paste0(system("xargs cat|awk 'NR==1||!=\"<column one name>\"'",input=paths,intern=T),collapse="\n"),header=T,sep="\t")


,这和普通fread xargs cat的速度差不多:)

注意:对于data.table v1.11.6之前的版本(2018年9月19日),请从cmd=中省略fread(cmd=

附录:使用并行库的mclapply代替串行lapply,例如,rbindlist(lapply(list.files("*.csv"),fread))也比rbindlist lapply fread快得多。

将121401 csvs读取到单个data.table中的时间。每个csv有3列,一个标题行,平均4.510行。机器是具有96个内核的GCP VM:

rbindlist lapply fread   234.172s 247.513s 256.349s
rbindlist mclapply fread  15.223s   9.558s   9.292s
fread xargs cat            4.761s   4.259s   5.095s


总而言之,如果您对速度感兴趣,并且有许多文件和许多内核,请阅读xargs cat比前3个答案中最快的解决方案快约50倍。

#7 楼

在我看来,大多数其他答案都被简洁的单行代码rio::import_list所淘汰:

library(rio)
my_data <- import_list(dir("path_to_directory", pattern = ".csv", rbind = TRUE))


任何其他参数都传递给rio::importrio几乎可以处理R可以读取的任何文件格式,并且在可能的情况下使用data.tablefread,因此它也应该很快。

#8 楼

使用plyr::ldply时,通过启用.parallel选项,同时读取每个大约30-40 MB的400个csv文件,速度大约提高了50%。示例包括文本进度栏。

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)


评论


好答案!如何将其他参数传递给fread或用户定义的函数?谢谢!

–董
4月13日12:54

@Tung在看ldply时显示了...其他参数传递给.fun。使用fread,skip = 100或function(x)fread(x,skip = 100)都可以

– manotheshark
4月13日21:24

使用function(x)fread(x,skip = 100)对我不起作用,但是在裸函数名称完成后提供额外的参数。再次感谢!

–董
4月14日在16:08

#9 楼

这是我读取多个文件并将它们组合为1个数据帧的特定示例:

path<- file.path("C:/folder/subfolder")
files <- list.files(path=path, pattern="/*.csv",full.names = T)
library(data.table)
data = do.call(rbind, lapply(files, function(x) read.csv(x, stringsAsFactors = FALSE)))


评论


您可以从data.table使用rbindlist()

– jogo
1月18日9:50

#10 楼

在dnlbrk的注释的基础上,对于大文件,assign可以比list2env更快。

library(readr)
library(stringr)

List_of_file_paths <- list.files(path ="C:/Users/Anon/Documents/Folder_with_csv_files/", pattern = ".csv", all.files = TRUE, full.names = TRUE)


通过将full.names参数设置为true,您将获得完整的路径每个文件作为文件列表中单独的字符串,例如List_of_file_paths [1]将类似于“ C:/Users/Anon/Documents/Folder_with_csv_files/file1.csv”

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}


您可以使用data.table包的fread或base R read.csv而不是read_csv。 file_name步骤允许您整理名称,以便每个数据框都不会保留文件的完整路径作为其名称。
您可以扩展循环,以便在将数据表转移到全局环境之前对数据表做更多的事情,例如:

for(f in 1:length(List_of_filepaths)) {
  file_name <- str_sub(string = List_of_filepaths[f], start = 46, end = -5)
  file_df <- read_csv(List_of_filepaths[f])  
  file_df <- file_df[,1:3] #if you only need the first three columns
  assign( x = file_name, value = file_df, envir = .GlobalEnv)
}


#11 楼

只要您的计算机上有许多内核,以下代码就可以为您提供最快的大数据速度:

if (!require("pacman")) install.packages("pacman")
pacman::p_load(doParallel, data.table, stringr)

# get the file name
dir() %>% str_subset("\.csv$") -> fn

# use parallel setting
(cl <- detectCores() %>%
  makeCluster()) %>%
  registerDoParallel()

# read and bind all files together
system.time({
  big_df <- foreach(
    i = fn,
    .packages = "data.table"
  ) %dopar%
    {
      fread(i, colClasses = "character")
    } %>%
    rbindlist(fill = TRUE)
})

# end of parallel work
stopImplicitCluster(cl)


2020/04/16更新:
当我发现一个可用于并行计算的新程序包时,可以使用以下代码提供替代解决方案。

if (!require("pacman")) install.packages("pacman")
pacman::p_load(future.apply, data.table, stringr)

# get the file name
dir() %>% str_subset("\.csv$") -> fn

plan(multiprocess)

future_lapply(fn,fread,colClasses = "character") %>% 
  rbindlist(fill = TRUE) -> res

# res is the merged data.table


#12 楼

我喜欢使用list.files()lapply()list2env()(或fs::dir_ls()purrr::map()list2env())的方法。这似乎简单且灵活。

或者,您也可以尝试使用小型软件包{tor}(至-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.caseinvert


要获得更大的灵活性,请使用list_any()。它允许您通过参数.f提供阅读器功能。

(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


通过...或在lambda函数内部传递附加参数。

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


#13 楼

有人要求我将此功能添加到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, ...))
}


通过参数化阅读器和减速器函数,人们可以选择使用data.table或dplyr,或者只使用适合较小数据集的base R函数。