前回までの進捗
こんにちは、小幡です。Rを学んでおります。
前回は「tidyr」というパッケージを使ってデータの変形作業を勉強しました。データには機械が読みやすい形と人間が読みやすい形の2種類があります。まずは手元にあるデータを機械が読みやすい形にすることから始め、「pivot_longer」と「pivot_wider」の2つの関数を使って整形をしました。
※編集部注:初出時、本連載は全7回の予定でしたが、編集の都合上、全8回に変更しました。その分内容も詳しくなりましたのでなにとぞ…!(2020.8.12追記)
そして今回は「dplyr」(読み方:ディープライヤー)というパッケージを使って、データの加工や集計の方法を勉強します。これを学べばデータから新しい指標を計算したり、項目ごとに集計したりすることができるそう。着実に進んでいる感じがします。では今日も早速、輿石さんよろしくお願いします!
Rのパッケージ「dplyr」とは
輿石さん:さて、前回の「tidyr」ではデータの形を変えるだけでしたが、今回は「dplyr」パッケージを使ってデータの加工や集計を行っていきます。データから足し算・引き算をはじめとする計算を行って新しいデータを追加したり、あるいは集計といった、Excelのピボットテーブルを使って作るようなアウトプットをRでできるようになるのが今回の目標ですよ。
株式会社ヴァリューズ ソリューション局 マネジャー
輿石拓真(こしいし・たくま)さん
輿石さん:簡潔に「Excelのピボットテーブル」と表現してしまいましたが、このセクションをクリアするとできることの幅がぐっと広がり、より複雑なこともできるようになります。R学習の中でもキモの部分なので頑張りましょうね。
小幡:分かりました!気合いを入れて頑張ります。
輿石さん:できることの幅が広がりますと言うと、いろいろ覚えなきゃいけないんじゃないかと心配になるかもしれないですが、そんなことはありません。次の2つのステップを踏んで習得していきましょう。
輿石さん:まずは7つの基本操作を覚えることが目標です。7つの道具(操作)を覚えたら、その道具を組み合わせることで色々なことができるようになっているはずです!②の列計算は徐々に覚えていけばOKです。(日時と文字列データの取り扱いはトピックスとして後日取り上げます。)
小幡:7つと聞くと頑張れる気がします!よろしくお願いします。
データフレームに対する7つの基本操作
輿石さん:では先に、7つの基本操作と対応する関数の例をまとめますね。
No. | 操作の内容 | 代表的な関数名 |
1 | 一部の列を取り出す | select |
2 | 行を特定の基準で並び替える | arrange |
3 | 一部の行を取り出す | filter |
4 | データを要約する | summarise |
5 | 新しい列を追加する | mutate |
6 | グループ化された集計を行う | group_by |
7 | データセットを結合する | ***_join |
輿石さん:以上の7つです。一つ一つはエクセルでもできるような単純な操作ですよね。例えば3番の「一部の行を取り出す」であれば、エクセルのフィルター機能を使ってデータの表示 / 非表示を行ったことがあると思います。一つ一つは単純なので頑張っていきましょう。
小幡:なるほど、楽しみです…!
輿石さん:7つの操作に使う関数は、インプットにデータフレームを取り、変更を加えたデータフレームをアウトプットする点が特徴です。データフレームに対して加えたい変更を行い、変更後のデータフレームをアウトプットします。以下のように、少しずつデータフレームに変更を加えていくことで様々なデータ加工ができるようになるんです。
今日は7つの操作のうち、3つを教えます。次に健康系のサプリメントECを想定したサンプルのデータを用意しました。このデータを使ってそれぞれの基本操作について学んでいきましょう!
# packageの読み込み
library(tidyverse)
# データの読み込み
kenko_ec_data <- read_tsv("https://adcreative.valuesccg.net/manamina/article/manaminar/kenko_ec_sample.tsv")
kenko_ec_data
●コンソールの出力内容
#> # A tibble: 10,361 x 7
#> userid purchase_date item_category series price purchase_amount
#> <chr> <date> <chr> <chr> <dbl> <int>
#> 1 K00002 2020-01-07 マルチ酵素 お徳用 3186 1
#> 2 K00003 2020-03-24 カルシウム プレミアム~ 934 1
#> 3 K00005 2020-01-30 クレアチン スタンダー~ 2560 1
#> 4 K00008 2020-01-30 乳酸菌 プレミアム~ 2574 1
#> 5 K00009 2020-01-15 クエン酸 スタンダー~ 1080 1
#> 6 K00010 2020-03-22 ビタミンB お徳用 1617 1
#> 7 K00019 2020-03-21 グルコサミン お徳用 5329 3
#> 8 K00023 2020-01-05 ビタミンB プレミアム~ 778 1
#> 9 K00027 2020-03-13 スピルリナ お徳用 5713 5
#> 10 K00031 2020-02-12 BCAA スタンダー~ 3694 1
#> # ... with 10,351 more rows, and 1 more variable: item_name <chr>
■1.一部の列を取り出す
輿石さん:データフレームから特定のある列だけに絞りたいときはselect
という関数を使います。
使い方はselect(データフレーム, 列1, 列2, 列3, 列…)
というように、まず最初に変更を加えたいデータフレームを、そのあとに残したい列の名前を書き並べます。kenko_ec_data
をuserid、purchase_date、item_categoryの3列に絞ってみましょう。
select(kenko_ec_data, userid, purchase_date, item_category)
●コンソールの出力内容
#> # A tibble: 10,361 x 3
#> userid purchase_date item_category
#> <chr> <date> <chr>
#> 1 K00002 2020-01-07 マルチ酵素
#> 2 K00003 2020-03-24 カルシウム
#> 3 K00005 2020-01-30 クレアチン
#> 4 K00008 2020-01-30 乳酸菌
#> 5 K00009 2020-01-15 クエン酸
#> 6 K00010 2020-03-22 ビタミンB
#> 7 K00019 2020-03-21 グルコサミン
#> 8 K00023 2020-01-05 ビタミンB
#> 9 K00027 2020-03-13 スピルリナ
#> 10 K00031 2020-02-12 BCAA
#> # ... with 10,351 more rows
小幡:できました!ある列だけを残して見たいときに使う、ということですね。
輿石さん:そうですね。エクセルでも列数が多かったりすると見づらいですよね。列数を絞ってデータを見やすくしたかったり、特定の列の値を確認したいときにも有用です。
ここでは残したい列の名前を記載して指示しましたが、列名の前に「-」を付けると消したい列を指定できます。また、その他にも列の指定に便利な関数が用意されているんです。
関数 | 使用例 | 説明 |
starts_with | starts_with("xxx") | 列名が"xxx"で始まる列を取得 |
ends_with | ends_with("xxx") | 列名が"xxx"で終わる列を取得 |
contains | contains("xxx") | 列名に"xxx"が含まれる列を取得 |
matches | matches("xxx") | 正規表現"xxx"にマッチする列を取得 |
num_range | num_range("x_" 1:5, width = 2) | 文字列"x_"に連番を組み合わせた列を取得(x_1、 x_2、 x_3など) |
one_of | one_of("x", "y", "z") | x、y、zに含まれる変数を選択 |
everything | everything() | すべての列を取得 |
輿石さん:試しにstart_with()
とマイナスの指定を組み合わせてコードを実行してみましょう。
select(kenko_ec_data, userid, starts_with("p"), -price)●コンソールの出力内容
#> # A tibble: 10,361 x 3
#> userid purchase_date purchase_amount
#> <chr> <date> <int>
#> 1 K00002 2020-01-07 1
#> 2 K00003 2020-03-24 1
#> 3 K00005 2020-01-30 1
#> 4 K00008 2020-01-30 1
#> 5 K00009 2020-01-15 1
#> 6 K00010 2020-03-22 1
#> 7 K00019 2020-03-21 3
#> 8 K00023 2020-01-05 1
#> 9 K00027 2020-03-13 5
#> 10 K00031 2020-02-12 1
#> # ... with 10,351 more rows
小幡:userid
とpから始まるpurchase_date
とpurchase_amount
の3列が出力されました。price
はマイナスで指定したらからpから始まっていても出力されていないんですね。
輿石さん:最後にマーケティング系でよくある使いどころを一つ。広告系のデータだと、成果ポイントがいくつもあって、それごとにCV○○
という列があったりするんです。start_with()
を使うことで、わざわざ何個も列名を列挙せずにCVという名前が入っている列名を取得できますよ。
■2.行を並び替える
輿石さん:それでは2つ目です。行を並び替えるにはarrange
という関数を使います。arrange(データフレーム, 列1, 列2, 列3, 列…)
というように、まず最初に変更を加えたいデータフレームを、そのあとに並べ替えの基準にしたい列を指定していきます。
基本的には指定した列を昇順にして並べ替えます。降順に並べ替えたい場合は、列名をdesc()
という関数で囲いましょう。
例えば「データフレームkenko_ec_data
をpriceの数字が大きい順に並べ替えたいとき」は次のようにします。
kenko_ec_data_sort <- arrange(kenko_ec_data, desc(price))
select(kenko_ec_data_sort, userid, item_name, price)
●コンソールの出力内容
#> # A tibble: 10,361 x 3
#> userid item_name price
#> <chr> <chr> <dbl>
#> 1 K06118 プロテイン サプリメント お徳用 90日分 14928
#> 2 K06118 プロテイン サプリメント お徳用 90日分 14928
#> 3 K08893 プロテイン サプリメント お徳用 90日分 14928
#> 4 K07710 プロテイン サプリメント お徳用 90日分 14928
#> 5 K01093 カゼインプロテイン サプリメント お徳用 90日分 10945
#> 6 K01680 カゼインプロテイン サプリメント お徳用 90日分 10945
#> 7 K01846 カゼインプロテイン サプリメント お徳用 90日分 10945
#> 8 K01680 カゼインプロテイン サプリメント お徳用 90日分 10945
#> 9 K02312 カゼインプロテイン サプリメント お徳用 90日分 10945
#> 10 K03541 カゼインプロテイン サプリメント お徳用 90日分 10945
#> # ... with 10,351 more rows
輿石さん:arrange
を使って並び替えたデータフレーム(kenko_ec_data_sort)をselect
関数に渡して3列に絞って表示させました。arrange
関数に列数を絞る働きはないので注意してくださいね。
小幡:なるほど。複数条件で並び替えることもできるんですか?
輿石さん:できますよ。並び替えたい順番に列名を書き並べていくだけです。例えば、まずは購入日が古い順に並び替えて、そのうえで購入数量が高い順に並び替えてみましょう。
kenko_ec_data_sort2 <- arrange(kenko_ec_data, purchase_date, desc(purchase_amount))
select(kenko_ec_data_sort2, userid, purchase_date, item_name, purchase_amount)
●コンソールの出力内容
#> # A tibble: 10,361 x 4
#> userid purchase_date item_name purchase_amount
#> <chr> <date> <chr> <int>
#> 1 K00121 2020-01-01 ホエイプロテイン サプリメント プレミアム 30日分 8
#> 2 K07670 2020-01-01 カルシウム サプリメント スタンダード 30日分 6
#> 3 K04749 2020-01-01 BCAA サプリメント スタンダード 30日分 5
#> 4 K07839 2020-01-01 アミノ酸 サプリメント プレミアム 30日分 5
#> 5 K08302 2020-01-01 亜鉛 サプリメント プレミアム 30日分 5
#> 6 K09248 2020-01-01 ビタミンD サプリメント プレミアム 30日分 5
#> 7 K04749 2020-01-01 BCAA サプリメント プレミアム 30日分 4
#> 8 K08410 2020-01-01 ホエイプロテイン サプリメント お徳用 90日分 4
#> 9 K08819 2020-01-01 ホエイプロテイン サプリメント プレミアム 30日分 4
#> 10 K08582 2020-01-01 ビタミンB サプリメント プレミアム 30日分 4
#> # ... with 10,351 more rows
小幡:できました!複数の条件で並び替えられるのは便利ですね。
■3.行の一部を取り出す
輿石さん:3つ目は「一部の行を取り出す」関数filter
です。特定の行に絞りたいとき、例えば「単価が3,000円より大きいレコードを選ぶ」という作業をしたいときをイメージしてください。
使い方はfilter(データフレーム, 条件)
の形です。最初に変更を加えたいデータフレームを指定するのはselect
やarrange
と同じですが、二つ目以降は列名ではなく条件を指定します。単価(price)が3,000円以上のレコードに絞ってみましょう。
kenko_ec_data_f <- filter(kenko_ec_data, price > 3000)
select(kenko_ec_data_f, userid, purchase_date, price)
●コンソールの出力内容
#> # A tibble: 3,615 x 3
#> userid purchase_date price
#> <chr> <date> <dbl>
#> 1 K00002 2020-01-07 3186
#> 2 K00019 2020-03-21 5329
#> 3 K00027 2020-03-13 5713
#> 4 K00031 2020-02-12 3694
#> 5 K00046 2020-02-27 5346
#> 6 K00064 2020-01-30 3042
#> 7 K00096 2020-02-25 4682
#> 8 K00097 2020-03-03 9725
#> 9 K00103 2020-03-22 3693
#> 10 K00110 2020-01-15 5346
#> # ... with 3,605 more rows
小幡:10,000行程度あったデータが3,615行に減っていますね。
輿石さん:条件は>
や==
などの演算子を使って表します。以下に一覧を示します。「イコール」は=
を2つ重ねて表現する点に注意してくださいね。
輿石さん:複数条件にしたいときは、3つ目の「&」「|」を使います。例えば「単価(price)が3,000円以上で、かつ商品カテゴリ(item_category)が「コラーゲン」」のレコードに絞りたいときは次のようにします。
kenko_ec_data_f2 <- filter(kenko_ec_data, price >= 3000 & item_category == "コラーゲン")
select(kenko_ec_data_f2, userid, purchase_date, item_category, price)
●コンソールの出力内容
#> # A tibble: 101 x 4
#> userid purchase_date item_category price
#> <chr> <date> <chr> <dbl>
#> 1 K00120 2020-03-19 コラーゲン 5400
#> 2 K00313 2020-03-22 コラーゲン 5400
#> 3 K00092 2020-02-06 コラーゲン 5400
#> 4 K00092 2020-03-23 コラーゲン 5400
#> 5 K00092 2020-01-18 コラーゲン 5400
#> 6 K00545 2020-03-25 コラーゲン 5400
#> 7 K00978 2020-03-08 コラーゲン 5400
#> 8 K01258 2020-02-04 コラーゲン 5400
#> 9 K01352 2020-02-13 コラーゲン 5400
#> 10 K01594 2020-02-04 コラーゲン 5400
#> # ... with 91 more rows
小幡:条件の合うレコードは101行ですね。
輿石さん:そうですね。distinct()という関数もよく使います。この関数は、データフレームから重複を削除します。「行を絞る」ともいえるのでここで一緒に勉強してしまいましょう。distinct(データフレーム, 列1, 列2, 列3, 列...)
という形で、第2引数以降は一意にしたい(重複をなくしたい)列を書き並べていきます。以下を実行してみてください。
distinct(kenko_ec_data, userid)
●コンソールの出力内容
#> # A tibble: 3,000 x 1
#> userid
#> <chr>
#> 1 K00002
#> 2 K00003
#> 3 K00005
#> 4 K00008
#> 5 K00009
#> 6 K00010
#> 7 K00019
#> 8 K00023
#> 9 K00027
#> 10 K00031
#> # ... with 2,990 more rows
小幡:列に指定したuserid列だけの3,000行が表示されました。
輿石さん:そうですね。このデータには3,000人分のデータが含まれていたんですね。組み合わせで一意にしたい場合は、distinct(kenko_ec_data, userid, purchase_date)
という感じで、複数の列を指定してあげてくださいね。
小幡:「行を絞る」セクションなのに、列も指定したものに絞られていますよね?
輿石さん:実は、引数に.keep_all = TRUE
というオプションを渡すと、指定しなかった残りの列もすべて残してくれるんです。重複がある場合はデータの上にあるレコードが残ることを覚えておいてください。
小幡:そんな引数があるんですね。
輿石さん:関数には細かな挙動をコントロールするための引数が用意されていることが多いです。?関数名
でヘルプページを見ると新しい発見があると思いますよ。.keep_all = TRUE
の使用例として次のコードを実行してみてください。
kenko_ec_data_sort <- arrange(kenko_ec_data, userid, purchase_date)
kenko_ec_data_d <- distinct(kenko_ec_data_sort, userid, .keep_all = TRUE)
select(kenko_ec_data_d, -item_name)
●コンソールの出力内容
#> # A tibble: 3,000 x 6
#> userid purchase_date item_category series price purchase_amount
#> <chr> <date> <chr> <chr> <dbl> <int>
#> 1 K00002 2020-01-07 マルチ酵素 お徳用 3186 1
#> 2 K00003 2020-01-17 ビタミンB プレミアム 778 1
#> 3 K00005 2020-01-30 クレアチン スタンダード 2560 1
#> 4 K00008 2020-01-30 乳酸菌 プレミアム 2574 1
#> 5 K00009 2020-01-15 クエン酸 スタンダード 1080 1
#> 6 K00010 2020-02-17 ビタミンB お徳用 1617 1
#> 7 K00019 2020-03-21 グルコサミン お徳用 5329 3
#> 8 K00023 2020-01-05 ビタミンB プレミアム 778 1
#> 9 K00027 2020-02-05 ビタミンB お徳用 1617 1
#> 10 K00031 2020-02-12 BCAA スタンダード 3694 1
#> # ... with 2,990 more rows
輿石さん:最初にarrange
を使って最新の日付を持つレコードが上にくるようにデータを並び替えています。並び替えたデータをdistinct
でuserid単位で一意になるように重複を削除しています。.keep_all = TURE
も指定したので、レコードは全部残っているはずです。(最後にデータの表示が見やすいようにselect
で列を絞りました。)人ごとに最新のレコードが抽出されたのですが、わかりますか??
小幡:...!日付降順での並び替えと一緒に使ったから最新になっているんですね!重複があったら上のレコードが残るという先ほど聞いた決まりが生きていますね。
※同一日付で複数商品の購入があった場合はたまたまデータの上の方にあったレコードが生き残っています。
輿石さん:「最近の購入がどうだったか?」を知りたいときに使えますよね。「基本操作を組み合わせると複雑なことができるようになる」という最初の言葉を少しでも実感してくれていたら嬉しいです。
輿石さん:さて、今日はこれで終わりです!お疲れさまでした。dplyrパッケージでできる7つの基本操作の内3つの処理をお伝えしました。ひとつひとつの作業は単純でも、3つの道具を使って組み合わせるといろいろな値が出せるようになったと思います。残りの集計処理を行うsummarise
や、処理をグループ化するgroup_by
などの基本操作を覚えたら、Rはさらに強力なツールとなっているはずです。
※次回は基本操作を組み合わせた処理を書く際に便利なパイプ演算子
についても勉強しますよ。
まとめ
今日はdplyrパッケージの7つの基本操作のうち、3つを学びました。まだ3つだけですが、3つだけでも組み合わせたら色々できるんだなあと実感できましたことが収穫です。できることの範囲がかなり広がったように思います!
# データの準備 ## packageの読み込み library(tidyverse) ## データの読み込み kenko_ec_data <- read_tsv("https://adcreative.valuesccg.net/manamina/article/manaminar/kenko_ec_sample.tsv") kenko_ec_data # 一部の列を取り出す ## select select(kenko_ec_data, userid, purchase_date, item_category) select(kenko_ec_data, userid, starts_with("p"), -price) # 行を並び替える ## arrange kenko_ec_data_sort <- arrange(kenko_ec_data, desc(price)) select(kenko_ec_data_sort, userid, item_name, price) kenko_ec_data_sort2 <- arrange(kenko_ec_data, purchase_date, desc(purchase_amount)) select(kenko_ec_data_sort2, userid, purchase_date, item_name, purchase_amount) # 一部の行を取り出す ## filter kenko_ec_data_f <- filter(kenko_ec_data, price > 3000) select(kenko_ec_data_f, userid, purchase_date, price) kenko_ec_data_f2 <- filter(kenko_ec_data, price >= 3000 & item_category == "コラーゲン") select(kenko_ec_data_f2, userid, purchase_date, item_category, price) ## distinct distinct(kenko_ec_data, userid) kenko_ec_data_sort <- arrange(kenko_ec_data, userid, purchase_date) kenko_ec_data_d <- distinct(kenko_ec_data_sort, userid, .keep_all = TRUE) select(kenko_ec_data_d, -item_name)
Rでデータを加工・集計する3つの操作を解説。 マーケターが1からRを勉強します【第5回】 | [マナミナ]まなべるみんなのデータマーケティング・マガジン
https://manamina.valuesccg.com/articles/804マーケター1年目の小幡さんがRを学んでいきます。講師は株式会社ヴァリューズのデータアナリスト、輿石さん。第5回はRを使ったデータの要約、列の追加、データのグループ化の3操作を習得します。Rでデータを扱えるようになりたいと考えている方、ぜひ小幡さんと一緒に勉強していきましょう。
メールマガジン登録
最新調査やマーケティングに役立つ
トレンド情報をお届けします
大学でマーケティングを勉強しながら、ヴァリューズでインターンとして働いていました。2020年の春からは新卒としてヴァリューズに入社しました。