Edited at

pipe中毒になったら、更にわがままな人間になった

More than 3 years have passed since last update.

pipeR三銃士に(自称)弟子入りしてから半年以上が経ちました。黄金連休、進捗どうですか(肉ソンに参加しましたが、掲げた目標が達成できなかったのでQiitaに記事を書きます)。

magrittr便利ですね。pipeRも頑張って欲しいですね。そんな私はなんでもかんでもチェインで繋げてしまう、%>%pipe%>%中毒者です。

magrittrのおかげでRでの作業が捗っているのですが、処理をしているとあれこれとわがままな発想が浮かんできました。そんなわがままな私の願いを叶える方法。基本的にvignetteに書いてあることなので、より詳しく(magrittrに馴染みのない人)はvignette("magrittr")でご確認ください。vignetteにあるような内容なので、これって常識なのかも(README嫁という自分への戒め)。


わがまま1: チェイン処理中の値を複数の関数に渡したい

@dichikaさんのブログでこんな話があります


集計結果を途中でプロットしながらもその結果はオブジェクトに保存しておきたい...

tee演算子を使うことで、chainの手を休めずにプロットなどの副作用を実行することが可能になります。

副作用を許しながらもchaninしていく - 盆栽生活 http://d.hatena.ne.jp/dichika/20140731/p1


こんな感じ

iris %>% group_by(Species) %>% 

summarise(avg = mean(Sepal.Length)) %T>% plot(.) -> res

T演算子を使い途中結果をglobal環境に保存します。複数の処理をする場合にT演算子便利なんだけど実行結果をglobal環境に残しておくのは嫌。結果を表示させるためにはもう一度呼び出さないといけないし...。

途中の実行結果はコンソールで確認するに留めたいというわがまま発想。

調べてみたらmagrittrのvignetteにきちんとAliasesという項目で説明がありました。こんな感じで使います。

iris %>% filter(Species == "setosa") %$% {

plot(Sepal.Length, Petal.Width)
mean(Sepal.Length)
}

function()のように、中括弧で囲んだ部分に対し、演算子直前の処理結果を複数の関数に渡すことでirisデータのSpecies::setosaの花弁幅と萼片長のプロットと萼片長の平均値が同時に出力できました。これはiris %>% filter(Species == "setosa")までの処理結果を中括弧で囲ったplot()mean()に渡しています。さらに%$%を使うことで、処理結果の変数にアクセスしているというものです。はっぴーです。

先の例では%$%で変数に対し処理を行いましたが、通常の%>%を引き渡す場合の注意点として、処理結果を引き渡す関数にはドット(magrittr処理における第一引数)を明示する必要があります。普段から関数名だけ(括弧なし)あるいは第一引数を省略している人は気をつけてください。

# これはoK

iris %>% {
str(.)
summary(.)
}
# だめな例。第一引数を省略、関数名のあとに括弧をつけない
iris %>% {
str()
summary
}


ミソ

なんでこんなことができるのかをざっくり説明すると、チェイン処理は無名関数としても機能することができるためです。上の例では、つぎの処理と同じことをチェイン処理によっていくつか省略しているということです。

# 無名関数としての処理

iris %>% (function(x) {
str(x)
summary(x)
})

# チェイン処理を施した無名関数として扱う
iris %>% {
str(.)
summary(.)
}

複数の処理を記述したfunction()とその引数であるxを省略しているということで。なので中括弧が必要と。...なるほどです。なお中括弧内で更にチェイン処理を使うことも可能です。

mtcars %>% lm("mpg ~ wt", data = .) %$% {

print(.)
broom::tidy(.) %>% knitr::kable()
}


わがまま2: 再帰的に利用できるようglobal環境にも保存したい

上の方法では、途中結果の代入ができません(T演算子を使っていないため?)。というわけで集計値の表示も保存もプロットもまとめてしたいんじゃ、という場合には関数内で使用するように永続代入 <<- を使用すると超はっぴー。

iris %>% filter(Species == "setosa") %$% {

plot(Sepal.Length, Petal.Width)
mean(Sepal.Length)
res <<- (.) # <- はだめ。永続代入するとよい
}
head(res)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 5.1 3.5 1.4 0.2 setosa
# 2 4.9 3.0 1.4 0.2 setosa
# 3 4.7 3.2 1.3 0.2 setosa
# 4 4.6 3.1 1.5 0.2 setosa
# 5 5.0 3.6 1.4 0.2 setosa
# 6 5.4 3.9 1.7 0.4 setosa


わがまま3: ggplot2を使った作図もしたい

qplot()ggplot()ではプロット前の演算子が異なるので注意。qplot()ではデータセットそのものを引き渡す%>%でokですが、ggplot()の場合は変数にアクセスする%$%を使いましょう。

iris %>% filter(Species == "setosa") %>% {

qplot(Sepal.Length, Petal.Width, data = .)
}

iris %>% filter(Species == "setosa") %$% {
ggplot(data = ., aes(Sepal.Length, Petal.Width)) + geom_point()
}

最初は関数の省略とかイミフwwwだったんですが、理解すると気持ちいいイイイ。あれこれ捗りそうです。さんきゅーStefan!!


参考・より詳しく