複数の.csvファイルを一度にインポートする方法は?


219

複数のdata.csvファイルを含むフォルダーがあり、それぞれに同じ数の変数が含まれていますが、それぞれ異なる時刻のものです。Rにそれらをすべて個別にインポートするのではなく、同時にインポートする方法はありますか?

私の問題は、インポートするデータファイルが約2000あり、コードを使用するだけでそれらを個別にインポートする必要があることです。

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

あまり効率的ではありません。

回答:


259

次のような結果になると、各データフレームは単一のリストの個別の要素になります。

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)

しかし、繰り返しになりますが、それらを単一のリストに残しておく方が良い場合がよくあります。


ありがとう!これは非常にうまくいきます...インポートしたばかりの各ファイルに名前を付けて、簡単に呼び出すことができるようにするにはどうすればよいですか?
小野ジョジョ

ファイルの一部の最初の数行を見せていただければ、いくつかの提案があるかもしれません-そのために質問を編集してください!
Spacedman

2
上記のコードはそれらを単一のオブジェクトとしてインポートするのに完全に機能しますが、データセットから列を呼び出そうとすると、データフレームではなく単一のオブジェクトであるため、列を認識しません。つまり、上記のコードの私のバージョンは次のとおりです:setwd( 'C:/ Users / new / Desktop / Dives / 0904_003')temp <-list.files(pattern = "*。csv")ddives <-lapply(temp、read.csv)したがって、各ファイルはddives [n ]しかし、ループを作成して、単一のオブジェクトではなくすべてのデータフレームにするループを作成するにはどうすればよいでしょうか。data.frame演算子を使用して個別にこれを達成できますが、これをループする方法がわかりません。@mrdwab
小野ジョジョ

@JosephOnoufriou、私の更新を参照してください。しかし、一般的に、すべてのデータフレームで同様の計算を行う場合は、リストを操作する方が簡単です。
A5C1D2H2I1M1N2O1R2T1

2
assign... を使用してこの回答の更新バージョンを実行する関数を記述しようとしている人は、割り当てられた値をグローバル環境に配置する場合は、を設定してくださいinherits=T
dnlbrky 14

127

スピーディーで簡潔なtidyverseソリューション:(Base Rの 2倍以上の速さ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"))) 

Hadleyがここで説明しているように(半分ほど下):

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()上記で提案された2番目と3番目のバリアントに示されているのと同じ方法で関数内でも処理できます。)

### 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<=$2; i++)); do 
  cp "$1" "${1:0:8}_${i}.csv";
done

bash what_you_name_this_script.sh "fileName_you_want_copied" 100 (ファイル名の最初の8文字とアンダースコアの後に)番号が付けられたファイルの100コピーが作成されます。

帰属と感謝

特別な感謝を込めて:

  • マイクロベンチマークを実証してくれたTyler RinkerAkrun
  • map_df() ここに紹介してくれたJake Kaupp 。
  • ビジュアライゼーションの改善、および小さなファイル、小さなデータフレーム分析の結果で観察されたパフォーマンスの逆転の議論/確認に関する有用なフィードバックを提供するDavid McLaughlin氏
  • のデフォルトの動作を指摘するためのmarbel fread()。(私は勉強する必要がありdata.tableます。)

1
あなたの解決策は私のために働きます。この中で、それらを区別するためにそのファイル名を保存したいと思います。それは可能ですか?
Niks

1
@ニックス-確かに!ファイルを読み取るだけでなく、読み取った各レコードにファイル名をすぐに追加する小さな関数を書き込んで入れ替えるだけです。そのようにreadAddFilename <- function(flnm) { read_csv(flnm) %>% mutate(filename = flnm) }して、今そこにあるmap_df単純な読み取り専用の代わりにそれをドロップしread_csv()ます。上記のエントリを更新して、機能と、質問がまだある場合や参考になると思われる場合にパイプにどのように適合するかを示します。
leerssej 2017

実際の問題read_csvは、それよりもはるかに遅いことfreadです。あなたが何かがより速いと言うつもりなら、私はベンチマークを含めます。1つのアイデアは、1 GBのファイルを30個作成して読み取ることです。これは、パフォーマンスが重要な場合です。
マーベル2018年

@marbel:提案ありがとうございます!530メガバイトと(最大100ファイル付き)小さいディレクトリに私は間のパフォーマンスが25%向上を見つけていますdata.tableさんfread()dplyrさんread_csv():14.2 19.9対秒。TBH、私はベースRとdplyrを比較するだけでしたが、のread_csv()約2〜4倍速いためread.csv()、ベンチマークは必要ないようでした。ただしfread()、より完全なベンチマーク結果を確認するために、少しの間、少しの間、一時停止するのは興味深いことです。再度、感謝します!
leerssej

1
別の素晴らしい点。私が書いたとき、data.tableアクティビティを適切なデータに変更しないように注意しすぎていたと思います(これは、データ全体の次の実行とその後のすべての実行のパフォーマンスに影響します)。もちろん、この場合は意味がありません。ありがとうございました。:-D関数なしで、より大きなマシンでより大きなデータセットを使用して、すぐに数値を実行することを楽しみにしています。
leerssej

104

R baseを使用して.csvファイルを1つの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.tableおよびを使用してさらにいくつかの追加の選択肢readr

fread()関数であるバージョン、data.tableパッケージ。これはRの中で最速のオプションです。

