niszetの日記

アナログCMOS系雑用エンジニアが頑張る備忘録系日記

(R) パッケージ化のタイミングは難しい

進捗日記

さて、最初に書いておくとすれば、

データにあわせて関数を書くな。関数にあわせてデータを整形しろ

です。

さて。前回

niszet.hatenablog.com

と書いたものの、パッケージ化するタイミングは実に難しいものだ、ということを感じました。ちょっと見せ方を変えようとしたとき、既存の関数を拡張するだけで良ければよいのですが、そうならない場合は安易に関数を追加したりするものです。結果、大量の「なんか似ているんだけど微妙に違う関数」や「色々あってどこからも使われなくなった関数」などが増えていきます。

だもんで、色々と見直して統合したり、消去していったりしていました。テストを書いてはいたものの、全部を網羅していないので結局ドキドキしながらパッケージをビルドして、ドキュメントを書いている側のプロジェクトでRのセッションを再度立ち上げてknitしていました。やれやれ…。

コピペが多いとはいえ、ドキュメント側がコードだけで1000行、パッケージはもうちょっとある規模感で、図が2桁枚ある(し、今後条件とともに増えていく)ので、パッケージ化自体は必須で、どこまで共有・共通化してドキュメント作成のコストを下げるのかというところが勝負になってきました(実際、図表生成はテンプレートに条件名を入れて実行するだけになっているのでknitの実行時間のみでドキュメント生成は出来ている。)

色々

とりあえず、色々とりとめなく書きます。

htmlかwordかで図の見栄えが変わるのを避けたい。

社内的にはドキュメントはwordでと言われることが多いので、こちらの記事を参考にやってみました。

qiita.com

が、上記の通り図表が多く、途中からそれぞれに対して条件で分岐するのは現実的ではなくなってしまい、元に戻しました。html出力の場合とword出力の場合で図の見え方が違うんですよね…。

そこで、ggsave()で図を一旦保存し、knitr::include_graphics()によってそれをドキュメントに埋め込むこととしました。これで両ドキュメントでの図の見栄えの差がなくなること、図を使いまわしたい時に既にデータがそろっている状態になること、などでメリットがありました。実際は適当なラッパー関数を作って、保存場所やサイズなどの制御はお任せしています。 幅6、高さ4でインチ単位にすると普段html出力で見ている図に近いかな。標準設定がいくつなのかは見つけられず。

facet_wrapは関数の中で使わない

これは、どの変数で展開したいのか?が変わることがあるので、コアになるggplot()の関数のラッパーからggplotオブジェクトを返してもらい、呼び出し側のRmdの中で+ facet_wrap()として作図する、というものです。コレに限らず、パッケージ中に含める関数は出来るだけシンプルで最小限にして、設定もデフォルト値を持たせつつも引数で変更できるようにしておく方が良いですね。もちろん、完全に仕様が固まったあとなら良いのですが、ドキュメント書いている間はね…。

htmlのタグを埋め込むとか

🐘さんに教えていただきました。

これでRのコードからhtmlのタグが生成できたのですが、たとえばh3などのタグを作ってもtocには含まれません。pander::pander()で生成するとtocに含まれましたが、この場合同一のチャンクに入っているモノがなぜか全てtocに表示されるということで、こちらは今は使っていません(いずれ挙動がわかったら使うかも)

何をしたいかといえば、条件を振ったときの結果の図を列挙する際に、値を変数で置き換えて次々と表示していきたいのですね。そのためにはhtmlのコードですらRで生成しなくてはいけない。

が、現時点ではforやapplyなどを使ってこれらの関数で生成しようとしても意図した動きとなりませんでした。

これについては子Rmdを生成しておき、knit_child()を使うと良さそう??というSOのやり取りがありました。

stackoverflow.com

近々やってみる予定ですが、子Rmdを生成するための関数やそれらのドキュメント要素の管理(ディレクトリ階層構造)などを決めておかないとまたリファクタリング()祭りですよ…。

不要な変数はrmで消しておけ

これは、変数名を間違えて3時間を溶かした男の物語である…。

いや、語るほどのことはなかった。変数はチャンクをまたいで存在してしまうので、そのチャンクで一時的に作った(つもりの)変数は、チャンクを抜ける前に消しておけってことです。マジで。

グローバルに使う変数はlistにまとめるとか

これは一部パッケージ側でやっていますが、Rmd中で使用する変数はひとところにまとめておいた方が良いですね。シングルトンとかそういうかっこいいものじゃなくて、単なるリストでOK。
本当は、これを拡張して前処理関数とかもここに持たせるべきだろうなーと思っている。パッケージから生成されたインスタンス的なイメージなんだけども、イマイチつかみ切れていない。課題。

betweenは両端を含む。

範囲選択にbetweenを使用しますが、この子、両端の値を含みます。まぁそらそうか?って気もしますが。
複数の領域に分けたいなーって時に、between(hoge, 1,2), between(hoge, 2,3)と書くと、hogeが2の場合はそれぞれの条件にヒットしてしまいます。私はこれで3時間(略

愚直に、片方は以上、もう一方は未満になるような条件文で選択するなどかな。ほかに良い方法ありますかねぇ…。ワカラングで聞いてみようかな。

numericでjoinするな

これも定番かもしれませんが…。特に、計算した結果が一致するだろ…という気持ちで、その値を使ってjoinするのダメ🙅
変換前の値をつかって、%in%などで選択するとか、characterにするとか、かな。「だいたい一致する値」でjoinは出来ないからな。私はこれで4時間(略

関数に複数の機能を持たせるな。

たとえば、「plotする、saveオプションがあれば画像を保存する」など。これは↑にも書いたようにggplotオブジェクトを返す関数とggsaveのラッパーにわけました。saveに関するステータスを返したいが、関数内部でplotしている場合はそちらのステータスも見たい。あるいはggplotオブジェクトを返したい、など要求が複数考えられるとき、関数に機能を持たせすぎだな、ということに気づくのです。

この場合、与えられた引数からggplotオブジェクトを作成する、ggplotオブジェクトを表示する、ggplotオブジェクトを画像として保存する、に分けられるというわけですね。真ん中のは画像を表示する、という機能に変わりましたが。

そんなわけで順調にR力の高まりを感じる日々を過ごしております。

あんまりR関係ないね…?

この辺りまとめると、前処理初心者あるあるが作れそうですねー・・・

あれ?パッケージの話じゃなかったっけ…??

Enjoy!!