まだ厨二病

RとPyhtonを使ったデータ分析・統計解析らへんの話題をしていくだけ

?Rパッケージ開発時に利用するデータの種類とその使い分け

昨日、Rコミュニティの質問広場兼お気楽な交流の場であるr-wakalangでこんな質問が寄せられた。

(意訳) パッケージの開発時にオブジェクトを保存しておいて、そのオブジェクトをパッケージ内の関数で利用したい

パッと思いつく回答として、data/ディレクトリに.rdaファイルを保存すればいいのでは?と思ったけどこれは厳密には正しくない。というのも、dataディレクトリに保存するのはパッケージの呼び出しとともに参照可能なオブジェクトになってしまう(後述)。

ふーむ、というわけで改めてHadleyの"R Packages"を見直してみたらきちんとした説明があった。完全な勉強不足だった。というわけでこの辺の情報を整理しておきたい。今回の話は、そのほとんどが Data · R packagesに書かれているものだ。より詳しく知りたい方は"R Packages"を読むと良い。インターネットで全部閲覧できるGitHubからリポジトリをクローンしてきて、pandocでPDFを生成しても良い。ちなみに、"R Packages"は日本語訳が出たらしい。私は持っていないがきちんと解説されていると思うので、そちらを見るという手段もある。

Rパッケージ開発入門 ―テスト、文書化、コード共有の手法を学ぶ

Rパッケージ開発入門 ―テスト、文書化、コード共有の手法を学ぶ

? Rパッケージで使われるデータの種類

さて、Rパッケージで使用するデータの種類として、主に次の3つのパターンが考えられる(このほかにはtestやvignettes用とか)。

  1. Exported data: パッケージの利用者が使用可能とするデータ
  2. Internal data: 保存しておきたいが、利用者には触れさせたくないデータ、オブジェクト
  3. Raw data: その他のテキストデータや、Rとは関係のないバイナリファイル

今回の場合では、関数内で使用するオブジェクトとしたいというので2に該当する。ではこれらのパターンごとにどのように対処したら良いかというのを次に見ていく。

Exported data: 外部で利用されるデータ

1のパターン。パッケージを利用するユーザーが利用できるように提供するデータはdata/フォルダに置かれる。data()関数で呼び出されるデータはこれに当たる。dataフォルダには.rdataもしくは.rdaファイル(data()関数で呼び出すことを想定する)を置く。これらのファイルに保存されたRオブジェクトはパッケージの呼び出しとともに利用可能になる。あるいは、data()関数のpackage引数で明示的に呼び出すことで利用できる。基本的には一つのファイルに一つのオブジェクトを保存する。

実例をあげるとこんな感じ。

nasa
# Error: object 'nasa' not found
library(dplyr)
nasa
# Source: local array [41,472 x 4] D: lat
# [dbl, 24] D: long [dbl, 24] D: month
# [int, 12] D: year [int, 6] M: cloudhigh
# [dbl[24,24,12,6]] M: cloudlow
# [dbl[24,24,12,6]] M: cloudmid
# [dbl[24,24,12,6]] M: ozone
# [dbl[24,24,12,6]] M: pressure
# [dbl[24,24,12,6]] M: surftemp
# [dbl[24,24,12,6]] M: temperature
# [dbl[24,24,12,6]]

あるいは、

# 新しいセッションを立ち上げる
nasa
# Error: object 'nasa' not found
data("nasa", package = "dplyr")
nasa
# Source: local array [41,472 x 4] D: lat
# [dbl, 24] D: long [dbl, 24] D: month
# [int, 12] D: year [int, 6] M: cloudhigh
# [dbl[24,24,12,6]] M: cloudlow
# [dbl[24,24,12,6]] M: cloudmid
# [dbl[24,24,12,6]] M: ozone
# [dbl[24,24,12,6]] M: pressure
# [dbl[24,24,12,6]] M: surftemp
# [dbl[24,24,12,6]] M: temperature
# [dbl[24,24,12,6]]

で呼び出しても良い。これはdplyr/data/nasa.rdaというバイナリファイルを呼び出していることになる。

こうしたデータを作るには、目的のオブジェクトを生成して、save()関数あるいは{devtools}パッケージのuse_data()関数で保存する。なおuse_data()では、.rdaファイルとしてdataディレクトリ下に自動的に保存される。

