Posted at
RDay 10

Rでplotに渡す...を簡単操作する


概要

R Advent Calendar 2018の10日目の記事です。


  • 自作関数内でellipsis (...)の初期値を決めてplot()関数などに渡したい

  • 簡単に上記ができるパッケージを作ったので,その紹介


ellipsis (...)とは

Rのellipsisとはいわゆる可変長引数のことです。

rbind(...)list(...)などでは,本来の意味通り,任意の個数のオブジェクトを連結したりリストにするために使われます。

また,plot()関数などの描画系関数で,共通のグラフィカルパラメータの受け渡し口になっているところをよく目にします。

いちいちグラフィカルパラメータを列挙しているとうるさいですからね。

その他,総称的関数の親関数で半ば義務的に使われる例があります。

今回は,2番目のケースについて掘り下げ,ellipsisの持つ柔軟性を維持しながらplot()関数などを拡張するためにどうすればよいのか書きます。


plot()関数のカスタマイズ

自作したplot系関数でellipsisの機能を利用するためには,...を自作関数に持たせておき,それを内部のplot()に直接渡します。

plot_log <- function(x, ...) {

log.x <- log(x)
plot(log.x, ...)
}

plot_log(x = 1:100, col = "red")

log(x).png

plot結果を見てみると,xlabがデフォルトのものであったり,ylabで内部の変数名が見えていたりと少し恥ずかしいです。

次に,周期関数を0 ~ 2$\pi$の範囲でプロットする別の自作plot関数を考えてみます。

今度はxlabylabの不満点もケアしたいと思います。

plot_2pi <- function(x, ...) {

plot(x, xlim = c(0, 2 * pi), xlab = "phase", ylab = "amplitude", ...)
}

plot_2pi(sin, col = "red")

sin(x).png

一応見栄えよくプロットできるようになりました。

しかし,この図のxlab, ylabに単位を追加したいだとか,x, sin(x)に変更したいといった場合に少し困ったことになります。

plot_2pi(sin, xlab = "x", ylab = "sin(x)")

Error in plot.function(x, xlim = c(0, 2 * pi), xlab = "phase", ylab = "amplitude",  : 

formal argument "ylab" matched by multiple actual arguments

このように,同じパラメータが何度も指定されていると怒られてしまいます。

自分で使うだけの関数でも,後々にレポートに図を載せることなどを考えると,こういった需要に対応しておきたいところです。

そのためには,...を操作してユーザから指定がないパラメータはデフォルトの値を,指定がある場合はその値を用いるという風に実装できます。

...list()を通すことでリスト化でき,リスト化した引数を使ってplotを呼び出すためにはdo.call()を使います。

plot_2pi_2 <- function(x, ...) {

elp <- list(...)
elp$x <- x
elp$xlim <- c(0, 2 * pi)
if (is.null(elp$xlab)) elp$xlab <- "phase"
if (is.null(elp$ylab)) elp$ylab <- "amplitude"
do.call(plot, elp)
}

plot_2pi_2(cos, col = "red", xlab = "x", ylab = "cos(x)")

cos(x).png

しかし,デフォルトのグラフィカルパラメータを指定したいだけなのに,えらく長いコードになってしまいました。

また,この関数ではxlimをユーザが変更しようとした場合に,警告なしでデフォルトの値に上書きされてしまいます。


elp パッケージ

ということで上のコードをより簡単に書くために,elpパッケージを作りました。


インストール

devtools::install_github("hosscine/elp")

このパッケージを使ってplot_2pi()を書き直してみます。

plot_2pi_3 <- function(x, ...){

elp <- elp::overwrite_elp(..., x = x, xlim = c(0, 2 * pi))
elp <- elp::softwrite_elp(..., append = elp, xlab = "phase", ylab = "amplitude")
do.call(plot, elp)
}

plot_2pi_3(tan, col = "red", ylab = "value", xlim = c(-pi, pi))

tan(x).png

という風に,強制したいパラメータはoverwrite_elp()の方に,ユーザの指定を優先したいパラメータはsoftwrite_elp()の方に書くことで,簡単にデフォルトパラメータを指定できました。

また,この例のようにoverwrite_elp()で指定されたxlimをユーザが指定した場合,

Warning message:

In plot_2pi_3(tan, col = "red", ylab = "value", xlim = c(-pi, pi)) :
arguments xlim is rejected

というように警告が表示されます。

この警告はそれぞれのwarn引数で制御できます。softwrite_elp()でも警告を表示させることができます。


注意点

overwrite_elp()softwrite_elp()を重ねて使う場合は,plot_2pi_3()の例のようにoverwrite_elp()を先に使うようにしてください。無駄に警告が出てくる場合があります。


テンプレート

  elp <- elp::overwrite_elp(..., x = x)

elp <- elp::softwrite_elp(..., append = elp)
do.call(graphics::plot, elp)


おわりに

ellipsis (...)を自作plot系関数に取り入れる方法と,...にデフォルト値を追加する操作の方法を書きました。

また,その操作を簡単に書ける拙作パッケージの紹介をしました。

明日はigjitさんの「Rの入門書について何か書くかも」です。楽しみにさせていただきます。