しおしお おしお

びぼーろくなど

R言語:dir()で取得したフォルダパスを、階層ごとに区切ってDataFrameに格納する

ファイル/フォルダのフルパスを取得した際、予め階層ごとに区切って持っておく*1と、『特定の階層にいる、あるファイル』なんて指定して集計する作業*2が捗ります。

基本は、dir()で取得したフルパスを\で区切ってからDataFrame化するのですが、
そこで一つ躓いたので備忘録がてらに以下メモ
(…車輪の再発明でないことを祈ります)

参考:当方の環境(sessionInfo())

まったくバージョン上げてない

> sessionInfo()
R version 3.4.2 (2017-09-28)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows >= 8 x64 (build 9200)

やりたいこと

たとえば、こんな感じのディレクトリに対して、C:以下の全ファイル/フォルダのフルパスをまるっと取ってくる

C:
├hogehoge01
│├fugafuga01_01
││├piyopiyo01_01_01
│││└pitcture01_01_01_01.png
││└memo01_01_01.txt
│├fugafuga01_02
│└memo01_01.txt
├hogehoge02
│└memo02_01.txt
├hogehoge03
│└fugafuga03_01
│  └pitcture03_01.png
└pitcture01.png 

そんで取ってきたフルパスに対して、階層ごとに区切ってDataFrameに格納することを考える

dir01 dir02 dir03 dir04
hogehoge01
hogehoge01 fugafuga01_01
hogehoge01 fugafuga01_01 piyopiyo01_01_01
hogehoge01 fugafuga01_01 piyopiyo01_01_01 pitcture01_01_01_01.png
hogehoge01 fugafuga01_01 memo01_01_01.txt
hogehoge01 fugafuga01_02
hogehoge01 memo01_01.txt
hogehoge02
hogehoge02 memo02_01.txt
hogehoge03
hogehoge03 fugafuga03_01
hogehoge03 fugafuga03_01 pitcture03_01.png
pitcture01.png

(※合っているか不安)

やったこと(Bad tip)

まず、指定階層以下のフォルダ/ファイル一覧を、何も考えずにdir()で取ってきてDataFrame化するとこうなる

コード

D_path <- "C:"
FileLists <- dir(path = D_path, recursive = T)
FileLists <- as.data.frame(FileLists)
FileName <- strsplit(as.character(FileLists$FileLists), "\\/")

DF_result <- do.call(rbind, FileName)
DF_result <- as.data.frame(DF_result)

出力結果(※イメージ)

V1 V2 V3 V4
hogehoge01 hogehoge01 hogehoge01 hogehoge01
hogehoge01 fugafuga01_01 hogehoge01 fugafuga01_01
hogehoge01 fugafuga01_01 piyopiyo01_01_01 hogehoge01
hogehoge01 fugafuga01_01 piyopiyo01_01_01 pitcture01_01_01_01.png
hogehoge01 fugafuga01_01 memo01_01_01.txt hogehoge01
hogehoge01 fugafuga01_02 hogehoge01 fugafuga01_02
hogehoge01 memo01_01.txt hogehoge01 memo01_01.txt
hogehoge02 hogehoge02 hogehoge02 hogehoge02
hogehoge02 memo02_01.txt hogehoge02 memo02_01.txt
hogehoge03 hogehoge03 hogehoge03 hogehoge03
hogehoge03 fugafuga03_01 hogehoge03 fugafuga03_01
hogehoge03 fugafuga03_01 pitcture03_01.png hogehoge03
pitcture01.png pitcture01.png pitcture01.png pitcture01.png

階層が一番深いファイルについては、想定した通り、各階層ごとに列がわかれてDataFrameに格納されている
しかし、階層が浅いファイル/フォルダでは、階層のない部分にも値が入ってしまっている
本当ならば、階層の浅いファイル/フォルダについては、以降の階層(DataFrameの列)にはNAが入っていてほしい

どうにかならんか

長さの違うリストを平和に結合する方法、少しググってみると、こんな感じの答えが出た
stackoverflow.com
状況としてはこの人と似ている(dir()の結果をstrsplit()に入れたときの出力は、長さの違うvectorのlistになっている*3 )
発生している現象も、これに近い

どうやら、長さの違うvectorをcbind()やrbind()で結合しようとすると、短い要素の足りない部分については、存在している部分を繰り返して埋める動きをするっぽい

解決策として、2番目の回答のqpcR::cbind.na()がスマートでよさげだったけど、qpcR::rbind.na()はないため断念
1番目の回答の主旨である、『短い方ベクトルの空いているところにNAを入れれば、cbindできるよ(超意訳)』が参考になりそう

やったこと(Better? tip)

そこで思いついたのが以下の方法

コード

D_path <- "C:\\hogehoge"
FileLists <- dir(path = D_path, recursive = T)
FileLists <- as.data.frame(FileLists)
FileName <- strsplit(as.character(FileLists$FileLists), "\\/")

Tmp <- lapply(FileName, function(x){c(x, rep(NA, (5-length(x))))})
DF_result <- do.call(rbind, Tmp)
DF_result <- as.data.frame(DF_result)

出力結果(※イメージ)

やったー!それっぽいのができたー!!

V1 V2 V3 V4 V5
hogehoge01 NA NA NA NA
hogehoge01 fugafuga01_01 NA NA NA
hogehoge01 fugafuga01_01 piyopiyo01_01_01 NA NA
hogehoge01 fugafuga01_01 piyopiyo01_01_01 pitcture01_01_01_01.png NA
hogehoge01 fugafuga01_01 memo01_01_01.txt NA NA
hogehoge01 fugafuga01_02 NA NA NA
hogehoge01 memo01_01.txt NA NA NA
hogehoge02 NA NA NA NA
hogehoge02 memo02_01.txt NA NA NA
hogehoge03 NA NA NA NA
hogehoge03 fugafuga03_01 NA NA NA
hogehoge03 fugafuga03_01 pitcture03_01.png NA NA
pitcture01.png NA NA NA NA

やったこと要約

srtsplit()で分割した結果(listに入ったvector)に対し、(5-そのvectorの要素数)個だけNAをくっつけ、do.call()でrbind()するだけ!!
※5としているところは階層の深さに応じて適当な数を入れる

かなーり不格好ではありますが、集計かけるときにはna.omit()なりで除外してやれば、(とりあえずは)普通に集計できる

言い訳

本当はrep(NA, (5-length(x)))の5としているところを、list内のvectorの最大サイズから持ってこれるとスマートなんだろうけど、list型はどうも苦手*4なので断念。
集計かける分には(ひとまず)困っていないので、とりあえずはこれで。

もしstrsplit()の返り値がDataFrameならば、以下の手段が使えたのでもっとラクスマートになったんだろうな
stackoverflow.com
[R] How to rbind list of vectors with unequal vector lengths?
R: Combining vectors or data frames of unequal length into one data&nbsp;frameryouready.wordpress.com


本当はこのあとddply()使って集計かけたんだけど、今日はひとまずここまで。*5

#R言語は個人的には扱いやすくて好きなのですが、毎回list型の扱いには難儀してます…

*1:要はDataFrame

*2:例:保存日時で階層化されたログデータについて、それらの有無を日付別で確認/集計する等

*3:例えばis.vector(DF_result[1])はTRUE

*4:私が書こうとすると、これだけで新たなfor文が生成されてしまいそう

*5:ddply関連でもつまづきがあったのですが、それについてはまた後日