magrittr の紹介

Stefan Milton Bache

November, 2014

Abstract

magrittr (洗練されたフランス語のアクセントで発音してください) パッケージには次の2つの目的があります: コードの可読性と保守性の向上です。 あるいは、もっと短くてもいいです: あなたのコードを煙に巻きましょう(パフパフ)!

その目的を達成するために、magrittr (アクセントを忘れずに)は、新しい「パイプ」のような演算子、%>%を提供します。 この演算子を使って、値を式や関数の呼び出しにパイプで送り込むことができます。 これは、他では知られていない機能です。 代表的な例としては、 F# で (控えめに言っても)広く使われている |> 演算子があります。 実際、Unix のパイプと並んで、これが magrittr パッケージを開発する動機となりました。

この vignette では、magrittr の主な機能を説明し、以下のことを実演します。この vignette では、magrittr の主な機能を説明し、最初のリリース以降に追加されたいくつかの機能を実演します。

はじめにと基礎知識

最初は、%>% のような演算子が本当に有益なものなのか疑問に思うかもしれません。 しかし、気がつくと、意味的にコードが変化して、読み書きともに直感的になります。

次の例では、Rに同梱されている mtcars データセットを少し加工してみましょう。

library(magrittr)

car_data <- 
  mtcars %>%
  subset(hp > 100) %>%
  aggregate(. ~ cyl, data = ., FUN = . %>% mean %>% round(2)) %>%
  transform(kpl = mpg %>% multiply_by(0.4251)) %>%
  print
#>   cyl   mpg   disp     hp drat   wt  qsec   vs   am gear carb       kpl
#> 1   4 25.90 108.05 111.00 3.94 2.15 17.75 1.00 1.00 4.50 2.00 11.010090
#> 2   6 19.74 183.31 122.29 3.59 3.12 17.98 0.57 0.43 3.86 3.43  8.391474
#> 3   8 15.10 353.10 209.21 3.23 4.00 16.77 0.00 0.14 3.29 3.50  6.419010

ここでは、mtcars という値(data.frame)から始めます。 そこから、サブセットを抽出し、シリンダー数に基づいて情報を集約し、さらに データセットを変換し、miles per gallon に加えて km/L の変数を追加します。 最後に、結果を表示してからその値を代入します。 タスクを考えるときの論理的な順序でコードが配置されていることに注意してください:data->transform->aggregate、これはコードが実行される順序と同じです。 これはレシピのようなもので、読みやすく、簡単に読むことができます!

恐ろしいことに、次のように書くこともできます。

car_data <- 
  transform(aggregate(. ~ cyl, 
                      data = subset(mtcars, hp > 100), 
                      FUN = function(x) round(mean(x), 2)), 
            kpl = mpg*0.4251)

括弧で囲まれているとごちゃごちゃしてしまいますし、コードを解読するのはより困難です。 特に自分で書いたものでなければ、尚更です。

また、magrittr では、aggregate で使用する関数をその場で「構築」することが非常に簡単であることにも注目してください。 パイプラインの左辺に実際の値を使うのではなく、プレースホルダーを使うだけです。 これは、Rの apply 関数群でも非常に便利です。

もちろん、上にあげた2番目の例をより良くするために、いくつかの一時的な変数を投入することができるかもしれません。 (これは、magrittr を使用する際には、ある程度回避されることが多い) しかし、紹介したような雑然とした行をよく見かけます。

そして、ここにもう一つのセールスポイントがあります:プロセスのどこかで別のステップを素早く追加したいとします。 これは、パイプラインバージョンでは非常に簡単にできますが、「標準的な」例では少し面倒です。

組み合わせた例 (訳注: 上にある最初コードのこと) では、パイプの優れた機能をいくつか示しています。

  1. デフォルトでは、左辺(LHS)は、右辺に現れる関数の第1引数として piped in されます。 これは、subsettransform の場合です。
  2. %>% は入れ子構造で使用することができます。 これは mpg から kpl への変換で示されています。
  3. LHSが最初以外の位置で必要なときは、ドット. をプレースホルダーとして使うことができます。 これは aggregate 式で示されています。
  4. 数式などのドットはプレースホルダーとして誤認識されません。 これは aggregate 式で利用されます。
  5. 引数(LHS)が1つだけ必要な場合は、空の括弧を省略することができます。 括弧を省略することができます。 これは print の呼び出しで示されています (これはまた、その引数を返します)。 ここでは、LHS %>% print() や、LHS %>% print(.) でも動作します。
  6. LHSにドット(.)を持つパイプラインは単項関数を作ります。 これは aggregate 関数の定義に使用されます。

