275

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

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

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

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

4

15 に答える 15

338

次のようなものは、各データ フレームを 1 つのリスト内の個別の要素として生成する必要があります。

temp = list.files(pattern="*.csv")
myfiles = lapply(temp, read.delim)

これは、これらの CSV が 1 つのディレクトリ (現在の作業ディレクトリ) にあり、すべての CSV に小文字の拡張子が付いていることを前提としています.csv

これらのデータ フレームを 1 つのデータ フレームに結合する場合は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)

しかし、繰り返しになりますが、多くの場合、それらを 1 つのリストに残しておく方がよいでしょう。

于 2012-07-11T13:16:28.437 に答える
207

迅速で簡潔な解決策: ( Base R のtidyverse2 倍以上の速さ) read.csv

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

そしてdata.tableは、これらのfread()ロード時間を再び半分に短縮することさえできます。( Base 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()、上記で提案した 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ファイルのコピーを 100 個作成し、(ファイル名の最初の 8 文字とアンダースコアの後に) 連番を付けます。

帰属と感謝

特別な感謝を込めて:

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

R ベースを使用して .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()バージョン。これは R で断然最速のオプションです。data.table

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()
于 2014-05-09T03:04:03.333 に答える
28

多くのファイルと多くのコアを使用するとfread xargs cat、上位 3 つの回答で最速のソリューションよりも約 50 倍高速になります (後述)。

rbindlist lapply read.delim  500s <- 1st place & accepted answer
rbindlist lapply fread       250s <- 2nd & 3rd place answers
rbindlist mclapply fread      10s
fread xargs cat                5s

121401 個の csv を単一の data.table に読み込む時間。各回は 3 回の実行の平均であり、四捨五入されます。各 csv には、3 つの列、1 つのヘッダー行、および平均で 4.510 行があります。マシンは 96 コアの GCP VM です。

@A5C1D2H2I1M1N2O1R2T1、@leerssej、および @marbel による上位 3 つの回答はすべて基本的に同じです。各ファイルに fread (または read.delim) を適用し、結果の data.tables を rbind/rbindlist します。小さなデータセットの場合、通常はrbindlist(lapply(list.files("*.csv"),fread))フォームを使用します。中規模のデータセットの場合、lapply の代わりに parallel の mclapply を使用します。これは、多くのコアがある場合にはるかに高速です。

これは、他の R 内部の代替手段よりも優れていますが、速度が重要な多数の小さな csv には最適ではありません。その場合、cat@ Spacedmanの回答のように、最初にすべてのcsvを1つのcsvに連結するために最初に使用する方がはるかに高速です。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 がシステム メモリに対して大きすぎる場合はどうなるでしょうか。(例: /dev/shm スペース不足エラー)

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 とほぼ同じ速度です:)

注: v1.11.6 より前の data.table (2018 年 9 月 19 日) の場合、cmd=fromを省略しfread(cmd=ます。

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

更新: これは、最速のソリューションを簡単に適用するために作成した関数です。私はいくつかの状況で本番環境で使用していますが、信頼する前に自分のデータで徹底的にテストする必要があります.

fread_many = function(files,header=T,...){
  if(length(files)==0) return()
  if(typeof(files)!='character') return()
  files = files[file.exists(files)]
  if(length(files)==0) return()
  tmp = tempfile(fileext = ".csv")
  # note 1: requires awk, not cat or tail because some files have no final newline
  # note 2: parallel --xargs is 40% slower
  # note 3: reading to var is 15% slower and crashes R if the string is too long
  # note 4: shorter paths -> more paths per awk -> fewer awks -> measurably faster
  #         so best cd to the csv dir and use relative paths
  if(header==T){
    system(paste0('head -n1 ',files[1],' > ',tmp))
    system(paste0("xargs awk 'FNR>1' >> ",tmp),input=files)
  } else {
    system(paste0("xargs awk '1' > ",tmp),input=files)
  }
  DT = fread(file=tmp,header=header,...)
  file.remove(tmp)
  DT
}

更新 2: ここでは、結果の data.table に各 csv の inpath の列を含める場合の fread_many 関数のより複雑なバージョンを示します。この場合、sep 引数を使用して csv セパレーターも明示的に指定する必要があります。

fread_many = function(files,header=T,keep_inpath=F,sep="auto",...){
  if(length(files)==0) return()
  if(typeof(files)!='character') return()
  files = files[file.exists(files)]
  if(length(files)==0) return()
  tmp = tempfile(fileext = ".csv")
  if(keep_inpath==T){
    stopifnot(sep!="auto")
    if(header==T){
      system(paste0('/usr/bin/echo -ne inpath"',sep,'" > ',tmp))
      system(paste0('head -n1 ',files[1],' >> ',tmp))
      system(paste0("xargs awk -vsep='",sep,"' 'BEGIN{OFS=sep}{if(FNR>1)print FILENAME,$0}' >> ",tmp),input=files)
    } else {
      system(paste0("xargs awk -vsep='",sep,"' 'BEGIN{OFS=sep}{print FILENAME,$0}' > ",tmp),input=files)
    }
  } else {
    if(header==T){
      system(paste0('head -n1 ',files[1],' > ',tmp))
      system(paste0("xargs awk 'FNR>1' >> ",tmp),input=files)
    } else {
      system(paste0("xargs awk '1' > ",tmp),input=files)
    }
  }
  DT = fread(file=tmp,header=header,sep=sep,...)
  file.remove(tmp)
  DT
}

警告: csv を読み取る前に連結するすべてのソリューションは、すべて同じセパレーターを持っていると想定しています。すべての csv が同じ区切り文字を使用していない場合は、代わりに rbindlist lapply fread、rbindlist mclapply fread、または fread xargs cat をバッチで使用します。バッチ内のすべての csv は同じ区切り文字を使用します。

于 2019-09-27T09:13:15.617 に答える
27

またはRで他のループ構成を使用するだけでなくlapply、CSVファイルを1つのファイルにマージできます。

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

cat *.csv > all.csv

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

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

COPYWindows では、DOS コマンド ボックスからand SEARCH(FINDまたは何か) を使用してこれを行うことができると思いますがcygwin、Unix コマンド シェルの機能をインストールして取得してみませんか?

于 2012-07-11T13:28:30.777 に答える
24

これは、すべての 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="")))
}
于 2014-02-05T21:44:09.583 に答える
4

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)
}
于 2016-09-24T10:23:08.733 に答える
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
于 2020-01-08T05:26:11.417 に答える