R ProjectとRでのファイル操作

RStudioのプロジェクト機能やhere, fsといったパッケージを使ったファイル操作の解説
R
RStudio
fs
here
file operation
Published

August 3, 2024

Rで解析するときには、データの取り込みや解析結果の出力のためにファイルの入出力が伴うことがほとんどだろう。 ファイルを取り扱うためにはファイルパスを適切に指定する必要があるが、 むやみにファイルパスを直接指定しているとエラーの原因になりかねない。

ここでは、そのようなパス操作を安全で楽にするために、 RStudioの機能のひとつであるプロジェクト機能やファイル・パス操作関連の話題を紹介する。

Projectの利用とWorking directoryの設定

Working directory (長いのでWDとする)とはプログラムが動作するディレクトリ(フォルダ)のことで、 プログラムはWDを起点としてファイルなどを探す。 自分が動作させているプログラムがどこをWDとしているのかは、常に意識しておく必要がある。

setwd()関数を使うことでWDを変更することができるが、 むやみにWDを変更することはエラーの原因になりやすいので、使用はなるべく控えたほうがよいだろう。

しかし、解析を始めようとRを起動したときに必要なファイルを集めたディレクトリにWDを設定したほうが解析がやりやすいのは事実である。 そのような時に、Project機能を活用することでWDの設定を簡単に行うことが出来る。

ProjectはRStudioの機能のひとつで、RStudioのメニューから「File / New Project…」を選ぶことで作成ができる。 Projectを作成すると(新しくディレクトリを作るか、既存のディレクトリを設定するかのどちらかで)、 作成したProjectディレクトリの中にプロジェクト名.Rprojというファイルが作成される。 そしてこの.Rprojファイルをダブルクリックすることで、 作成したProjectディレクトリをWDに設定した状態でRStudioを起動することができる。 基本的に.RprojファイルからRStudioを起動するとよい。

このようにすればsetwd()を起動時に使う必要はなくなるので、 あとは原則このWDから動かないようにコードを書くことでWDを移動することに起因するバグを防ぐことができる。

hereパッケージ

RStudioでプロジェクト機能を使っている状態であれば、hereパッケージのhere::here()関数を使うことで、 簡単に.Rprojファイルがあるディレクトリのパスが文字列で得られる。

here::here()
[1] "/Users/t_arae/blog"

ファイル、パスの操作(fsパッケージ)

基本的にfsパッケージの関数を用いる。 WindowsとMacではパスの区切り文字などが異なるが、 fsパッケージを使うことでそのような違いをパッケージ側で対処することができる。

fs::path()を用いて、ファイル・ディレクトリパスを作成するには次のようにする。 一つ目の引数をhere::here()にすることで、Projectディレクトリをまるごと共有する際の面倒を減らすことができる。

fs::path(here::here(), "Desktop", "result.txt")
/Users/t_arae/blog/Desktop/result.txt

fsパッケージでは多くの便利な関数が提供されている。 例えば、fs::dir_ls()fs::path_file()を使って、フォルダ内のcsvファイルをまとめて読み込み、 ファイル名の列を追加してから、1つのdata.frameに結合する操作は、次のように書くことができる。

fs::path(here::here(), "output") |>
  fs::dir_ls(, regexp = ".csv$") |>
  purrr::set_names(nm = ~ fs::path_file(.x)) |>
  purrr::map(readr::read_csv) |>
  purrr::imap(~ dplyr::mutate(.x, file = .y)) |>
  dplyr::bind_rows()
1
csvファイルがあるフォルダ({WD}/output)のパスを作成
2
1のフォルダにあるすべてのcsvファイルのパスのベクトルを取得
3
ベクトルの要素名をファイル名に変更
4
すべてのcsvファイルを読み込む
5
それぞれのdata.frameにファイル名を持つ列を追加
6
すべてのdata.frameを縦に連結(ただしすべてのcsvファイルが同じ列をもっている必要がある)

Rスクリプト内でのパス操作を楽にする

Rで解析した結果(グラフなど)を、解析ごとに分けて特定のディレクトリの中に出力しておきたいことがよくある。 ただし、ファイルを複数出力する時に、何度も出力先のディレクトリまで書いたパスを記述するのはわずらわしい。 here, fsパッケージを活用するとプロジェクト内でのパス操作を楽にすることができる。 私は普段以下のような数行のコードをスクリプトの最初の方に書いておく。

wd <- here::here()                                   # ワーキングディレクトリ
dir_out <- fs::path("data", "output")                # 出力先ディレクトリ
path_out <- function(...) fs::path(wd, dir_out, ...) # パス作成関数を定義
fs::dir_create(path_out())                           # 出力先ディレクトリの作成

2行目の出力先ディレクトリ(dir_out)はスクリプトごとに適当に変更する。

ここでは、作成した複数のグラフやデータを出力したファイルを出力先ディレクトリ以下に保存したいとする。 しかし、何度も出力先ディレクトリまで書いたパスを記述するのは大変だし、 むやみにsetwd()でWDを移動するのはリスクとなる。 この時、3行目で定義したpath_out()関数にファイル名を渡すと、 出力先ディレクトリまでを自動で入力したパスを得ることができる。 これを使えば以下のようにスクリプト内での出力ファイルのパス入力が簡単になる。

path_out()
path_out("important.txt")
path_out("plot", "bar_plot.png")
/Users/t_arae/blog/data/output
/Users/t_arae/blog/data/output/important.txt
/Users/t_arae/blog/data/output/plot/bar_plot.png

参考

A Simpler Way to Find Your Files

Constructs paths to your projects files. Declare the relative path of a file within your project with i_am(). Use the here() function as a drop-in replacement for file.path()', it will always locate the files relative to your project root.

https://here.r-lib.org/

hereパッケージの導入でファイル参照のパス問題の悩みを解消 - cucumber flesh

去年から気になっていたものの、その利点や使い道について理解できていなかったhereパッケージ、ようやくにして少しわかった気がする。今は声を大にして言える。hereは良いぞ。hereをプロジェクトに導入することで、これまでにあったWindows - macOS間でのパス表記の違いや作業ディレクトリの階層性によるパスが正しく指定できないといった課題を解決することができるかもしれない。と言う訳で布教用の記事を書く。また、テスト用のリポジトリも用意しているので、hereを使ってそのご利益を享受されたい方はこちらをcloneなりダウンロードするなりして手前で実行してほしい。 github.com 参考)…

https://uribo.hatenablog.com/entry/2018/01/25/082000

Cross-Platform File System Operations Based on libuv

A cross-platform interface to file system operations, built on top of the libuv C library.

https://fs.r-lib.org/

Sessioninfo

sessionInfo()
R version 4.3.2 (2023-10-31)
Platform: aarch64-apple-darwin20 (64-bit)
Running under: macOS Ventura 13.1

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

time zone: Asia/Tokyo
tzcode source: internal

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
 [1] digest_0.6.34     fastmap_1.1.1     xfun_0.46         knitr_1.48       
 [5] htmltools_0.5.7   rmarkdown_2.25    cli_3.6.3         compiler_4.3.2   
 [9] rprojroot_2.0.4   here_1.0.1        tools_4.3.2       evaluate_0.24.0  
[13] yaml_2.3.9        rlang_1.1.4       jsonlite_1.8.8    crayon_1.5.2     
[17] fs_1.6.3          htmlwidgets_1.6.4