初心者セッション
〜データハンドリング編〜

@y__mattu

2018/11/10 Tokyo.R #74

はじめに

誰?

icon

著書

R ユーザのための RStudio[実践]入門
− tidyverse によるモダンな分析フローの世界−

rstudiobook

  • 通称: 「宇宙本
  1. RStudio 入門(@y__mattu)
  2. スクレイピングによるデータ取得(@y__mattu)
  3. dplyr を中心としたデータハンドリング(@yutannihilation)
  4. ggplot2 による可視化(@kyn02666)
  5. R Markdown によるレポーティング(@kazutan)

データハンドリング

データ分析の流れ

やること

  • 絞り込み(列・行)
  • 新しい変数の作成
  • 集計
  • テーブルのマージ
  • 縦横変換
  • etc…

おことわり

今日の内容の 8 割は「宇宙本」第 3 章に書いてあります。

本題のまえに

tidyverse について

tidyverse(概念)

ざっくり:

  • R でやるいろんな操作(データハンドリング、可視化、スクレイピング、分析、etc)を直感的で統一的なインターフェースでできるようになったら嬉しくない?

tidyverse パッケージ

  • 上記の概念を実現するためのコアパッケージ群
  • install.packages("tidyverse")でインストール

tidyverse を読み込み

library(tidyverse)
── Attaching packages ─────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 2.2.1     ✔ purrr   0.2.4
✔ tibble  1.4.2     ✔ dplyr   0.7.4
✔ tidyr   0.8.0     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()

読み込まれるパッケージ

  • ggplot2: 可視化
  • dplyr: データの操作
  • tidyr: データを tidy に
  • readr: データの読み書き
  • purrr: 関数型プログラミング
  • stringr: 文字列の操作
  • forcats: 因子型データの操作
  • tibble: tibble というモダンなデータフレーム

本日の主役は

dplyr

特徴

パッケージを使わないやり方より

  • (大きいデータだと特に)
    速い
  • 簡単
    ≒ わかりやすい
  • 他のtidyverseのパッケージと相性がいい

この発表のゴール

  • Rの dplyr パッケージで簡単な集計ができるようになること

本日のデータ

EC サイトのログデータ

データの説明

データの読み込み方

  1. RStudio のプロジェクトを作成
  2. Terminal ペインで以下を実行
    git clone https://github.com/ymattu/sampledata_small
  3. readr パッケージの関数で読み込み
sales <- read_csv("sampledata_small/csv/Sales.csv")
product <- read_csv("sampledata_small/csv/Products.csv")
user_master <- read_csv("sampledata_small/csv/UserMaster.csv")

dplyr

列選択

sales %>%
  select(UserID) %>%
  head()

%>%

パイプ演算子

  • “これまでの処理を次の関数の第1引数として渡す」という働き”(抜粋:: 「宇宙本」)
1:3 %>%
  sum()
[1] 6
# これと全く同じ
sum(1:3)

列選択のやりかたいろいろ

product %>%
  select(starts_with("P")) %>%
  head(4)

列選択のやりかたいろいろ 2

select(product, 1:3) # 列番号が連続している場合
select(product, ProductID:Price) # 列名でも連続していれば同様
select(product, -CreatedDate) # 特定の列を除く
select(product, -4) # 特定の列番号を除く
select(product, starts_with("p"), ignore.case = TRUE) # 大文字小文字を無視
select(product, matches("^(Product|Price)")) # "Product"または"Price"で始まる列を選択

列追加

  • 税込み価格を計算
product %>%
  mutate(zeikomi = Price * 1.08) %>%
  head(4)

行の絞り込み

user_master %>%
  filter(Age >= 20, Sex == "F") # 年齢 20 歳以上の女性

集計

  • グルーピング + 集計
sales %>%
  group_by(UserID) %>%
  summarise(buy_count = n())

ここまでやったところで

パッケージを使わないでできないの?

  • できるものもあります。
  • select, filter あたりはできます。
  • でもめんどくさい
  • しかもデータが大きいと遅い
  • このあたり、私の過去資料もみてね
  • でも$はお手軽だしよく使います。

$で 1 列だけ取り出す

product$Category %>%
  unique()
[1] "雑貨・日用品"           "花・グリーン"          
[3] "食品"                   "衣料品"                
[5] "ヘルス&ビューティー"   "家具・インテリア・家電"

日付の操作

lubridate パッケージ

lubridate

  • 日付の操作をよしなにやってくれるパッケージ
library(lubridate)

ymd("20110604")
[1] "2011-06-04"
ymd(20120101) + years(1)
[1] "2013-01-01"

詳しくはこちらこちらを参照

データハンドリングでの使い所

たくさんあるけど例えば

sales %>%
  mutate(buy_year = year(Timestamp)) %>%
  head()

ここから集計につなげる

ユーザー、年ごとに集計

sales %>%
  mutate(buy_year = year(Timestamp)) %>%
  group_by(UserID, buy_year) %>%
  summarise(buy_count = n()) %>%
  arrange(UserID) %>% 
  head()

その他、代表的な
(面倒くさい)型たち

文字列型

因子型(factor型)

テーブルのマージ

複数のテーブルを考える

a

  x1 x2
1  A  1
2  B  2
3  C  3

b

  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
  • 基本は SQL と同じ

inner_join()

a

  x1 x2
1  A  1
2  B  2
3  C  3

b

  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
inner_join(a, b, by = "x1")
  x1 x2    x3
1  A  1  TRUE
2  B  2 FALSE

left_join()

a

  x1 x2
1  A  1
2  B  2
3  C  3

b

  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
left_join(a, b, by = "x1")
  x1 x2    x3
1  A  1  TRUE
2  B  2 FALSE
3  C  3    NA

full_join()

a

  x1 x2
1  A  1
2  B  2
3  C  3

b

  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
full_join(a, b, by = "x1")
  x1 x2    x3
1  A  1  TRUE
2  B  2 FALSE
3  C  3    NA
4  D NA  TRUE

anti_join()

a

  x1 x2
1  A  1
2  B  2
3  C  3

b

  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
anti_join(a, b, by = "x1")
  x1 x2
1  C  3

FAQ

dplyr とかだと何で
R の標準関数より速いの?

Answer : C++を使っているから

  • dplyrreadrでは、メインの処理を C++でやり、結果を R で受け取る、という構造になっています。
  • Rcpp パッケージが活躍!

たくさんのテーブルを join したい!

例えばこんな感じ(a, b, c 3 つのデータ)

  x1 x2
1  A  1
2  B  2
3  C  3
  x1    x3
1  A  TRUE
2  B FALSE
3  D  TRUE
  x1 x4
1  B 10
2  C 11
3  D 12

こうする…?

a %>%
  full_join(b, by = "x1") %>%
  full_join(c, by = "x1")
  x1 x2    x3 x4
1  A  1  TRUE NA
2  B  2 FALSE 10
3  C  3    NA 11
4  D NA  TRUE 12

数が増えると大変!

たくさんのテーブルを join したい!

Answer : 初心者セッションの範囲をこえますが、
purrrパッケージを使うと簡単です。

datlist <- list(a, b, c)
datlist %>%
  purrr::reduce(~full_join(.x, .y, by = "x1"))
  x1 x2    x3 x4
1  A  1  TRUE NA
2  B  2 FALSE 10
3  C  3    NA 11
4  D NA  TRUE 12

purrr パッケージの参考資料→そろそろ手を出す purrr

まとめ

言いたいこと

  • (イマドキな)R でのデータハンドリングでは tidyverse は必須
  • ぜひ使いこなせるようになりましょう。

本資料について

Enjoy!