「ビジネス活用事例で学ぶ
データサイエンス入門」輪読会
#2
第4章 どの属性の顧客が離脱しているのか?
2016/1/11
自己紹介
しまじろう よう
SIer で SE(金融・情報系)
個人事業主
ソーシャルゲームベンチャーでデータ解析者・PM を担当
現在は不動産仲介プラットフォーム企業で解析基盤周りのお
仕事をしています
C#/Ruby/R/Rails/DB
数学/統計・機械学習/経営/マーケティング
※中小企業診断士を取得予定
Twitter: you_s1025
2
問題の発生
リリース後1年3ヶ月
前月比でユーザ数が減少
原因の解明と対策!
3
問題の特定までの流れ
1.問題発生&仮説 x 3
A.プロモーションに問題
B.イベントが退屈
C.何らかのユーザセグメントに問題あり
2.消去法により上記 C. を支持
3.データの前処理
4.属性別分析
性別 問題なし
年代 問題なし
性別 x 年代 問題なし
デバイス 問題あり
日次の UU 推移をグラフで確認
9月半ばから Android で問題発生
4
前処理
DAU
log_date app_name user_id
2013-08-01 game-01 33754
2013-08-01 game-01 28598
2013-08-01 game-01 30306
# csv の読込
dau <- read.csv("DL76333/R/section4-dau.csv", header=T, stringsAsFactor=T)
user_info <- read.csv("DL76333/R/section4-user_info.csv", header=T, stringsAsFactors=T)
UserInfo
install_date app_name user_id gender generation device_type
2013-08-01 game-01 33754 M 20 iOS
2013-07-16 game-01 28598 M 50 iOS
2013-07-20 game-01 30306 F 30 iOS
# 2テーブルの結合
dau.user.info <- merge(dau, user.info, by=c("user_id", "app_name"))
# 月の情報を追加
dau.user.info$log_month <- substr(dau.user.info$log_date, 1, 7)
DAU-UserInfo
user_id app_name log_date install_date gender generation device_type log_month
33754 game-01 2013-08-01 2013-08-01 M 20 iOS 2013-08
28598 game-01 2013-08-01 2013-07-16 M 50 iOS 2013-08
30306 game-01 2013-08-01 2013-07-20 F 30 iOS 2013-08
5
属性別分析: 性別
# 1-1. 性別で分類
tbl.gender <- table(dau.user.info[, c("log_month", "gender")])
barplot(tbl.gender) # 棒グラフをプロット
# 1-2. 軸の入れ替え
t(tbl.gender)
barplot(t(tbl.gender))
# 1-3. 100分率の積み上げグラフで確認
t(sweep(tbl.gender, 1, rowSums(tbl.gender), "/") * 100)
barplot(t(sweep(tbl.gender, 1, rowSums(tbl.gender), "/") * 100))
性別ごとの推移(8月 9月)以外でも確認
※t (転置)関数でデータの縦と横を入れ替える
月ごとの絶対値では分かりづらい?
sweep/rowSums 関数で各列と行の総和との比を計算
問題が無い事を確認
※sweep, rowSums 関数については補足を参照
6
属性別分析: 年代
# 2-1. 年代で分類
tbl.generation <- table(dau.user.info[, c("log_month", "generation")])
barplot(tbl.generation)
# 2-2. 軸の入れ替え
t(tbl.generation)
barplot(t(tbl.generation))
# 2-3. 100分率の積み上げグラフで確認
t(sweep(tbl.generation, 1, rowSums(tbl.generation), "/") * 100)
barplot(t(sweep(tbl.generation, 1, rowSums(tbl.generation), "/") * 100))
年代ごとの推移(8月 9月)以外でも確認
※t (転置)関数でデータの縦と横を入れ替える
月ごとの絶対値では分かりづらい?
sweep/rowSums 関数で各列と行の総和との比を計算
問題が無い事を確認
7
属性別分析: 性別 x 年代
# 3-1. 年代で分類
dcst.gender_generation <- dcast(dau.user.info,log_month gender+generation,
value.var="user_id", length)
barplot(as.matrix(dcst.gender_generation[, -1]))
# 3-2. 軸の入れ替え
t(as.matrix(dcst.gender_generation[, -1]))
barplot(t(as.matrix(dcst.gender_generation[, -1])))
# 3-3. 100分率の積み上げグラフで確認
t(sweep(dcst.gender_generation[, -1], 1,
rowSums(dcst.gender_generation[, -1]), "/") * 100)
barplot(t(sweep(dcst.gender_generation[, -1], 1,
rowSums(dcst.gender_generation[, -1]), "/") * 100))
性別x年代ごとの推移(8月 9月)以外でも確認
sweep/rowSums 関数で各列と行の総和との比を計算
問題が無い事を確認
※t (転置)関数でデータの縦と横を入れ替える
月ごとの絶対値では分かりづらい?
8
属性別分析: デバイス
# 4-1. デバイスで分類
tbl.device <- table(dau.user.info[, c("log_month", "device_type")])
barplot(tbl.device)
# 4-2. 軸の入れ替え
t(tbl.device)
barplot(t(tbl.device))
# 4-3. 100分率の積み上げグラフで確認
t(sweep(tbl.device, 1, rowSums(tbl.device), "/") * 100)
barplot(t(sweep(tbl.device, 1, rowSums(tbl.device), "/") * 100))
デバイスごとの推移(8月 9月)以外でも確認
※t (転置)関数でデータの縦と横を入れ替える
月ごとの絶対値では分かりづらい?
sweep/rowSums 関数で各列と行の総和との比を計算
9月の比率が明らかに8月と異なる
9
属性別分析: デバイス > 日次 UU 推移
# 日付xデバイス の単位でユーザ数を算出
dau.user.info.device.summary <- ddply(dau.user.info, .(log_date, device_type), summarize, dau=length(user_id))
# log_date を日付型へ変換
dau.user.info.device.summary$log_date <- as.Date(dau.user.info.device.summary$log_date)
# 折れ線グラフの描画
ggplot(dau.user.info.device.summary, aes(x=log_date, y=dau, col=device_type, lty=device_type, shape=device_type)) +
geom_line(lwd=1) +
geom_point(size=4) +
scale_y_continuous(label=comma, limits=c(0, max(dau.user.info.device.summary$dau)))
Android の減
少が原因
これまでの分析で Android に問題が
ありそうな事が分かってきたので本
当に9月に問題が発生しているのか
を日次 UU の推移で確認
9月の2週辺りから明らかに減少
10
まとめ
11
1.問題解決への道筋
1.問題発生に対し仮説を立てる(発散)
2.ヒアリングその他により仮説を絞り込む(集約)
3.必要なデータを えて分析の準備を整える
4.絞り込んだ仮説に従ってデータを分析
5.原因の候補を見つけたらさらに深掘りして確度を上げる
6.得られた知見を元に関係部門と連携
2.クロス集計を活用する
1.2次元のクロスが基本
2.3次元以上のクロスも dcast 関数で扱う事が可能
3.可視化は大事
1.棒グラフ
2.時系列の折れ線グラフ
補足: sweep 関数
12
sweep(x, MARGIN, STATS, FUN= - )
• x: データ
• MARGIN: STATS を適用する方向 1:列 / 2:行
• STATS: 適用する統計量
• FUN: 演算子 or 関数 ※デフォルトは - (減算)
データと統計量との演算を行/列ごとに実施
# 3x5行列を生成
A <- matrix(1:15, 3)
# 各行を1サンプルとする偏差の算出
sweep(A, 1, apply(A, 1, mean))
# 各列の総和に対する比率の算出 ※列ごとに値の合計値が100(%)になる
sweep(A, 2, colSums(A), "/") * 100
補足: rowSums/colSums 関数
13
各行(列)ごとに総和を算出
rowSums(x) / colSums(x)
• x: データ
# 3x5行列を生成
(A <- matrix(1:15, 3))
[,1] [,2] [,3] [,4] [,5]
[1,] 1 4 7 10 13
[2,] 2 5 8 11 14
[3,] 3 6 9 12 15
# 行/列 ごとの総和を計算
[1] 6 15 24 33 42rowSums(A) #=> [1] 35 40 45
colSums(A) #=> [1] 6 15 24 33 42
補足: merge 関数①
14
2つのデータを結合する
merge(x, y, by, all)
• x: データ1
• y: データ2
• by: 結合に用いる項目(キー)名を配列で指定
• by.x: x 側で用いる結合のキーを指定 ※x, y 側で項目名が異なる場合に使用
• by.y: y 側で用いる結合のキーを指定 ※x, y 側で項目名が異なる場合に使用
• by: x, y 共に指定された結合のキーを用いる
• all: 結合する際に主軸とするデータ(x, y)を指定
• all.x=TRUE x に y を紐付ける ※左外部結合に相当
• all.y=TRUE y に x を紐付ける ※右外部結合に相当
• all=T x, y の両方が主軸になる ※両側外部結合に相当
※指定しない場合は内部結合に相当
# データの準備
x <- data.frame(id=1:5, value=seq(10, 50, 10))
y <- data.frame(key=4:8, score=seq(400, 800, 100))
x
id value
1 10
2 20
3 30
4 40
5 50
y
key scoer
4 400
5 500
6 600
7 700
8 800
補足: merge 関数②
15
y
x
#内部結合: 合致する行だけが残る
merge(x, y, by.x= id , by.y= key )
id value score
4 40 400
5 50 500
#左外部結合: x の側だけ全て残る
merge(x, y, by.x= id , by.y= key , all.x=T)
id value score
1 10 NA
2 20 NA
3 30 NA
4 40 400
5 50 500
#両側外部結合: x, y の両方とも全て残る
merge(x, y, by.x= id , by.y= key , all=T)
id value score
1 10 NA
2 20 NA
3 30 NA
4 40 400
5 50 500
6 NA 600
7 NA 700
8 NA 800
#右外部結合: y の側だけ全て残る
merge(x, y, by.x= id , by.y= key , all.y=T)
id value score
4 40 400
5 50 500
6 NA 600
7 NA 700
8 NA 500
T
T
F
F

