web-dev-qa-db-ja.com

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

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

私の問題は、私がインポートする約2000のデータファイルを持っていて、コードを使うだけでそれらを個別にインポートしなければならないということです:

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

非常に効率的ではありません。

169
Jojo Ono

次のようなものは、各データフレームを単一のリスト内の個別の要素として生成します。

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)

しかし、繰り返しますが、それらを1つのリストにまとめたほうがよい場合がよくあります。

209

これは、.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.tablereadrを使った追加の選択肢

fread()バージョン。これはdata.tableパッケージの関数です。これが最速の選択肢です。

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

Csvファイルを読み込むための新しいhadleyパッケージである readr を使用する。 freadより少し遅いですが、機能は異なります。

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

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

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

そしてdata.tablefread() はそれらのロード時間を半分にすることさえできます。 (1/4ベースR倍)

library(data.table)

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

stringsAsFactors = FALSE引数はデータフレームファクタを解放します。

型キャストがぎこちない場合は、引数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(.))

(型キャストとサブディレクトリの扱い方は、上で提案した2番目と3番目の変種で示したのと同じ方法で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(., stringsAsFactors = FALSE))
}

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

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

ミドリングユースケース

Boxplot Comparison of Elapsed Time my typical use case

より大きなユースケース

Boxplot Comparison of Elapsed Time for Extra Large Load

さまざまなユースケース

行数:ファイル数(1000、100、10)
列:最終データフレームサイズ(5MB、50MB、500MB)
(元のサイズを表示するには画像をクリックしてください)Boxplot Comparison of Directory Size Variations

ベース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 Rinker および Akrun
  • map_df()を紹介してくれたJake Kaupp
  • 小さいファイル、小さいデータフレームの分析結果で観察された視覚化の改善とパフォーマンスの逆転についての議論/確認に関する有益なフィードバックを寄せてくれたDavid McLaughlin。
84
leerssej

Rでlapplyや他のループ構造を使うのと同様に、あなたのCSVファイルを一つのファイルにマージすることができます。

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

cat *.csv > all.csv

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

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

私はWindowsでDOSコマンドボックスからCOPYSEARCH(あるいはFINDか何か)を使ってこれを行うことができると思いますが、なぜcygwinをインストールしてUnixコマンドシェルの力を得ないのですか?

24
Spacedman

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

plyr::ldplyを使用すると、400個のcsvファイルをそれぞれ約30〜40MB読み込むときに.parallelオプションを有効にすることで、約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)
4
manotheshark

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

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

追加の引数はrio::importに渡されます。 rioはRが読むことができるほとんどすべてのファイルフォーマットを扱うことができ、可能であればdata.tablefreadを使うので、それも速いはずです。

4
user3603486

Dnlbrkのコメントを基にして、大きなファイルの場合、assign2は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に設定することによって、あなたはあなたのファイルのリストの中の別々の文字列としてそれぞれのファイルへのフルパスを得るでしょう。 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またはbase 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)
}
2
user6741397

私は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

特定のファイルを読む必要がある場合は、それらのファイルパスを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

...を介して、または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
1
Mauro Lepore