library(data.table)
DT = do.call(rbind, lapply(files, fread))
# The same using `rbindlist`
DT = rbindlist(lapply(files, fread))

csvファイルを読み取るための別のパッケージであるreadrの使用。freadベースRよりも低速で高速ですが、機能は異なります。

library(readr)
library(dplyr)
tbl = lapply(files, read_csv) %>% bind_rows()

2
これは、Reduce(rbind、lapply(...))に対してどのように機能しますか?Rを学習するだけでパフォーマンスが低下する
aaron

4
data.tableパフォーマンスを向上させるバージョンを追加しました。
マーベル、2015年

特定のファイルのみを読み取ることはできますか?ex名前に「weather」を含むファイル?
2016

1
ここにそれを見つけました:stackoverflow.com/questions/10353540/…ありがとう。
2016

1
+1は、単一のデータフレーム(すべてのCSVファイルのSQL UNION)を生成するようなものであり、最も簡単に操作できます。OPは1つのデータフレームを必要とするか、多数のデータフレームを必要とするかを指定しなかったので、1つのデータフレームが最適であると想定しました。私はこの答えが好きです。これは、この説明do.call
The Red Pea

24

lapplyRでやその他のループ構造を使用するだけでなく、CSVファイルを1つのファイルにマージできます。

Unixでは、ファイルにヘッダーがない場合、次のように簡単です。

cat *.csv > all.csv

または、ヘッダーがあり、ヘッダーとヘッダーのみに一致する文字列を見つけることができる場合(つまり、ヘッダー行がすべて「Age」で始まる場合)、次のようにします。

cat *.csv | grep -v ^Age > all.csv

私は、Windowsにあなたがこれを行うことができると思うCOPYSEARCH(またはFINDDOSコマンドボックスからか何か)が、なぜインストールしないでcygwinUnixコマンドシェルのパワーをしてもらいますか?


または、インストールに干渉するGit Bashを使用しGitますか?
leerssej 2017

私の経験では、ファイルがかなり大きくなり始めた場合、これは最速のソリューションではありません。
アミール

20

これは、すべての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="")))
}

8

@ A5C1D2H2I1M1N2O1R2T1、@ leerssej、@ marbelによる上位3つの回答はすべて基本的に同じです。各ファイルにfreadを適用し、結果のdata.tablesをrbind / rbindlistします。通常はrbindlist(lapply(list.files("*.csv"),fread))フォームを使用します。

これは他のR内部の代替よりも優れており、少数の大きな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シェルグロブが失敗するほど多くのファイルがある場合はどうでしょうか?

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||$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=fromを省略しfread(cmd=ます。

補遺:シリアルlapplyの代わりに並列ライブラリのmclapplyを使用すると、たとえばrbindlist(lapply(list.files("*.csv"),fread))rbindlist lapply freadよりもはるかに高速になります。

121401 CSVを単一のdata.tableに読み込む時間。各csvには3つの列、1つのヘッダー行、および平均で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

要約すると、速度に興味があり、多くのファイルと多くのコアがある場合、fread xargs catは、上位3つの回答の中で最速のソリューションよりも約50倍高速です。


6

私の見解では、他のほとんどの回答はによって廃止されていますrio::import_list。これは簡潔なワンライナーです:

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

追加の引数はに渡されrio::importます。rioほぼすべてのファイル形式のRを扱うことができます読んで、それが使用できるdata.tablefreadそれはあまりにも高速である必要がありますので、可能な場合。


5

このオプションをplyr::ldply有効にすると、.parallel400個のcsvファイルをそれぞれ約30〜40 MBずつ読み取ることで、速度が約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またはに追加の引数をどのように渡しますかuser-defined functions?ありがとう!
タン

1
で@Tungルッキング?ldplyショー...、他の引数はに渡さ.fun。どちらfread, skip = 100function(x) fread(x, skip = 100)を使用するか、機能する
manotheshark

function(x) fread(x, skip = 100)私にとっては使用できませんでしたが、裸の関数名の後に追加の引数を指定するとうまくいきました。再度、感謝します!
タン

3

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)
}

read_csvの代わりに、data.tableパッケージのfreadまたはベースR 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)
}

3

これは、複数のファイルを読み取り、それらを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)))

1
rbindlist()data.table
jogo

3

次のコードを使用すると、コンピューターに多くのコアがある限り、ビッグデータの速度が最も速くなります。

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

1

list.files()lapply()およびlist2env()(またはfs::dir_ls()purrr::map()およびlist2env())を使用するアプローチが好きです。それはシンプルで柔軟なようです。

または、小さなパッケージ{ tor }(to-R試すこともできます。デフォルトでは、作業ディレクトリからリスト(list_*()バリアント)またはグローバル環境(load_*()バリアント)にファイルをインポートします。

たとえば、次のコマンドを使用して、作業ディレクトリからすべての.csvファイルをリストに読み取りますtor::list_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

特定のファイルを読み取る必要がある場合は、ファイルパスをregexpignore.caseおよびと一致させることができますinvert


さらに柔軟性を高めるために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

...またはラムダ関数内で追加の引数を渡します。

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

1

この機能をstackoverflow Rパッケージに追加するように依頼されました。それが小さなパッケージである(そしてサードパーティのパッケージに依存することはできない)ことを考えると、ここに私が思いついたものがあります:

#' 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{/programming/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を使用できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.