プログラミング
Rにおけるパイプ演算子の歴史:配管工、連鎖、そして有名な画家たち
Plumbers, chains, and famous painters: The history of the pipe operator in R (adolfoalvarez.cl)
要約
この記事は、R言語におけるパイプ演算子の進化の歴史を追っています。2012年のStack Overflowでの質問から始まり、初期のカスタム関数、dplyrパッケージの`chain`関数と`%.%`演算子、そしてmagrittrパッケージの`%>%`演算子の登場を経て、最終的にR 4.1.0で標準機能として導入された`|>`演算子に至るまでを解説しています。各段階でのコード例や開発者の貢献にも触れています。
全文翻訳
注:この記事は、以前のバージョンのサイトに公開した2014年の別の記事を更新したものです。
私たちの物語は、他の多くのR関連の物語と同様に、Stack Overflowで始まります。
2012年1月17日、匿名のユーザー「user4」が次のような質問をしました。「F#のフォワードパイプ演算子をRで実装するにはどうすればよいですか?」この演算子により、一連の計算を簡単に連鎖させることができます。例えば、入力データがあり、関数fooとbarを順番に呼び出したい場合、次のように書くことができます。
data |> foo |> bar
Ben Bolkerは同日回答し、Rにおける最初のパイプと見なせるものを提示しました。
"%>=" <- function(x,f) do.call(f,list(x))
pi %>% sin %>% cos
## [1] 1
しかし、Rパッケージにおけるパイプ関数の最初の正式な導入は、Peter Meilstrupが「さまざまなデータ操作およびプログラミングユーティリティ」を集めるためのパッケージptoolsを開始した2012年4月5日に行われたと考えられます。そのユーティリティの1つがchain関数で、引数をパイプする方法を実装していました。このパッケージは後にvadrに改名され、chain関数は現在でも見つけることができます。
dplyr
しかしすぐに、パイプの使用の普及を促進したパッケージの1つが作成されることになります。2012年10月28日、Hadley Wickhamは、彼のデータ分析パッケージplyr(当初は「plyr2」と呼ばれていました)の進化としてdplyrプロジェクトをGitHubで開始しました。元々、dplyrパッケージにはchain関数は組み込まれていませんでしたが、これは約1年後の2013年10月9日に変更され、dplyrに最初のパイプが登場しました。この関数もchainと名付けられましたが、パッケージはパイプ演算子として`%.%`を導入しました。chain関数の導入の背後にある考えは、データフレームに複数の関数を適用するための表記を単純化することでした。chain関数がない場合、dplyrでは動詞を内側から外側へ読む必要がありました。
library(hflights)
library(dplyr)
filter( summarise( select( group_by(hflights, Year, Month, DayofMonth), Year:DayofMonth, ArrDelay, DepDelay ), arr = mean(ArrDelay, na.rm = TRUE), dep = mean(DepDelay, na.rm = TRUE) ), arr > 30 | dep > 30 )
## # A tibble: 14 × 5
## # Groups: Year, Month [10]
## Year Month DayofMonth arr dep
## <int> <int> <int> <dbl> <dbl>
## 1 2011 2 4 44.1 47.2
## 2 2011 3 3 35.1 38.2
## 3 2011 3 14 46.6 36.1
## 4 2011 4 4 38.7 27.9
## 5 2011 4 25 37.8 22.3
## 6 2011 5 12 69.5 64.5
## 7 2011 5 20 37.0 26.6
## 8 2011 6 22 65.5 62.3
## 9 2011 7 29 29.6 31.9
## 10 2011 9 29 39.2 32.5
## 11 2011 10 9 61.9 59.5
## 12 2011 11 15 43.7 39.2
## 13 2011 12 29 26.3 30.8
## 14 2011 12 31 46.5 54.2
しかし、chain関数が導入されると、まず次のように変更されました。
chain(
hflights,
group_by(Year, Month, DayofMonth),
select(Year:DayofMonth, ArrDelay, DepDelay),
summarise( arr = mean(ArrDelay, na.rm = TRUE), dep = mean(DepDelay, na.rm = TRUE) ),
filter(arr > 30 | dep > 30)
)
そして`%.%`パイプを使用すると次のようになります。
hflights %.% group_by(Year, Month, DayofMonth) %.% select(Year:DayofMonth, ArrDelay, DepDelay) %.% summarise( arr = mean(ArrDelay, na.rm = TRUE), dep = mean(DepDelay, na.rm = TRUE) ) %.% filter(arr > 30 | dep > 30)
magrittr
しかし、`%.%`パイプはdplyrパッケージに長く留まることはありませんでした。2013年12月29日、Stefan Bacheは古いStack Overflowの質問を再訪し、元の回答の代替案を提案しました。
「どうせなら `%>%` <- function(e1, e2) {
cl <- match.call()
e <- do.call(substitute, list(cl[[3]], list(. = cl[[2]])))
eval(e)
}
これにより、次のようなチェーンが可能になります。
iris %>% subset(., Species == "setosa", select = -Species) %>% colMeans(.)
## Sepal.Length Sepal.Width Petal.Length Petal.Width
## 5.006 3.428 1.462 0.246
Stefanはこのパイプ演算子で作業を続け、2013年12月30日にはplumbrパッケージをGitHubに実装しました。このパッケージには`%>%`演算子が含まれていました。2日後、plumbrはベルギーの画家ルネ・マグリットの有名な絵画「イメージの裏切り」への明確な言及として、現在の名前であるmagrittrに改名されました。
dplyrパッケージは並行して開発されていましたが、これらの2つの開発は収束することを意図していました。2014年1月のRStudioブログによるdplyr発表投稿の最初のコメントで、Stefan BacheはHadley Wickhamにmagrittr演算子について知らせました。その後、2014年3月19日に、dplyrでchain関数は非推奨となり、最終的に2014年4月14日に、dplyrはmagrittrの`%>%`演算子を組み込み、元の`%.%`の代わりにこれを推奨しました。なぜなら、前者はShiftキーを押しながらタイプするのがより簡単だからです。2014年8月1日、`%.%`は非推奨となりました。
同時に、2014年7月31日、RStudio IDEバージョンv0.98.987は、dplyr/magrittrパイプ演算子`%>%`のショートカットを組み込み、その使用をさらに容易にしました(Shift+Alt+.)。しかし、これはすぐに現在の形式であるCtrl+Shift+Mに変更されました。
この物語の時点で、Stefanがmagrittrの作成を開始した方法について語った2014年8月5日のブログ投稿を訪れることをお勧めします。
pipeR
Rにおけるパイプ実装の別のイテレーションは、2014年4月7日にKun RenがGitHubでpipeRパッケージを公開したときに始まりました。このパッケージは、パイピングプロセスにさらなる柔軟性を加えるために、別のパイプ演算子`%>>%`を組み込んでいました。このパッケージは、関数の最初の引数として、式内の引数として(`.`を使用)、数式を使用して、中間結果を保存するために(`~`を使用)、または名前で要素を抽出するために(`()`を使用)など、パイプのいくつかの機能を含んでいます。
library(pipeR)
mtcars %>>% (lm(mpg ~ wt + cyl, data = .)) %>>% (~ lm_mtcars) %>>% summary %>>% (r.squared)
## [1] 0.8302274
現在、pipeRは2016年以来更新されていませんが、CRANとGitHubで利用可能であり、試したい場合は利用できます。
wrapr
2018年7月、wrapprパッケージは「ドットアローパイプ」`%.>%`を導入しました。これは、`a %.>% b`という表記が`. <- a; b`とほぼ同等です。例えば
library(wrapr)
1:10 %.>% mean(.)
## [1] 5.5
そして左辺値は環境に格納されたままです。
.
## [1] 1 2 3 4 5 6 7 8 9 10
詳細はこちら。
Base R Pipes in R
Rのコードの書き方を完全に変え、よりシンプルで読みやすくしました。
したがって、それらがベースRコードに組み込まれるのは時間の問題でした。
最初の試みの1つは、Lionel Henryによって開発され、2016年2月にベースRでネイティブパイプのテストを開始しました。彼はこの件とRコアへの他の提案について非常に推奨される投稿を書き、R 4.0をネイティブパイプ演算子を持つものと想像しました。
このような実装の理由の1つは、「ネイティブなパイプ処理のサポートは、外部パッケージ(magrittr)への依存のため、エラー回復を改善する」ということでした。
この時点で、物語の一部が欠けている可能性があります。次の3年間、このトピックに進展はなかったようですが、おそらくRコアチーム内で内部的な議論が行われました。
2019年10月5日、Antoine F.がr-develリストに次のように質問しました。「ベースRにはパイプ演算子が必要ですか?」その質問は、Lionel Henry、Duncan Murdoch、Luke Tierneyなど、多くのR開発者からの返信を実際に引き起こしました。
実際、Luke Tierneyは、2020年7月8日に、彼とMartin MächlerのUseR! 2020での基調講演で、Rの新しい開発について話しました。基調講演の一部はパイプに捧げられ、Lionel HenryとJim Hesterのアイデアのおかげで、ベースパイプ`|>`が開発中であることを進めました。
最終的に、2020年末に、`|>`が誕生しました。ネイティブパイプはRのdevelバージョンで公式に発表され、r-develメーリングリストやRコミュニティ全体で即座に反応がありました(例はこちら、こちら、こちら、こちら)。
パイプは最終的に2021年5月18日にリリースおよび公式発表されたRの安定バージョン4.1.0に搭載されました。
これで、dplyrドキュメントから取った最初の例は次のように書くことができます。
hflights |> group_by(Year, Month, DayofMonth) |> select(Year:DayofMonth, ArrDelay, DepDelay) |> summarise( arr = mean(ArrDelay, na.rm = TRUE), dep = mean(DepDelay, na.rm = TRUE) ) |> filter(arr > 30 | dep > 30)
## # A tibble: 14 × 5
## # Groups: Year, Month [10]
## Year Month DayofMonth arr dep
## <int> <int> <int> <dbl> <dbl>
## 1 2011 2 4 44.1 47.2