更新履歴
- 2020-04-03 : version 0.9.3 で内容を見直しました
- 2019-08-16 : version 0.6.0.108 で初稿を上げました
renv とは?
RStudioが開発を進めているRのPackage管理のためのパッケージである
パッケージ管理といえば、Pythonでは新しいパッケージが次々出ており、流れをキャッチアップするだけで一苦労だが、RではこれまでRStudio社のPackrat
くらい?しかなかったように思える(私が知らないだけかもしれないが)
パッケージ管理が必要な理由の1つとして、Rの場合は分析の再現性を担保するためだと思う。とくに、複数人でRプロジェクトをおこなうときに威力を発揮する。また自分の場合、CIでテストを行うときやShiny Appを開発するときにも威力を発揮している
もう1つの理由としては、プロジェクトをまたいで別のバージョンの同じライブラリを使いたいときである。たとえば、あるプロジェクトでは、dplyrの0.7系でがっつりコードを書いてていきなり修正するのが難しい。しかし、あるプロジェクトでは最新のdplyrを使いたい。また、あるプロジェクトでは、dplyrの開発バージョンを使いたいなどである。こうなってくると、1つのライブラリパスで管理することは困難になる
なぜrenv
が開発されているかの背景を理解するためにrenv
のProject pageに書いてあるGoalを見てみる
The goal is for renv to be a robust, stable replacement for the Packrat package, with fewer surprises and better default behaviors.
この一文から、Packrat
を代替しながら、より安定して使いやすいものを目指していることがわかる。Packrat
を使ってるRユーザはどれくらいいるのだろうか。少なくとも自分の周りでPackrat
使ってますという話はほとんど聞かない(自分はそこそこ使ってたほうだと思う)
(Packratの使い道を考えてみたがとても詳しいので、Packratの解説は省略します)
Packrat
との違い
renv
のPackrat
との違いはProject Pageに書かれている。大きいところとしては、renv.lock
というjson形式でRプロジェクトで使用しているパッケージのメタ情報を保存してくれることや、global cacheという仕組みでRプロジェクト間でパッケージを共有できる仕組みがある。そのため、Rプロジェクトを変更するごとに、毎回CRANにパッケージをダウンロードする待ち時間が発生するのはほとんどなくなった点が大きい
global cacheは簡単に言うと、ローカルのhomeディレクトリのような決められた配下にパッケージを保存して、Rプロジェクトで利用するときに、キャッシュがあるなら、そこへのリンク(正式にはシンボリックリンク)を貼ってくれる仕組みである
(以上、つらつらと書いたがよくわからないと思うので、絵としてわかりやすく図解化してくれているくろたんくさんの以下の図が圧倒的にわかりやすいので参考にすると良いです : https://speakerdeck.com/black_tank_top/renv-version-control?slide=13 )
個人的な感想
Packrat
をそこそこ使ってた私の感想としては、global cacheにより待ち時間が減り、デフォルトの挙動が覚えやすいので、使いやすいです。そのため、Rでパッケージ管理したことがない方へも導入のしきいは以前よりも低くなっていると感じます。renvでパッケージ管理にトライしてみましょう!
公式情報
- Github : https://github.com/rstudio/renv
- Project page : https://rstudio.github.io/renv/
- "Using renv with Docker" のArticles読めばDocker Image作るときに楽になると思います
参考情報
renvの有用な記事を随時更新していきます
- 2020-01-30 renv: Project Environments for R : rstudio::conf 2020での発表
- 2019-11-06 renv: Project Environments for R CRANについにアップロードされました
- 2019-10-01 くろたんくさんのrenvによるパッケージ管理(speakerdeck) : 分析再現性担保のためのrenvをつかったパッケージ管理のまとめ資料
- Snapshot and Restore : この記事がrenvの使用の流れを掴むのにわかりやすいです
インストール
> install.packages("renv")
> library(renv)
> print(packageVersion("renv"))
[1] ‘0.9.3’
使い方1 : 同じホストでの作業
普段作業するマシン上での作業の流れは以下です。
-
renv::init()
をする - Rコードを書く
-
renv::snapshot()
をたまにする - 作業を終えたら、
renv::snapshot()
をして、編集したファイルとともにrenv/
とrenv.lock
ファイルらをgit commitする
renv::init
は最初にするだけで、普段はrenv::snapshot()
だけ使うだけ。簡単です
renv::init()
renv
によるパッケージ管理を始めるために使用する
実行すると、renv/
というprivate R libraryの置き場と、.Rprofile
が作成される
.Rprofile
には、source("renv/activate.R")
が記載されており、Rプロジェクト開始とともにrenv
によるプロジェクト管理をスタートする処理が書かれている
* Initializing project ...
* Discovering package dependencies ... Done!
* Copying packages into the cache ... Done!
The following package(s) will be updated in the lockfile:
# CRAN ===============================
- renv [* -> 0.9.3]
* Lockfile written to '~/github/renv-sandbox/renv.lock'.
Restarting R session...
* Project '~/github/renv-sandbox' loaded. [renv 0.9.3]
完了後、.libPaths()
を実行してみると、private R libraryが使われている事がわかる(2つめはよくわかっていない)
[1] "/home/ooki/renv-sandbox/renv/library/R-3.6/x86_64-pc-linux-gnu"
[2] "/tmp/RtmpUk1Icd/renv-system-library"
補足 : global cacheの具体的な動き
このときprivate R library配下にパッケージのソースが置かれるのではなく、global cache配下(Linuxだと~/.local/share/renv
)に置かれ、private R libraryにはそこへのリンクが以下のような感じで置かれていることが確認できる
$ ls -la ~/renv-sandbox/renv/library/R-3.6/x86_64-pc-linux-gnu/
<snip>
lrwxrwxrwx 1 ooki ooki 124 Aug 16 19:24 assertthat -> /home/ooki/.local/share/renv/cache/v4/R-3.6/x86_64-pc-linux-gnu/assertthat/0.2.1/87318c127c936afe4da4d066d1fd01c3/assertthat/
lrwxrwxrwx 1 ooki ooki 122 Aug 16 19:24 backports -> /home/ooki/.local/share/renv/cache/v4/R-3.6/x86_64-pc-linux-gnu/backports/1.1.4/8ed7589b3c92d5dc620c45b5f0909d9f/backports/
lrwxrwxrwx 1 ooki ooki 111 Aug 16 19:23 BH -> /home/ooki/.local/share/renv/cache/v4/R-3.6/x86_64-pc-linux-gnu/BH/1.69.0-1/a3839f6fcfa121ea0b5c4b3a0b42456e/BH/
<snip>
renv::snapshot()
このコマンドにより、プロジェクト配下の何らかのファイル(xxx.R
やyyy.Rmd
)に新たなライブラリが使われていたら、renv.lock
に書き込んでくれる。変更がない場合は、以下のようなメッセージが出力される
> renv::snapshot()
* The lockfile is already up to date.
たとえば、新たにtidyr
パッケージを使うために、install.packages("tidyr")
と実行する。global cacheにすでにtidyr
が存在する場合は、そこへのリンクを貼ってくれるだけ(linked cacheという箇所)なのですぐに完了する
> install.packages("tidyr")
Retrieving 'https://cran.rstudio.com/bin/macosx/el-capitan/contrib/3.6/tidyr_1.0.2.tgz' ...
OK [downloaded 996.5 Kb in 0.5 secs]
Installing tidyr [1.0.2] ...
OK (linked cache)
たとえば、test.R
というファイルにlibrary(tidyr)
と新たに追記。
library(tidyr)
<Rのコードが続く>
この後に再度snapshotを実行すると、tidyr
が使われていることをrenv
が発見してくれて、renv.lock
に書き込んでくれる。
> renv::snapshot()
The following package(s) will be updated in the lockfile:
# CRAN ===============================
- BH [* -> 1.72.0-3]
- R6 [* -> 2.4.1]
- Rcpp [* -> 1.0.4]
- assertthat [* -> 0.2.1]
<snip>
- tidyr [* -> 1.0.2]
- tidyselect [* -> 1.0.0]
- utf8 [* -> 1.1.4]
- vctrs [* -> 0.2.4]
Do you want to proceed? [y/N]: y
* Lockfile written to '~/github/renv-sandbox/renv.lock'.
この辺の発見ロジックは、https://github.com/rstudio/renv/blob/master/R/dependencies.R#L488-L494 あたりのrenv::dependecies()
あたりにより実現されていると思われる
(当時 pacman::p_load()
が renv::dependecies()
に対応していないIssueをあげたのですが、翌日には対応してくれるほどのスピード感だったことに驚いた : https://github.com/rstudio/renv/issues/143 )
では、逆にこのtest.R
からlibrary(tidyr)
を消した(またはコメントアウトをした)あとに、再びsnapshotをするとどうなるかというと、renv.lock
からちゃんと消してくれます。プロジェクトで使用しているライブラリの管理が正確にできそうです
> renv::snapshot()
The following package(s) will be updated in the lockfile:
# CRAN ===============================
- BH [1.72.0-3 -> *]
- R6 [2.4.1 -> *]
- Rcpp [1.0.4 -> *]
- assertthat [0.2.1 -> *]
<snip>
- tidyr [1.0.2 -> *]
- tidyselect [1.0.0 -> *]
- utf8 [1.1.4 -> *]
- vctrs [0.2.4 -> *]
Do you want to proceed? [y/N]: y
* Lockfile written to '~/github/renv-sandbox/renv.lock'.
使い方2 : 異なるホストでの作業
普段作業するマシンとは別のマシン(たとえば、スペックの高い分析サーバが別にあるなど)上での作業の流れは以下です
- git clone したのちに、
renv::restore()
- あとは同じ
renv::restore()
別のマシンまたはCIからRプロジェクトと同じパッケージをインストールするときに使用する
基本的にrenv.lock
ファイルさえあれば動くので、Gitでrenv.lock
を管理しておけばどこからでも同じ環境が再現しやすい
今のところ自分はDocker ImageやCIのまっさらな環境に、renv::restore()
させてから、Testさせるときに使っている
> renv::restore()
The following package(s) will be installed:
_
BH [1.69.0-1]
R6 [2.4.0]
RColorBrewer [1.1-2]
<snip>
Do you want to proceed? [y/N]: y
<snip>
プロジェクトのうち一部のファイルのみをrenvで管理したいときは
デフォルトでは、.gitignore
に準拠してrenvで管理するファイルを決めているが、gitでは管理したいけど、renvでは管理したくないというケースがある。例えば、プロジェクトのすべてのファイルは管理したいけど、試しにアドホックに分析したファイルで読み込んだみたライブラリなどである。そのようなときは、.renvignore
を用意しておけば良い
adhoc_analysis.R
のように書いてから、renv::snapshot()
をすれば、adhoc_analysis.RにあるRライブラリは管理されなくなる
参考 : dependencies.html#ignoring-files
その他コマンド
renv::upgrade
renv自体をupdateしたいときは、renv::upgrade
でversionを指定すれば、renvのupdateが行われ、管理してるrenv.lock
ファイルでも自然と更新される
> renv::upgrade(version = "0.9.2-12")
renv::deactivate()
renv
によるパッケージ管理が不要と思ったら、気軽にdeactivate()をすればOK
> renv::deactivate()
Restarting R session...
もう一度気が変わってrenv
を使いたいとなれば、再度renv::init()
をすると、以前のrenv.lock
をもとに再開するか聞かれるので選択肢から選びましょう
> renv::init()
This project already has a lockfile. What would you like to do?
1: Restore the project from the lockfile.
2: Discard the lockfile and re-initialize the project.
3: Activate the project without snapshotting or installing any packages.
renv::status()
renvによるパッケージの管理状態を知るコマンド(といってもあまり使わない)
> renv::status()
* The project is already synchronized with the lockfile.
renv::dependencies()
プロジェクトにあるソースとパッケージの関係を出力。おそらく、renv::snapshot()
等で使われる
> renv::dependencies()
Finding R package dependencies ... Done!
Source Package Require Version
1 /home/ooki/renv-sandbox/analysis.R dplyr
2 /home/ooki/renv-sandbox/analysis.R ggplot2
3 /home/ooki/renv-sandbox/analysis.R tidyr
4 /home/ooki/renv-sandbox/script.R remotes
5 /home/ooki/renv-sandbox/script.R renv
renv::hydrate()
ProjectのなかでSystem libraryから使われているPackageをprivate R libraryにインストールしてくれる。renv::init()
の中でこれが実行されている模様
> renv::hydrate()
renv::clean()
使ってないライブラリをPrivate R libraryからRemoveしてくれる。たまにお掃除したくなったときに使う
> renv::clean()
renv::migrate()
Packratからrenvに移行するコマンド。使用したことはないが一応あるみたい
renv::settings$ignored.packages()
renv管理から無視するパッケージ(testやdevtoolsなどが該当するかと思う)を設定する
参考 : https://rstudio.github.io/renv/reference/settings.html
> renv::settings$ignored.packages()
character(0)
> renv::settings$ignored.packages("devtools", persist = FALSE)
> renv::settings$ignored.packages()
[1] "devtools"
renv::settings$use.cache(FALSE)
renvのglobal cacheを使わなくする
renv::settings$snapshot.type("simple")
renvのsnapshotの方法を変更する。デフォルトはpackrat
。simple
にしたら、もとのRのユーザライブラリのすべてをrenv.lock
に書き込もうとするようになる
おわりに
Packrat
はあまり周りで使ってる人はいなかったが、複雑なことをしなければrenv
は使い勝手もシンプルでわかりやすい気がします。デフォルトのパッケージ環境を汚さなかったり、分析の再現可能性を担保できることを期待して、Rユーザの中で流行ってほしいなと個人的には思っています。Enjoy Renv!