複数のggplot2要素を関数で渡したい

**(2016/03/15追記)ggplot2 v2.1.0で動作確認しました。**

## Q

複数のggplot2の要素を加えたいのですが、以下のように手動でやるとうまくいきます:

```r:manual_example.R
require(ggplot2)
p <- ggplot(aes(x = mpg, y = hp), data = mtcars)
p + geom_vline(xintercept = 20) + geom_point(data = mtcars)
```
![manual_exsample.R-1.png](https://qiita-image-store.s3.amazonaws.com/0/35118/479c53c3-af0d-dd22-22bc-0cd975a077ee.png)

でも、これ関数に持たせようと思って以下のようにしてみるとエラーがでます:

```r:NG_example.R
myFunction <- function() {
  return(
    geom_vline(xintercept = 20) + geom_point(data = mtcars)
  )
}
p <- ggplot(aes(x = mpg, y = hp), data = mtcars)
# p + myFunction()
# ここで以下のエラーがでます:
# "エラー: 二項演算子の引数が数値ではありません"
```

ggplot2に関数の戻り値で複数の要素を放り込むにはどうしたらいいでしょうか?

## A

ggplot2は要素の「リスト」をサポートしています:

```r:OK_example.R
myFunction <- function()
 list(geom_vline(xintercept = 20),
      geom_point(data = mtcars))

p <- ggplot(aes(x = mpg, y = hp), data = mtcars)
p + myFunction()
```
![OK_example.R-1.png](https://qiita-image-store.s3.amazonaws.com/0/35118/f20ab9e6-8412-d788-e14e-7e1a1140ff5a.png)

このように各要素をリストとしてまとめ、"+"で追加するとうまく組み込まれます。このgeom_*以外にも、"+"で束ねることができるものであれば同様にlistとして渡すことができます。

またこれを利用すれば、事前にggplot2の要素をまとめておいてデータセットだけ切り替えるなど、非常に応用がききます。

## 応用

### 要素のリストを使いまわす
「ggplot2は要素のリストをサポートする」ことを応用すれば、以下のようなことができます:


```r:layer_example.R
lay = list(stat_summary(fun.y = mean, geom = "line"),
           stat_summary(fun.data = mean_se))

ggplot(mtcars, aes(am, mpg, colour = factor(vs), group=factor(vs))) + lay
ggplot(iris, aes(Species, Sepal.Length, group=1)) + lay
```
![layer_example.R-1.png](https://qiita-image-store.s3.amazonaws.com/0/35118/108f94cd-10db-dd6d-04d5-d8d1c58e24bd.png)
![layer_example.R-2.png](https://qiita-image-store.s3.amazonaws.com/0/35118/71ed5b6b-f347-f854-0005-cac60ba4ba30.png)

このコードでは、「折れ線グラフでxの各点における標準誤差も描く」というのを繰り返し利用するため、それらを表現するggplot2要素をlayというlistに束ねています。こうすることで、"+ lay"と追加することで簡単に描くことができます。

### 繰り返しを関数にして、lapplyでリストで受け取る
また、繰り返し処理を行いたいときは、lapplyで記述することも可能です:


```r:repeat_example.R
p <- ggplot(iris, aes(y=Sepal.Width, x=Species)) +
  stat_summary(fun.y=mean, geom = "bar") + 
  ylim(0,6) + 
  lapply(1:3, function(i) geom_segment(x=i-0.4, y=7-i, xend=i+0.4, yend=7-i))
p
```
![repeat_example.R-1.png](https://qiita-image-store.s3.amazonaws.com/0/35118/dd2bf5d2-250d-db7f-0af9-6d59497c63fd.png)

繰り返し処理をしたい場合、`for(i in 1:3)`などで実行するとうまくいきません。ですが、繰り返したい処理を関数にしてlapplyで実行するようにすれば、返り値はlistになるのでちゃんと要素の内容が当たるようになります。

### mapplyで複数の引数がある関数を繰り返す
もし複数の引数が必要ならば、mapplyを利用する方法もあります:


```r:mapply_example.R
p = ggplot(data.frame(x = c(-10, 10)), aes(x)) +
  mapply(function(m, s, co) stat_function(fun = dnorm, args = list(mean = m, sd = s), colour = co), 
         -1:1, c(0.5, 1, 1.5), rainbow(3))
p
```
![mapply_example.R-1.png](https://qiita-image-store.s3.amazonaws.com/0/35118/f646a265-ab16-0ef0-750e-8d822a16c09c.png)

mapplyは第一引数の関数に対し、それ以降の引数を当てて実行し、その結果をlistで返してきます。なので上の例のように、パラメータを変化させながら重ね書きする時などはきっと重宝するでしょう。

これらはほんの一例ですので、ぜひ色々試してみてください。

## 参考資料

この記事は、以下のstackoverflowの内容を参考に書き起こしました:

- [r - How can I combine multiple ggplot2 elements into the return of a function? - Stack Overflow](http://stackoverflow.com/questions/4835332/how-can-i-combine-multiple-ggplot2-elements-into-the-return-of-a-function)