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

# 概要
[R Advent Calendar 2018](https://qiita.com/advent-calendar/2018/rlang)の10日目の記事です。

- 自作関数内でellipsis (`...`)の初期値を決めて`plot()`関数などに渡したい
- 簡単に上記ができるパッケージを作ったので，その紹介

# ellipsis (`...`)とは

Rのellipsisとはいわゆる可変長引数のことです。
`rbind(...)`や`list(...)`などでは，本来の意味通り，任意の個数のオブジェクトを連結したりリストにするために使われます。

**また，`plot()`関数などの描画系関数で，共通のグラフィカルパラメータの受け渡し口になっているところをよく目にします。**
いちいちグラフィカルパラメータを列挙しているとうるさいですからね。

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

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

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

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

```r
plot_log <- function(x, ...) {
  log.x <- log(x)
  plot(log.x, ...)
}
```
```r
plot_log(x = 1:100, col = "red")
```

![log(x).png](https://qiita-image-store.s3.amazonaws.com/0/322296/4438e182-3417-1aa0-dfd0-f7bfdca7e05e.png)

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

次に，周期関数を0 ~ 2$\pi$の範囲でプロットする別の自作plot関数を考えてみます。
今度は`xlab`，`ylab`の不満点もケアしたいと思います。

```r
plot_2pi <- function(x, ...) {
  plot(x, xlim = c(0, 2 * pi), xlab = "phase", ylab = "amplitude", ...)
}
```
```r
plot_2pi(sin, col = "red")
```

![sin(x).png](https://qiita-image-store.s3.amazonaws.com/0/322296/3c20be35-bc63-42d5-dfe8-abe1e232f919.png)

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

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

```r
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()`を使います。

```r
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)
}
```
```r
plot_2pi_2(cos, col = "red", xlab = "x", ylab = "cos(x)")
```

![cos(x).png](https://qiita-image-store.s3.amazonaws.com/0/322296/ca3cf771-a72e-c1f2-6eb0-8df92dad722f.png)

しかし，デフォルトのグラフィカルパラメータを指定したいだけなのに，**えらく長いコード**になってしまいました。
また，この関数では`xlim`をユーザが変更しようとした場合に，**警告なしでデフォルトの値に上書き**されてしまいます。

# elp パッケージ

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

#### インストール

```r
devtools::install_github("hosscine/elp")
```

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

```r
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)
}
```
```r
plot_2pi_3(tan, col = "red", ylab = "value", xlim = c(-pi, pi))
```

![tan(x).png](https://qiita-image-store.s3.amazonaws.com/0/322296/34bacd2c-73e9-e988-d489-73a223f97b48.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()`を先に使うようにしてください。**無駄に警告が出てくる場合があります。

## テンプレート
```r
  elp <- elp::overwrite_elp(..., x = x)
  elp <- elp::softwrite_elp(..., append = elp)
  do.call(graphics::plot, elp)
```

# おわりに

ellipsis (`...`)を自作plot系関数に取り入れる方法と，`...`にデフォルト値を追加する操作の方法を書きました。
また，その操作を簡単に書ける拙作パッケージの紹介をしました。

**明日は[igjit](https://qiita.com/igjit)さんの「Rの入門書について何か書くかも」です。楽しみにさせていただきます。**