library(magrittr)
R4.1.0からbaseパッケージにパイプ演算子|>
が追加されました。
これまでmagrittr
パッケージの%>%
で実現していたことを、base Rで書けるようになったのは嬉しいことです。 ただし、両者には微妙な違いがありますのでそれをご紹介します。
magrittr
パッケージを呼び出しておきましょう。
magrittrのパイプは右辺の表現を前もって評価する
magrittr
の%>%
では、右辺をnon-standard evaluationで評価するのでtidyverseらしく拡張された記述が可能です。 単純な例で言うと、引数が1つだけならば関数f
の名前だけでもうまく動きます。
%>% f x
これがbase
の|>
だと、Rが認識できる関数の形にするためにカッコが必要になります。
|> f() x
このx |> f()
はf(x)
とまったく同じなのに対して、x %>% f
は先にf
が何なのかを判定してからf(x)
が実行されています。 つまりそこには2段階のステップが内包されているということです。 それはsys.calls
などを使うと確認できます。
<- function(x){ print(sys.calls()) }
f <- 1
x
%>% f x
[[1]]
x %>% f
[[2]]
f(.)
|> f() x
[[1]]
f(x)
このように|>
は1段階で実行されていますが、%>%
は2段階で実行されていることが分かります。
環境(environment)に依存した処理に注意
パイプで結んだf(x)
の前に1ステップあるか否かは、呼び出し元の環境を変更するときなどに影響してきます。 例えばassign
を使って変数を作成する場合を考えましょう。assign
は通常は「現在の環境」に変数を作成します。
assign("y", 2)
y
[1] 2
これは「assign
が呼び出された環境」に変数を作成しているとも言えます。 base
の|>
を使った場合はグローバル環境(通常の環境)から直接関数を呼び出したのと同じになるため、assign
は想定どおり動きます。
3 |> assign("y", value=_)
y
[1] 3
ここで、セットしたい値はassign
の2番目の引数に渡すので、パイプでつなぐためにR4.2.0から実装されたplaceholderの_
を使いました。
y
の値が3に更新されたことが分かります。しかしmagrittr
の%>%
では異なった動きをします。
# yを削除
rm("y")
4 %>% assign("y", .)
y
Error in eval(expr, envir, enclos): object 'y' not found
変数yが作成されません。先ほどの%>%
の2段階ステップのうちassign
は2段階目で実行されます。 つまりassign
の呼び出し元は1段階目のステップになります。(ここまでステップと呼んでいるものはRの中ではframeと言います)
1段階目の環境は一時的なもので、処理が終わると消えてしまいます。 そこに作られた変数yは、一緒に消えてしまったというわけです。
そこで、一時的な環境でなくグローバル環境に変数を作成するには、それを明示的に指定することが必要になります。
# グローバル環境(.GlobalEnv)を引数に追加
4 %>% assign("y", ., pos=.GlobalEnv)
y
[1] 4
今度はちゃんと変数yが作成されました。
このようにmagrittr
とbase
のパイプには微妙な違いがあります。 ただ、それが問題になるような場面はほとんどないと思うので、あまり気にする必要はないでしょう。