こんにちは,shun(@datasciencemore)です!
今回は行処理についてやっていきます.
行処理ができるとなんとfor文をほとんど使わなくて済むようになります.
どのような処理なのでしょうか??
行処理はめっちゃ重要なので,いつも以上にじっくり見ていこうと思います.
0.行処理ってなに??
行処理は,データフレームの行ごとに処理をしていくことだよ!!
行処理は,データフレームにrowwiseを適用することでできるようになるよ!!
ついに来ました,rowwise!!
これ,めっちゃすごい機能なんです.
すごくシンプルに行ごとに処理することなんて紹介しましたけど,このrowwiseを使えるようになると実に様々な処理がとても簡単に記述できてしまいます.
まずは恒例となったrowwiseのイメージからどうぞ!
あはは
そうだよね,これだけだと抽象的だよね.
具体例を挙げればすんなりわかるよ~
今回はデータにdiamondsを使用していきます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
> diamonds # A tibble: 53,940 x 10 carat cut color clarity depth table price x y z <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 4 0.290 Premium I VS2 62.4 58 334 4.2 4.23 2.63 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48 7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47 8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53 9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49 10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39 # … with 53,930 more rows |
1.基本
簡単な例で確認していきましょう.
行ごとにx, y, zの平均を求めたいとします.
具体的に書くと
1行目のx, y, zの平均を求める.
2行目のx, y, zの平均を求める.
…
n行目のx, y, zの平均を求める.
って処理をすれば求められそうですね.
惜しい!
for文でもいいんだけど,rowwiseを使うともっと簡単に書けるんだ!
データサイエンスには繰り返し処理は頻出です.
で,繰り返し処理でほとんどのプログラミング言語はfor文を使います.
なんですが,for文って実はいろいろデメリットがあるんですね.
スピードが遅かったり,可読性が落ちたり...
Pythonでは,for文が多重ネストしてるコードをよく見ます.
1 2 3 4 |
for i in list_i: for j in list_j: for k in list_k: function(i, j, k) |
みたいな処理です.
この例だと3個しかループ変数がありませんが,それ以上になるともうカオス.
コードが恐ろしく読みづらくなります...
ちなみにPythonでも.applyを使えばfor文は回避できます.
ただ,なぜかはわかりませんが,あんまり.applyなどが使用されないでfor文が多い気がしますね...
なので,上の例はPythonが悪いという例ではなく,あくまで多重ネストの例として考えてください.
Pythonさん,すいません...
Rを使えば,こんな感じで驚くほど簡単に書けてしまいます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# x,y,zの平均を行ごとに算出して,Mean列に格納 diamonds %>% rowwise() %>% mutate( Mean = mean(c(x, y, z)) ) # A tibble: 53,940 x 11 # Rowwise: carat cut color clarity depth table price x y z Mean <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <dbl> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 3.45 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31 3.35 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 3.48 4 0.290 Premium I VS2 62.4 58 334 4.2 4.23 2.63 3.69 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 3.81 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48 3.46 7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47 3.47 8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53 3.57 9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49 3.38 10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39 3.48 # … with 53,930 more rows |
このコードを次のイメージと照らし合わせてください.
meanという関数は,ベクトルを引数にとることで,その平均を算出することができました.
1行目のx, y, zの平均を求める.⇒ mean(c(3.95, 3.98, 2.43))
2行目のx, y, zの平均を求める.⇒ mean(c(3.89, 3.84, 2.31))
3行目のx, y, zの平均を求める.⇒ mean(c(4.05, 4.07, 2.31))
4行目のx, y, zの平均を求める.⇒ mean(c(4.2, 4.23, 2.63))
それぞれ見比べると,変わっているところはmeanの引数であるベクトルだけですよね.
rowwiseを使用すると,列名を指定してあげるだけで各行の処理をその行に対応する列の数値を使用してくれるんですね!
でもPythonでも同じことができるんなら,そこまでありがたみを感じませんね...
ソンナコトナイヨ!
実はRでは,Pythonにはない,とてつもない便利機能があるんだ!
その便利機能とrowwiseを組み合わせると,様々な処理をわかりやすく記述することができるんだ!!
さて,便利機能とはどんな機能なんでしょうか?
次の章で詳細にみていきましょう.
2.便利機能
さっ,それでは,先ほどの便利機能について説明していきます.
その便利機能とは,Rだと普通の値(数値や文字列)だけでなく,どんなオブジェクトでもtibbleの中に格納できる!!というものです.
それって例えばどんなオブジェクトなんですか??
よく使うのは,データフレーム,関数,グラフ,モデルなどだね!
どれもめっちゃ便利だよ~
例えば,diamondsの各行にデータフレーム : irisを入れてみましょう.
(単なる例なので実用性はまったくありません笑)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# diamondsにirisを格納する. diamonds_with_iris = diamonds %>% mutate( data_iris = list( iris %>% tibble() ) ) # diamonds_with_iris 確認 diamonds_with_iris # A tibble: 53,940 x 11 carat cut color clarity depth table price x y z data_iris <dbl> <ord> <ord> <ord> <dbl> <dbl> <int> <dbl> <dbl> <dbl> <list> 1 0.23 Ideal E SI2 61.5 55 326 3.95 3.98 2.43 <tibble [150 × 5]> 2 0.21 Premium E SI1 59.8 61 326 3.89 3.84 2.31 <tibble [150 × 5]> 3 0.23 Good E VS1 56.9 65 327 4.05 4.07 2.31 <tibble [150 × 5]> 4 0.290 Premium I VS2 62.4 58 334 4.2 4.23 2.63 <tibble [150 × 5]> 5 0.31 Good J SI2 63.3 58 335 4.34 4.35 2.75 <tibble [150 × 5]> 6 0.24 Very Good J VVS2 62.8 57 336 3.94 3.96 2.48 <tibble [150 × 5]> 7 0.24 Very Good I VVS1 62.3 57 336 3.95 3.98 2.47 <tibble [150 × 5]> 8 0.26 Very Good H SI1 61.9 55 337 4.07 4.11 2.53 <tibble [150 × 5]> 9 0.22 Fair E VS2 65.1 61 337 3.87 3.78 2.49 <tibble [150 × 5]> 10 0.23 Very Good H VS1 59.4 61 338 4 4.05 2.39 <tibble [150 × 5]> # … with 53,930 more rows |
出力されたデータフレームの一番右側に今までに見たことのないlist型のdata_iris列が追加されました. これの中身を見てみましょう.
データフレームの値を詳細に見たいときは, データフレーム %>% pluck("列名", x) と指定すれば,データフレームの列名のx行目の値を見ることができます.
1 2 3 |
# data_irisの1行目を見る. diamonds_with_iris %>% pluck("data_iris", 1) |
見慣れたirisですね,ちゃんと格納できていました!
この機能は,系列ごとに傾向の違いを見たいときに非常によく使います.
なぜなら,そういうときって,系列ごとにデータフレームをグルーピングして考えますもんね.
系列ごとにデータフレームをグルーピングすることを ネストする といいます.
以前やったgroup_byを覚えているでしょうか?
これの②計算をしないで①グルーピングのみをするイメージです.
これができたら便利ですよね.
summariseだと平均とか単純な計算しかできなかったですが,データフレームごと格納できるのであれば,それらのデータフレームを分析していろんなことができそうですもんね!
実は,group_nestとnest_byを使用すれば簡単に実現できるんです.
次項では,group_nestとnest_byについて詳細にみていきましょう.
ちなみにぼくはgroup_byをほとんど使いません.
なぜなら,後述のgroup_nestやnest_byを使用すれば,group_byで使用できる関数よりもはるかに複雑な処理を書けるからです.
(もちろん,group_byで使用できる関数の処理も書けます.)
3.ネスト group_nestとnest_by
それでは,ネストするための関数,group_nestとnest_byについてみていきましょう.
っといってもそんなに難しくないですから安心してください笑
ただ,ネストしたい系列を指定するだけです.
例として,diamondsのcut列でネストしてみましょう.
cut列は「①Ideal, ②Premium, ③Good, ④very Good, ⑤Fair」の5つの系列があります.
cut列でネストすると次のようになります.
1 2 3 4 5 6 7 8 9 10 11 12 |
# cutでネスト(group_nest) diamonds %>% group_nest(cut) # A tibble: 5 x 2 cut data <ord> <list<tbl_df[,9]>> 1 Fair [1,610 × 9] 2 Good [4,906 × 9] 3 Very Good [12,082 × 9] 4 Premium [13,791 × 9] 5 Ideal [21,551 × 9] |
簡単にネストできましたね. 続いてnest_byです.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# cutでネスト(nest_by) diamonds %>% nest_by(cut) # A tibble: 5 x 2 # Rowwise: cut cut data <ord> <list<tbl_df[,9]>> 1 Fair [1,610 × 9] 2 Good [4,906 × 9] 3 Very Good [12,082 × 9] 4 Premium [13,791 × 9] 5 Ideal [21,551 × 9] |
group_byのときと何も変わらないですね??
group_byとnest_byは何が違うんですか??
nest_byで出力されるデータフレームをよく見てみて!!
group_byにはない,# Rowwise: cutという文字列が表示されてるよね!!
要するにgroup_byとnest_byの違いは,rowwiseの有無なんだ!!
group_byは,rowwiseされずにnest_byはrowwiseされて出力されるんだ!!
だから,以下のコードは,nest_byしたときと全く同じ出力になるよ!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
# cutでgroup_nestしてからrowwise diamonds %>% group_nest(cut) %>% rowwise() # A tibble: 5 x 2 # Rowwise: cut data <ord> <list<tbl_df[,9]>> 1 Fair [1,610 × 9] 2 Good [4,906 × 9] 3 Very Good [12,082 × 9] 4 Premium [13,791 × 9] 5 Ideal [21,551 × 9] |
group_nestとrowwiseのどちらを使ったほうがいいかはケースバイケースなので,各自でいろいろ試してみてください!
まとめ
今回は,以下のことをやりました.
- rowwiseの基本
- Rの便利機能
- ネスト(group_nestとnest_by)
rowwiseは,行ごとに処理をするための関数です.
また,Rは,データフレームに様々なオブジェクトを格納するという便利機能を持っています.
さらに,group_nestとnest_byを使用すれば,簡単に指定した系列でネストできるのでした.
これらのrowwiseとRの便利機能を使用すれば,様々な処理を実現できます.
次回はそれらの具体例について詳細にみていきましょう.
それじゃ,お疲れ様でした!!