上記では説明しませんでしたが、「匿名関数」や「ラムダ関数」へのパイプもあります。 これは、標準的な関数の定義を用いて可能です。 例えば、以下のようになります。

car_data %>%
(function(x) {
  if (nrow(x) > 2) 
    rbind(head(x, 1), tail(x, 1))
  else x
})

しかし、magrittr ではショートハンド記法も可能です。

car_data %>%
{ 
  if (nrow(.) > 0)
    rbind(head(., 1), tail(., 1))
  else .
}
#>   cyl  mpg   disp     hp drat   wt  qsec vs   am gear carb      kpl
#> 1   4 25.9 108.05 111.00 3.94 2.15 17.75  1 1.00 4.50  2.0 11.01009
#> 3   8 15.1 353.10 209.21 3.23 4.00 16.77  0 0.14 3.29  3.5  6.41901

すべての右辺は、実際には単項関数の「体の表現」であるため、これは単純な右辺の表現を自然に拡張したものにすぎません。 もちろん、この方法を使えば、より長く、より複雑な関数を作ることができます。

最初の例では,無名関数を括弧で囲んでいます。 関数やコールを生成する文を右辺に使いたいときは、必ず括弧を使います。 括弧を使って、右辺を評価してからパイプを行うようにします。

別の、あまり役に立たない例:

1:10 %>% (substitute(f(), list(f = sum)))
#> [1] 55

追加のパイプ演算子

magrittr は、関連する3つのパイプ演算子も提供しています。 これらは %>% ほど一般的ではありませんが、特殊なケースでは便利です。

“tee” パイプである %T>%%>% と同様に動作しますが、右辺の演算結果ではなく、左辺の値を返します。 これは、パイプラインのステップがその副作用(表示、プロット、ログなど)のために使用される場合に便利です。 例として(ここでは実際のプロットを省略しています)。:

rnorm(200) %>%
matrix(ncol = 2) %T>%
plot %>% # plot usually does not return anything. 
colSums
#> [1] 9.982095 1.886718

“exposition” パイプ %$% は、左手側のオブジェクト内の名前を右手側の式に公開します。 基本的には、with 関数を使用するための短縮形です(同じ左辺のオブジェクトも受け入れられます)。 この演算子は、例えば lmaggregate のように、関数自体がデータ引数を持たない場合に便利です。 ここでは説明としていくつかの例を示します。

iris %>%
  subset(Sepal.Length > mean(Sepal.Length)) %$%
  cor(Sepal.Length, Sepal.Width)
   
data.frame(z = rnorm(100)) %$% 
  ts.plot(z)

最後に、「割り当て」パイプ %<>% は、チェーンの最初のパイプとして使用できます。 その効果は、パイプラインの結果が、通常のように結果を返すのではなく、左辺のオブジェクトに割り当てられることです。 これは本質的に、foo <- foo %>% bar %>% baz のような式の短縮表記で、foo %<>% bar %>% baz になります。 他の例としては:

iris$Sepal.Length %<>% sqrt

expr <- ... が意味を成すときは、いつでも %<>% を使うことができ、たとえば、以下のようになります。

エイリアス

magrittr では、%>% 演算子に加えて、他の演算子のエイリアスも提供しており、加算や乗算などの演算を magrittr 構文にうまく適合させています。 例として、以下を考えてみましょう。:

rnorm(1000)    %>%
multiply_by(5) %>%
add(5)         %>%
{ 
   cat("Mean:", mean(.), 
       "Variance:", var(.), "\n")
   head(.)
}
#> Mean: 4.832224 Variance: 24.37681
#> [1]  8.493352  6.053608 -7.094153  3.205494 -1.307951  0.633988

と書かれていますが、よりコンパクトに書くと次のようになります。

rnorm(100) %>% `*`(5) %>% `+`(5) %>% 
{
  cat("Mean:", mean(.), "Variance:", var(.),  "\n")
  head(.)
}

エイリアスのリストを見るには、例えば ?multiply_by を実行してください。

開発版

magrittr パッケージは、GitHubの開発ページで開発版も提供されています。 github.com/tidyverse/magrittr です。