Heavy Watal

devtools — Rパッケージ作成支援

自分で書いた関数が多くなってきたら、まとめてパッケージを作るとよい。 少しだけ面倒だが、以下のようなメリットがある。

CRANに上げる程ではないとしても、 GitHubに公開しておけば誰でも使えるようになるので、 共同研究者と解析環境を共有したり、 ひとつの論文のワークフローを置いておいたり、いろいろ使い道はある。

Rパッケージ

最低限の作成手順

  1. 開発支援パッケージをインストールする: install.packages(c("devtools", "usethis"))

  2. usethis の関数をいくつか使って骨組みを作る:

    usethis::create_package("hello")
    usethis::use_mit_license()
    usethis::use_roxygen_md()
    usethis::use_package_doc()
  3. devtools::check() で様子を見てみる。 LaTeX 関連で怒られたら足りないパッケージを入れる: sudo tlmgr install inconsolata helvetic

  4. ローカルgitリポジトリを作って最初のコミットをする: usethis::use_git()

  5. GitHubかどこかに空のリポジトリを作る。 パッケージと同名でなくてもよい。

  6. そのリモートリポジトリにプッシュ:

    git remote add origin https::/github.com/heavywatal/rhello.git
    git push -u origin master
  7. とりあえず誰でもインストール可能なパッケージができたはず:

    remotes::install_github("heavywatal/rhello")

ソース構造

https://r-pkgs.org/package-structure-state.html

DESCRIPTION  # 一番大事
NAMESPACE    # 見せるオブジェクトを列挙
README.md    # 全体の説明を簡単に
R/           # Rソースコード
data/        # サンプルデータなど
inst/        # CITATION
man/         # ヘルプファイル.Rd
src/         # C++ソースコード
tests/
vignettes/

CRANから落としてくる .tar.gz ソースコード (bundle package) とは違う。

DESCRIPTION

NAMESPACE

R/ ソースコード

src/ C++ソースコード

Rcppページの”Rパッケージで使う”セクションを参照

vignettes/

個々の関数の使用例はRソースファイルの @examples に書くとして、 複数の関数を組み合わせてどう使うかとか、 パッケージ全体の使い方とかを説明するのがvignettes/の役割。 Rmarkdown形式で書いてHTMLやPDFで表示できるので表現力豊か。

usethis::use_vignette("hello") で雛形を作ってもらうのが楽。

pandocpandoc-citeproc が必要なので Homebrew とかでインストールしておく。

check()がデフォルトでvignette=TRUEかつ処理がやや重いので、 毎回その重さを受け入れるか、わざわざFALSE指定するかというのは悩みどころ。

pkgdownでウェブサイトを構築すると、 ここに置いてあるvignettesはArticlesという位置づけになる。

inst/

ここに入ってるものはインストール先でトップディレクトリに移される謎仕様。

論文で引用されることを想定している場合は inst/CITATION を作る。 citation("ggplot2") のように参照できるようになる。

tests/

testthatパッケージを使うのがデファクトスタンダード。 use_testthat() で初期設定して use_test("somefunction") のようにテストファイルを追加する。 tests/testthat/ 以下のファイル構成は R/ と同じにしておくといい。

さらにcovrパッケージを使って、 ソースコードのうちどれくらいがテストでカバーされてるかを可視化すると良い。

その他

demo/

vignettesに取って代わられた古い機能。 ソースコード*.Rを置いておくとdemo()関数で呼び出せるというだけ。 :check()でソースコードの中身は実行されないが、 demo/00Index というファイルとの整合性が取れてないと警告される。 「デモの名前 + スペース3つ以上かタブ + 適当な説明」という形式。 ファイル名から拡張子を取り除いたものがデモの名前になる。

mydemo1    Description of demo 1
mydemo2    Description of demo 2
exec/

実行可能スクリプトの置き場所。 インストール先はそのままパッケージ内の exec/ (つまり最初からパスが通ってるような場所ではない)。 パーミッションはインストール時に設定してもらえるのでソースツリーでは 644 でOK。 Rscript をshebangで指定することも可能。 例えばRStudioを使いこなせないターミナル勢としては knitr::knit() するだけのコマンド とか作っておくと便利。

tools/

たぶん何を入れても一切チェックされずインストールもされない自由なディレクトリ。 tests/vignettes/ に入れる前のガラクタコードを一時的に置いとくとか。

.Rbuildignore

Rパッケージらしからぬ変なファイルやディレクトリがあると怒られるので、 そういうやつをこのファイルに列挙して無視してもらう。

devtools

