老化によるset.seed忘れを防ぎたい

Rひとりアドベントカレンダー1日目。

set.seed()は乱数の種を設定する関数ということはよく知られている。 乱数の種を設定することで、乱数に再現性がとれるようになる。 例えば以下の2行のコードはまとめて実行することで毎回同じ結果を返す。

> set.seed(1)
> rnorm(5)
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078

だがここでもう一回rnorm()を実行したらどうなるだろう。 なんとなく同じrnorm(5)の結果が得られるように思えないだろうか。 答えはブーである。

> rnorm(5)
[1] -0.8204684  0.4874291  0.7383247  0.5757814 -0.3053884

同じrnorm(5)の結果を得るにはset.seed(1)を再び実行する必要がある。

> set.seed(1)
> rnorm(5)
[1] -0.6264538  0.1836433 -0.8356286  1.5952808  0.3295078

ハハッ、こんなん当たり前っしょとか思ってる人、dplyr::sample_n()を利用する際もちゃんとset.seed()を併せて実行しているだろうか。

set.seed(1)
result <- iris %>% sample_n()

こんなの絶対にset.seed()の実行忘れる自信がある。10回中1回は絶対忘れる。俺は日々老化している。

ということで、dplyrのイシューにもsample_n()のオプションにseed入れたほうがよくね?という提案がある。却下されているが。

github.com

却下されている理由としては、汎用的なwithr::with_seed()があるからというものである。

使い方はこんな感じ。

library(withr)
> with_seed(1, iris %>% sample_n(5))
  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1          5.1         3.4          1.5         0.2     setosa
2          5.7         2.8          4.5         1.3 versicolor
3          5.4         3.0          4.5         1.5 versicolor
4          6.3         2.8          5.1         1.5  virginica
5          4.7         3.2          1.6         0.2     setosa
> with_seed(1, iris %>% sample_n(5))
  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1          5.1         3.4          1.5         0.2     setosa
2          5.7         2.8          4.5         1.3 versicolor
3          5.4         3.0          4.5         1.5 versicolor
4          6.3         2.8          5.1         1.5  virginica
5          4.7         3.2          1.6         0.2     setosa

確かにこうしておけば、seedとの一体感が生まれるし何より実行忘れがない。 そしてwith_preserve_seed()を使えば、いちいち指定しなくても最初にset.seed()したシードで統一できる。

library(withr)
> set.seed(1)
> with_preserve_seed(iris %>% sample_n(5))
  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1          5.1         3.4          1.5         0.2     setosa
2          5.7         2.8          4.5         1.3 versicolor
3          5.4         3.0          4.5         1.5 versicolor
4          6.3         2.8          5.1         1.5  virginica
5          4.7         3.2          1.6         0.2     setosa
> with_preserve_seed(iris %>% sample_n(5))
  Sepal.Length Sepal.Width Petal.Length Petal.Width    Species
1          5.1         3.4          1.5         0.2     setosa
2          5.7         2.8          4.5         1.3 versicolor
3          5.4         3.0          4.5         1.5 versicolor
4          6.3         2.8          5.1         1.5  virginica
5          4.7         3.2          1.6         0.2     setosa

今後はwith_seed()そしてwith_preserve_seed()を使っていくことにする。

どうかな。