Rでデータ解析の再現性を保つためには、入力データと解析をしたコードをきちんと管理しておくのと同時に、 解析を行ったRの環境を再現可能にしておく必要がある。
この記事ではR自体のバージョン管理を行うrig
と、 インストールしたRパッケージのバージョン管理を行うrenv
パッケージの簡単な解説を行う。
GitHub - r-lib/rig: The R Installation Manager
The R Installation Manager. Contribute to r-lib/rig development by creating an account on GitHub.
https://github.com/r-lib/rig
Project Environments
A dependency management toolkit for R. Using renv, you can create and manage project-local R libraries, save the state of these libraries to a lockfile, and later restore your library as required. Together, these tools can help make your projects more isolated, portable, and reproducible.
https://rstudio.github.io/renv
Rのバージョン管理(rig
)
まずはRのバージョン管理をするモチベーションについて考えてみる。
Rはバージョンが変わるごとに、性能が上がったり、バグが修正されたり、機能が追加されたりする。 新たな機能の追加について考えてみると例えば、R 4.0からデフォルトのカラーパレットが変更されたり、 R 4.1から新たにパイプ演算子(|>
)が追加されたりしている。 また、Biocoductorの各バージョンはR本体のバージョンと連動しており、 例えばBioconductor 3.19には正式にはR 4.4が必要となる。
このように新しいRの機能を試してみたり、あるいは新しく使用したいソフトウェアの要求から、 R本体のバージョンをアップデートしたいことがある。 一方で、R本体のバージョンを変更することで以前のバージョンでは動いていたプログラムが動作しなくなったり、 プロジェクトごとにRのバージョンを変更したいといった場合には、 R本体のバージョンを手軽に元に戻したり、切り替えたりする必要がある。 しかしながら、自分の手で複数のRのバージョンを切り替えるのはなかなか難しい。
R自体のバージョン管理を行う方法はいくつかあり、例えばdockerなどのコンテナ仮想化技術を使う方法などがある。 ここではより手軽にできる比較的新しい方法として、R専用のインストールマネージャーであるrig
を紹介する。 rig
を使うことで複数のバージョンのRをインストールし、必要に応じてRのバージョンを切り替えることが可能になる。
rig
はまだ安定版ではない(記事の執筆時点でv0.7.0)ので、多少不具合が起きる可能性がある。
rig --version
RIG -- The R Installation Manager 0.7.0
rig
はRustというプログラミング言語で書かれたコマンドラインツールで、 ターミナルなどのコマンドラインからrig
コマンドを実行してR自体の管理を行う。 すでにRをインストールしている場合、そのRの管理をrig
で行うことはできないので、あらかじめ入っているRをアンインストールしておく。
rig
は多くのサブコマンドを提供している。rig --help
を実行することでサブコマンドの一覧や用例を見ることができる。
rig --help
Name:
rig - manage R installations
Description:
rig manages your R installations, on macOS, Windows, and Linux. It can
install and set up multiple versions of R, and make sure that they work
together.
On macOS, R versions installed by rig do not interfere. You can run
multiple versions at the same time. rig also makes sure that packages
are installed into a user package library, so reinstalling R will not
wipe out your installed packages.
rig is currently experimental and is a work in progress. Feedback is much
appreciated. See https://github.com/r-lib/rig for bug reports.
Usage: rig [OPTIONS] [COMMAND]
Commands:
sysreqs Manage R-related system libraries and tools (experimental)
default Print or set default R version [alias: switch]
list List installed R versions [alias: ls]
add Install a new R version [alias: install]
rm Remove R versions [aliases: del, remove, delete]
system Manage current installations
resolve Resolve a symbolic R version
rstudio Start RStudio with specified R version
library Manage package libraries [alias: lib] (experimental)
available List R versions available to install.
run Run R, an R script or an R project
help Print this message or the help of the given subcommand(s)
Options:
-q, --quiet Suppress output (overrides `--verbose`)
-v, --verbose... Verbose output
--json Output JSON
-h, --help Print help
-V, --version Print version
Examples:
# Add the latest development snapshot
rig add devel
# Add the latest release
rig add release
# Install specific version
rig add 4.1.2
# Install latest version within a minor branch
rig add 4.1
# List installed versions
rig list
# Set default version
rig default 4.0
Rのインストール(rig available
, rig add
)
rig available
で利用可能なRのバージョン一覧を見ることができる。 rig available --help
でこのサブコマンドのヘルプメッセージを確認することができ、 例えば rig available --all
ですべての利用可能なRのバージョンを確認したりできる。
rig available --all
name version release date type
------------------------------------------
4.1.0 4.1.0 2021-05-18 release
4.1.1 4.1.1 2021-08-10 release
4.1.2 4.1.2 2021-11-01 release
4.1.3 4.1.3 2022-03-10 release
4.2.0 4.2.0 2022-04-22 release
4.2.1 4.2.1 2022-06-23 release
4.2.2 4.2.2 2022-10-31 release
4.2.3 4.2.3 2023-03-15 release
4.3.0 4.3.0 2023-04-21 release
4.3.1 4.3.1 2023-06-16 release
4.3.2 4.3.2 2023-10-31 release
4.3.3 4.3.3 2024-02-29 release
4.4.0 4.4.0 2024-04-24 release
4.4.1 4.4.1 2024-06-14 release
next 4.4.1 patched
devel 4.5.0 devel
rig add {name}
を実行すると{name}
で指定したRをインストールすることができる。 rig add --help
でこのサブコマンドの詳細が確認できる。
rig add 4.4.1
インストールしたRの切り替え(rig list
, rig default
)
rig list
を実行するとインストールしたRの一覧を見ることができる。
rig list
* name version aliases
------------------------------------------
4.1-arm64 (R 4.1.3)
4.2-arm64 (R 4.2.1)
* 4.3-arm64 (R 4.3.2)
4.4-arm64 (R 4.4.1)
左にアスタリスク(*
)がついているRが現在有効化(コマンドラインでR
と打って起動するもの)されているバージョンを示している。
R --version
R version 4.3.2 (2023-10-31) -- "Eye Holes"
Copyright (C) 2023 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin20 (64-bit)
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.
複数のバージョンのRをインストールしている場合、rig default {name}
を実行することで、 インストールしたRの中で有効化するRのバージョンを切り替えることができる。 rig default
でバージョンを切り替えたあとで、rig list
を実行すると有効化されているRが切り替わっていることがわかる。
rig default 4.4-arm64
rig list
* name version aliases
------------------------------------------
4.1-arm64 (R 4.1.3)
4.2-arm64 (R 4.2.1)
4.3-arm64 (R 4.3.2)
* 4.4-arm64 (R 4.4.1)
R --version
WARNING: ignoring environment value of R_HOME
R version 4.4.1 (2024-06-14) -- "Race for Your Life"
Copyright (C) 2024 The R Foundation for Statistical Computing
Platform: aarch64-apple-darwin20
R is free software and comes with ABSOLUTELY NO WARRANTY.
You are welcome to redistribute it under the terms of the
GNU General Public License versions 2 or 3.
For more information about these matters see
https://www.gnu.org/licenses/.
Rの解析環境の管理(renv
)
R本体のバージョン管理はrig
を利用することでできる様になった。 では、Rのパッケージ管理は何を使ったら良いだろうか? renv
は以下に挙げるような解析プロジェクトでよく必要となる条件に対して、 カッコで示すような便利な機能を提供し、条件を満たしてくれるRのパッケージである。
- プロジェクトごとに異なるRのバージョン、異なるバージョンのパッケージを使いたい(仮想環境の提供)
- プロジェクトごとに使用しているR、パッケージのバージョンを記録しておきたい(バージョン管理)
- プロジェクトを共有した他の人や別のPCにおいても、プロジェクトのRの環境を再現したい(解析環境の再現)
プロジェクトの仮想環境の初期化(renv::init()
)
プロジェクトごとに異なるバージョンのパッケージを使いたい場合、 そのプロジェクトだけで有効になる仮想的なRの環境を構築する必要がある。 renv::init()
関数を使って、プロジェクトごとの仮想環境を初期構築をすることができる。
まずは、練習用のプロジェクトのディレクトリ(./test-renv/
)を作って、magrittr
パッケージを使うコードを書いたRのスクリプトファイル(./test-renv/scripts/use_magrittr.R
)を追加する。
<- function(...) fs::path("test-renv", ...)
path_temp ::dir_create(path_temp("scripts"))
fswriteLines(text = "library(magrittr)", con = path_temp("scripts", "use_magrittr.R"))
::dir_tree(path_temp(), recurse = 2, all = TRUE) fs
test-renv
└── scripts
└── use_magrittr.R
次にrenv::init()
関数を使ってプロジェクトの初期化を行う。 renv::init()
関数を実行すると、プロジェクト内にあるスクリプトを解析して、 スクリプト内で使われているRのパッケージが記録される。 プロジェクト内にスクリプトがない場合は、起動時のR本体のバージョンと、 renv
のバージョンだけが記録される。 また、bare = TRUE
を設定すると初期化時のバージョンの記録はスキップされる。
::init(project = path_temp(), restart = TRUE) renv
- Linking packages into the project library ... Done!
The following package(s) will be updated in the lockfile:
# CRAN -----------------------------------------------------------------------
- magrittr [* -> 2.0.3]
- renv [* -> 1.0.3]
The version of R recorded in the lockfile will be updated:
- R [* -> 4.3.2]
- Lockfile written to "~/blog/posts/2024/2024-09-14-rig-renv-intro/test-renv/renv.lock".
# 作成したプロジェクトの有効化
::load(project = path_temp())
renv# 現在のプロジェクトのディレクトリを確認
::project() renv
[1] "/Users/t_arae/blog/posts/2024/2024-09-14-rig-renv-intro/test-renv"
# 初期化によって、プロジェクトディレクトリ内にいくつかのものが作られている。
::dir_tree(path_temp(), recurse = 2, all = TRUE) fs
test-renv
├── .Rprofile
├── renv
│ ├── .gitignore
│ ├── activate.R
│ ├── library
│ │ └── R-4.3
│ └── settings.json
├── renv.lock
└── scripts
└── use_magrittr.R
初期化すると、プロジェクトのディレクトリ内に新たにrenv/
ディレクトリとrenv.lock
, .Rprofile
というファイルが作成される。それぞれの役割は以下の通り。
renv.lock
ファイルにはR本体のバージョンとプロジェクトで使用するパッケージの情報が記録されるrenv/
ディレクトリは仮想化に必要なコードとプロジェクトで使われるパッケージが含まれる.Rprofile
はRが起動した際に自動で読み込まれる特別なRのスクリプトファイル
プロジェクトディレクトリでRを起動すると、まず .Rprofile
が自動で読み込まれ、 その中でさらに仮想環境を有効化するためのコードが書かれたrenv/activate.R
を読み込むようになっている。
renv/activate.R
内のコードが実行されると、パッケージを読み込む場所がrenv/library/
以下に設定され、 結果としてプロジェクトごとに異なるバージョンのパッケージを利用することが可能となる。
プロジェクトの初期化には、renv::init()
。
パッケージ情報の更新(renv::status()
, renv::snapshot()
)
プロジェクトで新しいパッケージを使用し始めたり、パッケージをアップデートすると、 記録しているバージョン情報も更新する必要がある。
renv
では、プロジェクト内で利用できる(インストールされている)パッケージの情報が、 renv.lock
ファイルに記録されている情報と一致しているかどうかなどの バージョン情報の確認を行ったり、renv.lock
ファイルの情報を更新する機能がある。
プロジェクト内の現在のバージョン情報の状況を確認するには、renv::status()
を実行する。 問題がなければ以下のようなメッセージが出る。
::status() renv
No issues found -- the project is in a consistent state.
では、新しくmarkdown
パッケージを使うRのスクリプトをプロジェクトに保存してみよう。
# 新たに`markdown`パッケージを使うコードが書かれたRのスクリプトファイルを追加
writeLines(text = "library(markdown)", con = path_temp("scripts", "use_markdown.R"))
この状態で、renv::status()
を実行すると新たに追加されたスクリプトファイルが解析されて、 現在の状態の問題点が報告される。
# `markdown`パッケージが使われているがインストールも記録もされていない旨のメッセージが出る
::status() renv
The following package(s) are missing:
package installed recorded used
markdown n n y
See ?renv::status() for advice on resolving these issues.
メッセージが出たmarkdown
パッケージをインストールして、再度renv::status()
を実行してみる。
# `markdown`パッケージをインストールする
install.packages("markdown")
The following package(s) will be installed:
- commonmark [1.9.1]
- markdown [1.13]
- xfun [0.46]
These packages will be installed into "~/blog/posts/2024/2024-09-14-rig-renv-intro/test-renv/renv/library/R-4.3/aarch64-apple-darwin20".
# Installing packages --------------------------------------------------------
- Installing commonmark ... OK [linked from cache]
- Installing xfun ... OK [linked from cache]
- Installing markdown ... OK [linked from cache]
Successfully installed 3 packages in 6.1 milliseconds.
# もう一度、`renv::status()`を実行
# インストールはされているが`renv.lock`ファイルに記録されていない
::status() renv
The following package(s) are in an inconsistent state:
package installed recorded used
commonmark y n y
markdown y n y
xfun y n y
See ?renv::status() for advice on resolving these issues.
必要なパッケージがインストールできたところで、パッケージの情報をrenv.lock
ファイルに記録するには、 renv::snapshot()
を実行する。
# `renv::snapshot()`を実行して、`renv.lock`にバージョンを記録する
::snapshot() renv
The following package(s) will be updated in the lockfile:
# CRAN -----------------------------------------------------------------------
- commonmark [* -> 1.9.1]
- xfun [* -> 0.46]
# RSPM -----------------------------------------------------------------------
- markdown [* -> 1.13]
- Lockfile written to "~/blog/posts/2024/2024-09-14-rig-renv-intro/test-renv/renv.lock".
# もう一度、`renv::status()`を実行
# 今度は問題ない旨のメッセージが出る
::status() renv
No issues found -- the project is in a consistent state.
このようにして、renv::status()
を実行してパッケージの齟齬がある場合には、 その不具合を解消した上で、renv::snapshot()
を実行していくことで プロジェクトの環境のバージョン情報を更新していくことができる。
バージョン情報の更新は、renv::status()
で状態を確認しつつ、定期的にrenv::snapshot()
。
プロジェクト環境の再現(renv::restore()
)
ここまでの手順でプロジェクト固有の仮想環境を構築し、 環境内の状態(R本体とパッケージのバージョン)を記録することができる様になった。 では、この環境の状態を他の場所でも再現するにはどうしたら良いだろうか。
すべてのバージョンの情報はrenv.lock
ファイルに保存されているはずなので、 このrenv.lock
ファイルと解析に使っているRのスクリプトファイルがあれば再現できる様になっていると良い。 この作業は、renv::restore()
関数で行うことができる。
それでは、先ほどまでの作業で構築したRの仮想環境を一度破棄して、 新しい作業用ディレクトリで同じ環境が構築できるか試してみよう。
まずは、renv.lock
ファイルとscripts/
ディレクトリをひとつ上のディレクトリに退避(コピー)してから、 ディレクトリを消しておく。
通常はこのようにプロジェクトのディレクトリを削除することは当然しないので、 実際の自分のプロジェクトディレクトリでは真似しない様に
# 解析環境を再現するために、`renv.lock`ファイルをコピー
::file_copy(path_temp("renv.lock"), path_temp("../renv.lock"))
fs::dir_copy(path_temp("scripts/"), path_temp("../scripts/"))
fs
# 元のディレクトリに戻って、テスト用のディレクトリを消す
::deactivate(clean = TRUE)
renv::dir_delete(path_temp()) fs
Code
# 前の環境でロードされた`renv`をunloadしておかないと、`renv:::renv_imbue_self()`でエラーが出る。
unloadNamespace(asNamespace("renv"))
まずは前準備として、新しく環境再現用のプロジェクトディレクトリ(re-test-renv/
)を作成し、 退避していたrenv.lock
とscripts/
を新しいプロジェクトディレクトリに移動する
# 再現用の仮想環境を作るテスト用のディレクトリを作って、Working directoryを移動する
<- function(...) fs::path("re-test-renv", ...)
path_temp2 ::dir_create(path_temp2())
fs
# 退避していた`renv.lock`と`scripts/`を新しいプロジェクトディレクトリに移動する
::file_move(path_temp2("../renv.lock"), path_temp2("renv.lock"))
fs::file_move(path_temp2("../scripts"), path_temp2("scripts"))
fs
::dir_tree(path_temp2(), recurse = 2, all = TRUE) fs
re-test-renv
├── renv.lock
└── scripts
├── use_magrittr.R
└── use_markdown.R
# 仮想環境を初期化して、`renv.lock`ファイルから使用していた環境を復元する
::init(project = path_temp2(), bare = TRUE, restart = TRUE)
renv::load(project = path_temp2()) renv
- None of the packages recorded in the lockfile are currently installed.
# 現在の仮想環境のディレクトリが移動していることを確認
::project() renv
[1] "/Users/t_arae/blog/posts/2024/2024-09-14-rig-renv-intro/re-test-renv"
現在の解析環境の状態をrenv::status()
で確認してみよう。
::status() renv
The following package(s) are missing:
package installed recorded used
commonmark n y ?
magrittr n y y
markdown n y y
xfun n y ?
See ?renv::status() for advice on resolving these issues.
renv.lock
ファイルに記録されているインストールされるべきパッケージは、 いまのところまだ一つもインストールされていないことがわかる。
これらのパッケージをインストールするにはrenv::restore()
を使用する。
# `renv.lock`ファイルからプロジェクトに使っていたパッケージをインストール
::restore(lockfile = path_temp2("renv.lock")) renv
The following package(s) will be updated:
# CRAN -----------------------------------------------------------------------
- commonmark [* -> 1.9.1]
- magrittr [* -> 2.0.3]
- xfun [* -> 0.46]
# RSPM -----------------------------------------------------------------------
- markdown [* -> 1.13]
# Installing packages --------------------------------------------------------
- Installing commonmark ... OK [linked from cache]
- Installing magrittr ... OK [linked from cache]
- Installing xfun ... OK [linked from cache]
- Installing markdown ... OK [linked from cache]
メッセージから、test-renv/
で使用していたパッケージと同じバージョンのパッケージがインストールされたことが確認できる。
では、再構築した仮想環境の状態を再確認してみよう。
::status() renv
No issues found -- the project is in a consistent state.
必要なパッケージが正しいバージョンで全てインストールされているため、 問題が見つからなかった旨のメッセージが出力された。
Code
# 終わったので消しておく
::deactivate(clean = TRUE)
renv::dir_delete(path_temp2()) fs
プロジェクト環境の再構築はrenv::restore()
を使う。
まとめ
R自体の管理はrig
、プロジェクトごとの環境の管理はrenv
を使ってみよう。