library(magrittr)R4.1.0からbaseパッケージにパイプ演算子|>が追加されました。
これまでmagrittrパッケージの%>%で実現していたことを、base Rで書けるようになったのは嬉しいことです。 ただし、両者には微妙な違いがありますのでそれをご紹介します。
magrittrパッケージを呼び出しておきましょう。
magrittrのパイプは右辺の表現を前もって評価する
magrittrの%>%では、右辺をnon-standard evaluationで評価するのでtidyverseらしく拡張された記述が可能です。 単純な例で言うと、引数が1つだけならば関数fの名前だけでもうまく動きます。
x %>% fこれがbaseの|>だと、Rが認識できる関数の形にするためにカッコが必要になります。
x |> f()このx |> f()はf(x)とまったく同じなのに対して、x %>% fは先にfが何なのかを判定してからf(x)が実行されています。 つまりそこには2段階のステップが内包されているということです。 それはsys.callsなどを使うと確認できます。
f <- function(x){ print(sys.calls()) }
x <- 1
x %>% f[[1]]
x %>% f
[[2]]
f(.)
x |> f()[[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", .)
yError: 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のパイプには微妙な違いがあります。 ただ、それが問題になるような場面はほとんどないと思うので、あまり気にする必要はないでしょう。