R で関数のデフォルト引数を変更する(魔改造編) #rstatsj

  • 3
    Like
  • 0
    Comment
More than 1 year has passed since last update.

先日、「R で関数のデフォルト引数を変更する」という記事をポストしたのですが、R に管理されたい皆さまに置かれましては、まさかあれで終わりだなんて思ってないですよね?
そう、この話はここからが本番です。
今日は R の関数のデフォルト引数を恒久的に固定する方法を紹介します。

やりたいこと

前回の記事では、formals() によって関数のデフォルト引数を変更しました。

R
formals(rnorm)$mean <- 100
rnorm(1)
結果
[1] 100.5635

この方法は、単にデフォルト値を変更しているだけなので、引数を指定して呼び出すことによって、デフォルト値を無効にできます。

R
rnorm(1, mean = 0)
結果
[1] 0.4243097

これはこれでいいのですが、今回は、このように引数を指定して呼び出しても、デフォルト値が固定されたままにしたい。

なんでそんなことしたいの?

たまに、使用しているパッケージがバグっていることってありますよね?
先日、rstan パッケージを使っていたら、traceplot() 関数が動かないというバグに遭遇しました。

R
traceplot(stanfit)
結果
if (debug) { でエラー:  引数の長さが 0 です 
Error in if (debug) { : argument is of length zero

この原因を調べてみると、traceplot() から呼び出されている ggplot2 の内部関数 titleGrob()debug 引数に NULL が渡されていることが原因のようです。
ggplot2 は最近メジャーバージョンが上がって 2.0.0 となり、大幅な変更があったようですが、それに rstan が対応できていないということらしいです。

この debug 引数は、デバッグ時に使用するもののようなので、基本的に FALSE で大丈夫のはずです。
なので、debug = FALSE と固定したいのですが、titleGrob() 関数は、traceplot() の内部深くにおいて debug 引数を指定して呼び出されているため、formals() でデフォルト値を変更しただけでは、このバグを解決できません。

このバグに対処するには、titleGrob() 関数の debug 引数を恒久的に FALSE に固定する必要があります。

魔改造

というわけで、titleGrob() を魔改造します。
アイデアとしては、新しく my_titleGrob() 関数を作り、その内部で debug=FASE を指定して titleGrob() 関数を呼び出します。
そして、その my_titleGrob() 関数を、もともとの titleGrob() 関数と置き換えます。
ポイントだけ押さえたコードは下記のようになります。

R
original_titleGrob <- ggplot2:::titleGrob
my_titleGrob <- function(label, x, y, ...(省略)..., debug = NULL) {
  original_titleGrob(label=label, x=x, y=y, ...(省略)..., debug = FALSE)
}
ggplot2:::titleGrob <- my_titleGrob

ただし、R には色々な制約があって、このコードはこのままでは動きません。
実際に実行可能なコードは下記のようになります。

R
original_titleGrob <- ggplot2:::titleGrob
my_titleGrob <- function (label, x, y, hjust, vjust, angle = 0, gp = gpar(), 
                          margin = NULL, expand_x = FALSE, expand_y = FALSE, debug = NULL) {
  original_titleGrob(label, x, y, hjust, vjust, angle, gp, 
                     margin, expand_x, expand_y, debug = FALSE)
}
ns <- asNamespace("ggplot2")
unlockBinding("titleGrob", env = ns)
assign("titleGrob", my_titleGrob, envir = ns)
lockBinding("titleGrob", env = ns)

これを実行したあとで、traceplot(stanfit) を行うと、うまくいくことを確認しました。

一般の関数への適用

さて、このように関数の引数の値を恒久的に固定するというのを、任意の関数に適用するにはどうすればいいでしょうか?

やり方はあるんですが、説明するのが面倒くさいので、パッケージを作りました。

インストール方法はいつもの通りです。

R
install.packages("devtools")
devtools::install_github("hoxo-m/fixer")

このパッケージを使って、上記で説明した ggplot2titleGrob() 関数の debug 引数を FALSE に固定するには、

R
fix_params_persist(ggplot2:::titleGrob(debug = FALSE))

こんな感じでできます。

最初に例で出した、rnorm()mean を 100 に固定するというのをやってみましょう。

R
fix_params_persist(rnorm(mean=100))
rnorm(1, mean=0)
結果
[1] 99.09647

というわけで、mean 引数に何を指定しようが、mean = 100 に固定されるようになりました。

まとめ

R のパッケージのバグに遭遇したとき、魔改造で乗り切ろうとするようになったら、もう末期ですね。
みなさんはマネしないようにしましょう!

Enjoy!