現場的によくある分析のやり方
今回はちょっと実際の現場のデータサイエンス(データマイニング)の話をしてみようと思います。現在の僕の仕事は、基本的には
- 「Webコンテンツプラットフォーム上を回遊しているユニークユーザー(UU)の行動データをHadoopクラスタから抽出」し、
- 「そのデータに基づいてUUが『特定のタイミングor期間』*1に『どんなこと』*2をすれば『こちらが期待する結果』*3を残してくれるか」を、
データマイニングを用いて調べるというものです。
この辺のやり方というのは、データサイエンスによるビッグデータ分析を実践している企業の現場であれば、恐らくですがそんなに大きくは変わらないと思います。上記の目的に合ったデータマイニング手法ももちろん沢山あって、例えば
- 決定木・回帰木
- ロジスティック回帰
- サポートベクターマシン(SVM)*4
- ランダムフォレスト
辺りを使えば何かしらの分類ができると思います。というわけで、ここではそれらを使うまでのデータの前処理についてちょっと書いてみます。
Hiveで生テーブルを抽出する
大体どこの現場でも同じだと思いますが、例えばHadoopクラスタに流し込んでいるユーザーの行動ログは「何かしらのアクション(クリック・タップなど)をしたらその都度ユーザーID、アクションID、日付and/or日時を1行に羅列して」記録していく、というようなものと仮定しましょう。
そこで、ここでは「4月5日に何かのアクションをしたユーザーがその後3日間のうちどこかで再びログインしてくれる(3日間定着)」ために、どんなアクションをしてもらえば良いのか?を調べることにしましょう。想定としては、分析対象はスマホからの画像投稿が可能なスマホ向けSNSサービスであり、'login_table'がテーブル名で、ユーザーIDを収めるカラムとして'user_id'、アクションIDには'action_id'が割り振られているとします。この場合、
3日間定着したUUのテーブルを抽出する場合
SELECT DISTINCT x.user_id, x.action_id FROM ( SELECT DISTINCT user_id, action_id FROM login_table WHERE action_date = '2013-04-05' )x JOIN ( SELECT DISTINCT user_id FROM login_table WHERE action_date >= DATE_ADD('2013-04-05',1) AND action_date <= DATE_ADD('2013-04-05',3) )y ON x.user_id = y.user_id
3日間定着せず、離脱したUUのテーブルを抽出する場合
SELECT DISTINCT x.user_id, x.action_id FROM ( SELECT DISTINCT user_id, action_id FROM login_table WHERE action_date = '2013-04-05' )x LEFT OUTER JOIN ( SELECT DISTINCT user_id FROM login_table WHERE action_date >= DATE_ADD('2013-04-05',1) AND action_date <= DATE_ADD('2013-04-05',3) )y ON x.user_id = y.user_id WHERE y.user_id IS NULL
というようなHiveクエリ*5を書くことになるはずです。ちなみに僕自身は、その辺の検証仮説に関連するロジックの部分はできるだけHiveクエリの段階で実装しておくようにしています。そうすれば、その後のデータマイニングのところは固定しておけば済むからです。
ともあれ、これらのクエリで得られるテーブルは概ね以下のような形になります。
ユーザーID | アクションID |
---|---|
1001 | 記事閲覧 |
1001 | コメント閲覧 |
1001 | コメント投稿 |
1001 | 検索 |
1001 | カメラ起動 |
1001 | 画像投稿 |
1002 | 記事閲覧 |
1002 | 検索 |
1003 | 記事閲覧 |
1003 | コメント閲覧 |
1003 | コメント投稿 |
1004 | 記事閲覧 |
1004 | コメント閲覧 |
1004 | カメラ起動 |
1004 | 画像投稿 |
1004 | 記事投稿 |
... | ... |
DISTINCTを入れてあるので各アクションとも1回ずつ現れ、基本的に時間順序などは無視しています。ともかく、「4月5日に個々のUUがどんなアクションをしたか?」が単純に並べられたテーブルがこれで取得できます。
素性ベクトル+学習ラベルのテーブル(クロス集計表)に直す
このテーブルに対してデータマイニングを実施して仮説の検証を行うわけですが、問題になるのがデータの形式です。例えば上記の4手法をRで使うとすれば、
- 決定木・回帰木: {mvpart}
- ロジスティック回帰: {base}*6
- サポートベクターマシン(SVM): {kernlab}, {e1071}, etc.
- ランダムフォレスト: {randomForest}
といったRパッケージを利用することになります。これらは全て素性(そせい)ベクトル+分類ラベルという形のデータを与えることで、動くものです*7。
よって、先ほどHiveで取得してきた生テーブルを上記の例にならえば、1001 & 1004が定着、1002 & 1003が離脱したとすると
ユーザーID | 記事閲覧 | 記事投稿 | コメント閲覧 | コメント投稿 | カメラ起動 | 画像投稿 | 検索 | 定着or離脱 |
---|---|---|---|---|---|---|---|---|
1001 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 定着 |
1002 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 離脱 |
1003 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 離脱 |
1004 | 1 | 1 | 0 | 1 | 1 | 1 | 0 | 定着 |
... | ... | ... | ... | ... | ... | ... | ... | ... |
というようなテーブルに書き換える必要がある、というわけです。確かに行方向に素性ベクトル+分類ラベルが並んだテーブルになっていますが、これは要するにクロス集計表そのものですね。なので、とにかく上記のテーブルをクロス集計さえすれば、簡単にRでデータマイニングするところまで持って行けるというわけです。
そのためには何かしらのコードを書いて処理しても良い*8のですが、それほど大きなテーブルでなければExcelのピボットテーブル機能を使えば簡単にできます。
やり方としては簡単で、Excel上に上記のテーブルをベタッと貼り付けて、2行とも選択した上で「挿入」タブから「ピボットテーブル」をクリックするだけです*9。あとはこのデータに対して、formulaを'「定着or離脱」~.,'として上記のRの関数群のどれかに引数として与えれば、手っ取り早くデータマイニングの結果が得られます。
もちろん、別にデータマイニングなんか駆使しなくても、個々の素性(記事閲覧・コメント閲覧・カメラ起動etc.)ごとに合計値を計算しても、それなりに意味のある結果が得られます。ですが、分類ラベルに従って機械学習などで分類をかけた方が、少なくとも定着・離脱の分類ラベル別に個々の素性ごとに合計値を計算するなどするだけよりも、さらに有意義な知見が得られると思います。
*1:その時々のマーケ戦略に依存する。例えば新規UUがユーザー登録した初日とか、ユーザー登録した週とか、あるコンテンツでイベントを打った週とか
*2:これも規模次第。個々のアプリのレベルなら「どのボタンを押したか」「どのカードを引いたか」だし、プラットフォーム全体ならズバリ「どのゲームをやったか」が該当する
*3:よく使われるのが「定着」「課金」などのコンバージョン
*4:SVMだけは分類と予測はできるけれども、識別関数の性質上どの変数が分類&予測に貢献しているかを調べることはできない。現場の感覚としてはその高い汎化性能を生かして予測一本に絞った方が使いやすいと思われる
*5:SQLに類似するHive上で動作するクエリ言語。MapReduceで動くように内部的に解釈することを前提としているので、普通のSQLと同じように出来ること・出来ないことがあります。そして糞コードというか糞クエリでごめんなさい
*6:デフォルトパッケージなので特にインストール不要
*7:いずれも検証仮説を表すRのformulaとして'y~a+b+c'であったり'y~.'といった回帰関係表現を引数として与える必要があるが、基本的にこれは「分類ラベル」~「素性1」+「素性2」+...の形で書ける
*8:僕はPythonで簡単なコードを書いてやってます。というか10万行とか超えてくると手作業では時間がかかり過ぎて煩わしいので
*9:実は上記のテーブルのさらに右隣に全部1だけが入ったカラムを付け足してから実行する、先にユーザーIDの隣に定着vs.離脱ラベルを記述したカラムを別に作っておくなど、ちょっとしたコツがある