ビジネス活用事例で学ぶデータサイエンス入門 #2

  • 1.
  • 2.
    自己紹介 しまじろう よう SIer でSE(金融・情報系) 個人事業主 ソーシャルゲームベンチャーでデータ解析者・PM を担当 現在は不動産仲介プラットフォーム企業で解析基盤周りのお 仕事をしています C#/Ruby/R/Rails/DB 数学/統計・機械学習/経営/マーケティング ※中小企業診断士を取得予定 Twitter: you_s1025 2
  • 3.
  • 4.
    問題の特定までの流れ 1.問題発生&仮説 x 3 A.プロモーションに問題 B.イベントが退屈 C.何らかのユーザセグメントに問題あり 2.消去法により上記C. を支持 3.データの前処理 4.属性別分析 性別 問題なし 年代 問題なし 性別 x 年代 問題なし デバイス 問題あり 日次の UU 推移をグラフで確認 9月半ばから Android で問題発生 4
  • 5.
    前処理 DAU log_date app_name user_id 2013-08-01game-01 33754 2013-08-01 game-01 28598 2013-08-01 game-01 30306 # csv の読込 dau <- read.csv("DL76333/R/section4-dau.csv", header=T, stringsAsFactor=T) user_info <- read.csv("DL76333/R/section4-user_info.csv", header=T, stringsAsFactors=T) UserInfo install_date app_name user_id gender generation device_type 2013-08-01 game-01 33754 M 20 iOS 2013-07-16 game-01 28598 M 50 iOS 2013-07-20 game-01 30306 F 30 iOS # 2テーブルの結合 dau.user.info <- merge(dau, user.info, by=c("user_id", "app_name")) # 月の情報を追加 dau.user.info$log_month <- substr(dau.user.info$log_date, 1, 7) DAU-UserInfo user_id app_name log_date install_date gender generation device_type log_month 33754 game-01 2013-08-01 2013-08-01 M 20 iOS 2013-08 28598 game-01 2013-08-01 2013-07-16 M 50 iOS 2013-08 30306 game-01 2013-08-01 2013-07-20 F 30 iOS 2013-08 5
  • 6.
    属性別分析: 性別 # 1-1.性別で分類 tbl.gender <- table(dau.user.info[, c("log_month", "gender")]) barplot(tbl.gender) # 棒グラフをプロット # 1-2. 軸の入れ替え t(tbl.gender) barplot(t(tbl.gender)) # 1-3. 100分率の積み上げグラフで確認 t(sweep(tbl.gender, 1, rowSums(tbl.gender), "/") * 100) barplot(t(sweep(tbl.gender, 1, rowSums(tbl.gender), "/") * 100)) 性別ごとの推移(8月 9月)以外でも確認 ※t (転置)関数でデータの縦と横を入れ替える 月ごとの絶対値では分かりづらい? sweep/rowSums 関数で各列と行の総和との比を計算 問題が無い事を確認 ※sweep, rowSums 関数については補足を参照 6
  • 7.
    属性別分析: 年代 # 2-1.年代で分類 tbl.generation <- table(dau.user.info[, c("log_month", "generation")]) barplot(tbl.generation) # 2-2. 軸の入れ替え t(tbl.generation) barplot(t(tbl.generation)) # 2-3. 100分率の積み上げグラフで確認 t(sweep(tbl.generation, 1, rowSums(tbl.generation), "/") * 100) barplot(t(sweep(tbl.generation, 1, rowSums(tbl.generation), "/") * 100)) 年代ごとの推移(8月 9月)以外でも確認 ※t (転置)関数でデータの縦と横を入れ替える 月ごとの絶対値では分かりづらい? sweep/rowSums 関数で各列と行の総和との比を計算 問題が無い事を確認 7
  • 8.
    属性別分析: 性別 x年代 # 3-1. 年代で分類 dcst.gender_generation <- dcast(dau.user.info,log_month gender+generation, value.var="user_id", length) barplot(as.matrix(dcst.gender_generation[, -1])) # 3-2. 軸の入れ替え t(as.matrix(dcst.gender_generation[, -1])) barplot(t(as.matrix(dcst.gender_generation[, -1]))) # 3-3. 100分率の積み上げグラフで確認 t(sweep(dcst.gender_generation[, -1], 1, rowSums(dcst.gender_generation[, -1]), "/") * 100) barplot(t(sweep(dcst.gender_generation[, -1], 1, rowSums(dcst.gender_generation[, -1]), "/") * 100)) 性別x年代ごとの推移(8月 9月)以外でも確認 sweep/rowSums 関数で各列と行の総和との比を計算 問題が無い事を確認 ※t (転置)関数でデータの縦と横を入れ替える 月ごとの絶対値では分かりづらい? 8
  • 9.
    属性別分析: デバイス # 4-1.デバイスで分類 tbl.device <- table(dau.user.info[, c("log_month", "device_type")]) barplot(tbl.device) # 4-2. 軸の入れ替え t(tbl.device) barplot(t(tbl.device)) # 4-3. 100分率の積み上げグラフで確認 t(sweep(tbl.device, 1, rowSums(tbl.device), "/") * 100) barplot(t(sweep(tbl.device, 1, rowSums(tbl.device), "/") * 100)) デバイスごとの推移(8月 9月)以外でも確認 ※t (転置)関数でデータの縦と横を入れ替える 月ごとの絶対値では分かりづらい? sweep/rowSums 関数で各列と行の総和との比を計算 9月の比率が明らかに8月と異なる 9
  • 10.
    属性別分析: デバイス >日次 UU 推移 # 日付xデバイス の単位でユーザ数を算出 dau.user.info.device.summary <- ddply(dau.user.info, .(log_date, device_type), summarize, dau=length(user_id)) # log_date を日付型へ変換 dau.user.info.device.summary$log_date <- as.Date(dau.user.info.device.summary$log_date) # 折れ線グラフの描画 ggplot(dau.user.info.device.summary, aes(x=log_date, y=dau, col=device_type, lty=device_type, shape=device_type)) + geom_line(lwd=1) + geom_point(size=4) + scale_y_continuous(label=comma, limits=c(0, max(dau.user.info.device.summary$dau))) Android の減 少が原因 これまでの分析で Android に問題が ありそうな事が分かってきたので本 当に9月に問題が発生しているのか を日次 UU の推移で確認 9月の2週辺りから明らかに減少 10
  • 11.
  • 12.
    補足: sweep 関数 12 sweep(x,MARGIN, STATS, FUN= - ) • x: データ • MARGIN: STATS を適用する方向 1:列 / 2:行 • STATS: 適用する統計量 • FUN: 演算子 or 関数 ※デフォルトは - (減算) データと統計量との演算を行/列ごとに実施 # 3x5行列を生成 A <- matrix(1:15, 3) # 各行を1サンプルとする偏差の算出 sweep(A, 1, apply(A, 1, mean)) # 各列の総和に対する比率の算出 ※列ごとに値の合計値が100(%)になる sweep(A, 2, colSums(A), "/") * 100
  • 13.
    補足: rowSums/colSums 関数 13 各行(列)ごとに総和を算出 rowSums(x)/ colSums(x) • x: データ # 3x5行列を生成 (A <- matrix(1:15, 3)) [,1] [,2] [,3] [,4] [,5] [1,] 1 4 7 10 13 [2,] 2 5 8 11 14 [3,] 3 6 9 12 15 # 行/列 ごとの総和を計算 [1] 6 15 24 33 42rowSums(A) #=> [1] 35 40 45 colSums(A) #=> [1] 6 15 24 33 42
  • 14.
    補足: merge 関数① 14 2つのデータを結合する merge(x,y, by, all) • x: データ1 • y: データ2 • by: 結合に用いる項目(キー)名を配列で指定 • by.x: x 側で用いる結合のキーを指定 ※x, y 側で項目名が異なる場合に使用 • by.y: y 側で用いる結合のキーを指定 ※x, y 側で項目名が異なる場合に使用 • by: x, y 共に指定された結合のキーを用いる • all: 結合する際に主軸とするデータ(x, y)を指定 • all.x=TRUE x に y を紐付ける ※左外部結合に相当 • all.y=TRUE y に x を紐付ける ※右外部結合に相当 • all=T x, y の両方が主軸になる ※両側外部結合に相当 ※指定しない場合は内部結合に相当 # データの準備 x <- data.frame(id=1:5, value=seq(10, 50, 10)) y <- data.frame(key=4:8, score=seq(400, 800, 100)) x id value 1 10 2 20 3 30 4 40 5 50 y key scoer 4 400 5 500 6 600 7 700 8 800
  • 15.
    補足: merge 関数② 15 y x #内部結合:合致する行だけが残る merge(x, y, by.x= id , by.y= key ) id value score 4 40 400 5 50 500 #左外部結合: x の側だけ全て残る merge(x, y, by.x= id , by.y= key , all.x=T) id value score 1 10 NA 2 20 NA 3 30 NA 4 40 400 5 50 500 #両側外部結合: x, y の両方とも全て残る merge(x, y, by.x= id , by.y= key , all=T) id value score 1 10 NA 2 20 NA 3 30 NA 4 40 400 5 50 500 6 NA 600 7 NA 700 8 NA 800 #右外部結合: y の側だけ全て残る merge(x, y, by.x= id , by.y= key , all.y=T) id value score 4 40 400 5 50 500 6 NA 600 7 NA 700 8 NA 500 T T F F