骨組みを作るとこからCRANにデプロイするとこまでお世話してくれる。 いろんな専門パッケージの集合体。

主な関数

document(pkg = ".", roclets = NULL, quiet = FALSE)
roxygen2 を呼び出してソースコードから NAMESPACEman/*.Rd を自動生成する。
src/ 内のドキュメント変更はこれ1回の実行では反映されない。
check(pkg = ".", document = NA, ...)
パッケージとしての整合性を確認。 ついでに document() は実行できるけど spell_check() はできないので手動で。
test(pkg = ".", filter = NULL, ...)
testthat を呼び出して tests/ 以下のテストコードを実行する
build(pkg = ".", path = NULL, ...)
R CMD install でインストール可能な tar ball (bundle package) を作る。 Rcppコードをコンパイルするという意味ではない。
install(pkg = ".", reload = TRUE, quick = FALSE, build = !quick, ...)
ローカルにあるソースからインストール。 build = TRUE のとき(デフォルト)、わざわざ bundle package を tempdir() に作ってからそいつでインストールする。
install_github(repo, ref = "master", subdir = NULL, ...)
GitHubリポジトリからインストール。
unload(pkg = ".", quiet = FALSE)
datach("package:XXX") とか unloadNamespace(XXX) よりもちゃんとまっさらにパッケージを外す。
load_all(pkg = ".", reset = TRUE, recompile = FALSE, export_all = TRUE, ...)
install() せずファイルから直接 library() する。 ロード済みでもまず unload() が呼ばれるので安心。
clean_dll(pkg = ".")
src/ 以下に生成される .o, .so を消す。 普段は触る必要ないが、たまにこれが必要な不具合に出くわす。

roxygen2

RソースコードのコメントからNAMESPACEとヘルプ(man/*.Rd)を自動生成する。

roxygen2::roxygenise(package.dir=".", ..., clean=FALSE) を直接呼んでもよいが、 基本的には devtools::document() を使って間接的に利用する。

使い方

#' Title of the simple function to add 1
#'
#' The second paragraph is recognized as the description.
#' It is not recommended to use @@title and @@description explicitly.
#' @param x A numeric vector
#' @export
#' @examples
#' increment(42)
increment = function(x) {x + 1}

タグ

使用可能なタグ一覧は names(roxygen2:::default_tags()) で取得可能。 解説は準備中らしい

@import pkg1, pkg2, ...
NAMESPACEimport() するパッケージを指定。 名前の衝突が怖いので基本的に使わない。
@importFrom pkg func
NAMESPACEimportFrom() するパッケージと関数を指定。 パッケージ内でよほど何回も登場する関数や演算子を登録する。 e.g., @importFrom magrittr %>% 。 重複して何度も書いちゃっても大丈夫。
@export
NAMESPACEexport() する関数を指定。 一般ユーザーからはこれが付いてる関数だけ見える。
@param arg1 description...
関数の引数。型や役割の説明を書く。
@inheritParams package::function
引数の記述を別の関数から継承する。 その関数に必要で @param が無いものだけいい感じに補ってくれる。
@noRd と組み合わせて使えればいろいろ楽できそうだけどダメっぽい。
@template template-name
man-roxygen/template-name.R の内容を利用する。 @inheritParams と違って、その関数で使用しないparamまで展開されちゃうのが欠点。
@eval fun()
関数を評価して出てきた文字列ベクタをroxygenコメントとして処理する。 各要素の先頭は #' ではなく@タグで。 関数には引数も渡せるのでいろいろできる。
@return description
関数の返り値。
@examples code...
例となるコードを記述する。 exportしないやつには書いてはいけない。 \dontrun{} に入れるとチェックから除外される。 単数形の @example は外部ファイルのパスを受け取る。
@rdname basename
man/に書き出すRdファイルの名前。 複数の関数で同じものを指定すればヘルプをひとつにまとめられる。 このときタイトルや @param などは共有される。 似たような引数を持つ関数や、関連する関数をまとめるのによく使う。
@include other-file.R
指定したファイルを先に読み込む。 メソッドの定義などで順序が重要になる場合に使う。
@seealso ...
[mean()], [ggplot2::diamonds], <https://...> のようにしてリンクを張れる。
@family
これを共通して持つ関数同士に @seealso が自動生成される。 ただし @rdname とかで関数をまとめたりしてるとうまくいかないっぽい。
@section Some Title:
新しいセクションを作る。前には空行、行末にコロン、後には普通の文章。
@docType, @name
パッケージやデータを記述するのに必要だったが今では不要っぽい。

関連書籍