15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rで大量のデータファイルをまとめて読み込む方法

Last updated at Posted at 2018-02-27

はじめに

Kaggleとかに参加すると、大量のデータファイルを与えられる事が多い。例えばGoogle Cloud & NCAA® ML Competition 2018-Men'sを例に取ると、DataFilesフォルダ以下は下記のようになる。

スクリーンショット 2018-02-27 11.02.02.png

下記のように一つ一つデータを読み込もうとすると面倒くさいので、どうにかしたいというのがこの記事のモチベーションです。なので、「もっといい方法あるよ!」というツッコミをいただけると幸いです。

一つ一つ読み込む例
library(readr)
Cities <- read_csv("./inputs/DataFiles/Cities.csv")
Conference <- read_csv("./inputs/DataFiles/Conference.csv")

list.files関数とread_csv関数を使って一括で読み込む

list.files関数を使って、ファイルのリストを作り、lapply関数を使うことで、一気にデータを読み込むことができる。

list.files関数とread_csv関数を使った例
dataframe_paths <- list.files(path = "./inputs/DataFiles", full.names = T)
dataframe_list <- lapply(dataframe_paths, read_csv)

こうするとそれぞれのファイルから読み込まれたデータがリストdataframe_listの要素として格納される。

このままだとname属性がついていないので、下記のようにすることで名前をつけることもできる。

リストの要素に名前をつける
dataframe_names <- list.files(path = "./inputs/DataFiles", full.names = F) %>% 
  gsub(".csv", "", .) # .csvという文字列を一括で削除する

names(dataframe_list) <- dataframe_names

おそらくこれが一番簡単な方法だと思う。

各データファイルを独立したオブジェクトとして読込みたい

上記の例だと、一つのリストにまとめてデータが格納される。しかし、同じ性格をもったデータファイル(例えばFY2017.csv, FY2018.csv等)であれば一つのリストにまとめて格納したほうが都合がいいが、全然違う性格のデータファイルであれば、それぞれ独立したオブジェクトとして格納したくなる。

assign関数を使う

assign関数を使うことで、データファイルに任意の名前をつけて読み込むことができる。assign関数はassign(objectname, value)という文法で、これは"objectname <- value"に等しい。valueにはベクトルだろうと、データフレームだろうとなんでも入れることができる。というわけで下記のような関数を作ってみた。

assign関数を使った自作関数
assign_data <- function(path, type = "csv"){
  names <- list.files(path = path,
                      full.names = F,
                      pattern = paste0("\\.", type, "$")) %>% 
          gsub(paste0(".",type), "",.)
  paths <- list.files(path = path,
                      full.names = T,
                      pattern = paste0(".", type, "$"))
  for(i in 1:length(names)){
    assign(names[i], read_csv(paths[i]), envir = .GlobalEnv)
  }
}

assign_data("./inputs/DataFiles")

これは引数のpathに任意のフォルダパスを与えると、type(デフォルトはcsv)に引っかかるデータファイルを見つけて、読み込む関数。
namesでオブジェクトにつける名前のリストを作り、pathsで読み込む対象となるファイルのパスのリストを作る。あとはfor文でnamesの(= pathsの)数だけread_csvで読み込んでいく。assign関数のenvir = .GlobalEnvオプションをつける必要があることに注意(関数の中で呼び出しているため)。
一応、他のフォーマット(tsvとか)に対応できるように、第二引数にtypeを取れるようにしたが、ほとんどcsvで提供されることが多いので、csv決め打ちでも良かったかもしれない。

自作関数の応用

例えば上記の./inputs配下にDataFiles以外にもフォルダがある場合に下記のようにすれば一括で読み込むこともできる。

一括でサブフォルダまで含めて格納する
dir_list <- list.dirs(path = "./inputs", recursive = T)
lapply(dir_list, assign_data)

lapplyはリストを返すが、assign_dataは何も返さないので、下記の様にNull値が返ってくる形になるが問題はない。

[[1]]
NULL

[[10]]
NULL

さいごに

他にもeval(parse(text = s))を使った方法とかも試してみたけれども、これが一番簡単だったかなと思う。ただ、自作関数のところでfor文を使わざるをえなかったり、なんかもっと簡単な方法がある気がして仕方がなかったので、ツッコミをいただけると幸いです。

追記

for文を使わない自作関数として下記のようなものを作ってみた

for文使わないパターン
assign_data <- function(path, type = "csv"){
  names <- list.files(path = path,
                      full.names = F,
                      pattern = paste0("\\.", type, "$")) %>% 
          gsub(paste0(".",type), "",.)
  paths <- list.files(path = path,
                      full.names = T,
                      pattern = paste0("\\.", type, "$"))
  lapply(names, function(x){
    assign(x, read_csv(paths[grep(paste0("/",x), paths)]), envir = .GlobalEnv)

}

ところがsystem.timeで処理時間を計測してみたら、for文使ったほうが早かった orz。多分grepとかやってる分だけ余計に時間がかかっているんだろうなぁ… map()とか使ったらうまくいくんだろうか。

パターン ユーザ システム 経過
for文パターン 14.600 1.659 16.546
lapplyパターン 15.236 1.788 17.521

追記2

というわけでmap関数を使ったバージョンを作ってみた、namesとpathsの2つのベクトルを使うので、map2()を使ってみた、が、やっぱり遅い。なぜだorz。あと関係ないけどreadr::read_csvの代わりにdata.table::fread使ったら半分以下の時間で終わった。freadすげぇ。

map関数版
library(purrr)
assign_data <- function(path, type = "csv"){
  names <- list.files(path = path,
                      full.names = F,
                      pattern = paste0("\\.", type, "$")) %>% 
          gsub(paste0(".",type), "",.)
  paths <- list.files(path = path,
                      full.names = T,
                      pattern = paste0("\\.", type, "$"))
  map2(names, paths, ~assign(.x, read_csv(.y), envir = .GlobalEnv))
}
パターン ユーザ システム 経過
for文パターン 14.600 1.659 16.546
lapplyパターン 15.236 1.788 17.521
mapパターン 15.143 1.824 17.450
参考:freadパターン 6.953 0.797 8.502
15
14
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?