# 利用したいオブジェクトを作る
demo_df <- iris
# save(demo_df, file =
# 'data/demo_df.rda')
devtools::use_data(demo_df)
# Saving demo_df as demo_df.rda to
# /Users/uri/mypkg/data

ここに保存したdemo_df.rdaというファイルの内容は、ビルド・インストールしたパッケージで上の例のようにdata()関数を使ったり、パッケージ読み込み後にユーザーが利用可能になる(data("demo_df", package = "mypkg"))。

細かいところだと、DESCRIPTIONのLazyDataフィールドでFALSEにするとパッケージを読み込んだだけではdataディレクトリに保存したオブジェクトを利用できなくなる(なのでHadleyはTRUEにしとけよっ、って言ってる(意訳))。

また、これらのデータはexportされるので、きちんとドキュメントを書く必要がある。

Internal data: 内部で利用されるデータ(オブジェクト)

今度はパッケージ利用者には触れてほしくないデータ、つまり開発時やパッケージで用いる関数の処理などでのみ使いたいデータ(オブジェクト)の場合である。今度は複数のオブジェクトであっても構わない。

こういうデータは関数を記述したRコードを保存するRディレクトリと同じ階層に"sysdata.rda"という名称で保存する。

絶対にsysdata.rdaという名称で保存しろよ、絶対だぞ!... 誤ったファイル名で保存することを防ぐために先のパターンでも使用したdevtools::use_data()関数のinternal引数でTRUEを与えると、自動的にR/sysdata.rdaに保存される。

# 関数内で利用したいオブジェクトを作っておく
a <- 1:3
b <- list(a = 1:4, b = letters[1:3])
demo_df <- iris
# Internal dataにする場合は引数internal =
# TRUEとする
devtools::use_data(a, b, demo_df, internal = TRUE)
# Saving a, b, demo_df as sysdata.rda as
# sysdata.rda to /Users/uri/mypkg/R

ここで保存したオブジェクトはパッケージ内の関数で参照できるオブジェクトとなり、ユーザーは利用できない(mypkg::demo_dfなどのように名前空間を指定すれば呼び出せる)。また、ユーザーが利用することを想定していないので、Exported dataと違ってドキュメントを用意する必要はない。

data-rawディレクトリを活用する

さて最後のパターンを説明する前にdata-rawディレクトリを活用すると良いよ、という話をしておく。

これまで扱ってきた.rdaというファイルはバイナリファイルであって、バージョン管理システムを使っていてもその構造や変化を把握しにくい。また、データの値が更新された際には新たにオブジェクトを作り直す必要がある。そのためオブジェクトを生成する過程を記録しておきたい。そこでdata-rawというディレクトリを新たにもうけ、そこにオブジェクトの生成から保存までの過程を記録しておくと再現性が向上するので良いという話。

devtools::use_data_raw()でディレクトリを作ってくれるので、あとはオブジェクトを作るまでの工程をRファイルとしてdata-rawディレクトリに保存していく。

devtools::use_data_raw()
# Creating data-raw/ Next: * Add data
# creation scripts in data-raw * Use
# devtools::use_data() to add data to
# package

Raw data: その他のテキストデータや、Rとは関係のないバイナリファイルなど

Rは実にさまざまな種類のデータの読み込みが可能となっている。そういった専用データの読み込み関数の利用例として、パッケージ開発者がデモ用のファイルを添付しておきたい、ということがある。そうしたファイルはinst/ディレクトリに保存しておく。このディレクトリに保存されたファイルは、パッケージがインストールされると、instディレクトリの構造を反映してパッケージインストール先のディレクトリの直下に置かれることになる。

instディレクトリに保存され、インストールされたパッケージ内のファイルは次のようにして参照することができる。

system.file("test.csv", package = "mypkg")
# [1]
# '/Library/Frameworks/R.framework/Versions/3.2/Resources/library/mypkg/test.csv'

こうした機構を利用した例として、いくつかのパッケージを上げておく

? 所感

何はともあれ、実際のパッケージの構造を見てみるのが良いと思う。インストールしたパッケージではディレクトリ構造が異なる場合があるので、GitHubなんかにあるものを見てみることを勧める。シリパクマスターの道は遠い。

広告を非表示にする