井出草平の研究ノート

パイプ演算子[R]

Rの分析例をみていると、パイプ演算子が使われたものがあるので、自分の勉強がてら基本的な使い方をメモしておきたい。

パイプ演算子の目的

Rの標準書式は可視性がいまいちである。

x <- c(0.109, 0.359, 0.63, 0.996, 0.515, 0.142, 0.017, 0.829, 0.907)
round(exp(diff(log(x))), 1)

二行目は括弧が重なってよくわからない状態になっている。
括弧がうまく閉じていない、というのはRのコードを書いていると、よく経験する事態である。

これをパイプ演算子で表現すると、以下のようになる。

library(magrittr)
x %>% log() %>%
    diff() %>%
    exp() %>%
    round(1)

パイプ演算子を利用する利点は2点だろうか。

  1. 可視性が高まる。
  2. 計算をするときには一番中の括弧から解いていくので、計算手順と同じ手順でコードが書ける。

標準的な書き方だと中から書いて、左右に動かしながら、計算式を書いていくので、コードが合理的に書けない。
また、括弧が足りなかったり、多かったりしてエラーを吐かれることもしばしばある。
そういった問題をパイプ演算子は解決してくれるようだ。

パイプ書式

関数の基本から。

log(x) ## 標準
x %>% log() ## パイプ

オプションのつけ方。

round(pi, 6) ## 標準
pi %>% round(6) ## パイプ

babynamesデータからTaylorという名前の男性の人数を数える。

library(babynames)
library(dplyr)
data(babynames)
sum(select(filter(babynames,sex=="M",name=="Taylor"),n)) ## 標準

パイプ演算子は次のように書く。

babynames%>%filter(sex=="M",name=="Taylor")%>%  ## パイプ
            select(n)%>%
            sum

代入パイプ演算子(Assignment Pipe Operations)

パイプ演算子にはいくつか種類がある。 代入機能を持つパイプ演算子%<>%では変数加工がスマートになる。

irisデータを使って、Sepal.Lengthのデータを平方根にするというコードを書く。
<-を使うと標準書式では次のように書く。

iris$Sepal.Length <- sqrt(iris$Sepal.Length)

同じ事をパイプ書式で書くと次のようになる。

iris$Sepal.Length <- 
  iris$Sepal.Length %>%
  sqrt()

これをassign演算子%<>%で書くとスマートに書くことができる。

iris$Sepal.Length %<>% sqrt

Tee演算子(Tee Operator)

Tee演算子は右側の値ではなく左側の値を返す。

set.seed(123)
rnorm(200) %>%
matrix(ncol = 2) %T>%
plot %>% 
colSums

エクスポジション演算子(Exposition Operator)

iris %>%
  subset(Sepal.Length > mean(Sepal.Length)) %$%
  cor(Sepal.Length, Sepal.Width)

dplyrパッケージの5つのコマンド

dplyrは5つのコマンドから構成されている。

  • select...データフレームから指定した列のみ抽出する
  • filter...行の絞り込み
  • arrange...行を並べ替える
  • mutate...列の追加
  • summarize...集計

標準コードで書いたもの。

library(hflights)

grouped_flights <- group_by(hflights, Year, Month, DayofMonth)
flights_data <- select(grouped_flights, Year:DayofMonth, ArrDelay, DepDelay)
summarized_flights <- summarise(flights_data, 
                arr = mean(ArrDelay, na.rm = TRUE), 
                dep = mean(DepDelay, na.rm = TRUE))
final_result <- filter(summarized_flights, arr > 30 | dep > 30)

final_result

パイプ演算子で書いたもの。

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)

違いを一言で言うならば、標準コードはオフジェクトに格納をすることが基本的な発想であるが、パイプ演算子は格納せずに主として右側にそのまま関数で変換した値を渡すという発想ということなのだろう。

RStudioのショートカット

%>%を何度も打つのはめんどくさい。 RStudioのアドインをインストールすると少しは便利になる。

https://rstudio.github.io/rstudioaddins/

コンソールから下記のコードを実行し、RStudioを再起動。

devtools::install_github("rstudio/addinexamples", type = "source")

f:id:iDES:20191226061923p:plain

メニューのAddinsにinsert %in%が追加されている。

ショートカットの振り分けはModify Keyboard Shortcutsメニューから。

f:id:iDES:20191226061936p:plain

説明ではCtrl + Alt + Shift + Kを当てるようだが、Kはなぜか反応しなかったのてpipeのPを当てておいた。

f:id:iDES:20191226061947p:plain

関数型言語

統計解析にはあまり関係がないが、Rの言語型には議論があるようだ。
Rは関数型言語のようで関数型言語ではないらしい。

kiito.hatenablog.com

統計パッケージとしてRを利用している者にとっては、さほど重要なことではないが、Rを関数型言語として成立させるものがパイプ演算子だと考えると良いらしい。

このエントリは下記のエントリを参照した。

www.datacamp.com