日本全国各所で猛暑日が続き、
facebookは野外フェスと海と海外旅行とビアガーデンの写真が溢れ始めております。

しかし猛暑だろうが極寒だろうが、
データ分析官は、週末のビールを信じて
黙々と(たまに独り言と、思い出し笑いをしながら)RとSQLを回す日々なはずです。

ところが、この週末ビールを阻む大いなる壁があります。
そう、もうお気づきかと思いますが、みなさんおなじみ”既読スルー”です。
飲みに誘ってもレスポンスが無いのですから飲みに行けません。(一人飲みをするには勇気が足りない)

「え?既読スルーて、友人の間でもカジュアルに起こる事なの?」と思った貴君、
私だってこの状態が当たり前だとは思っておりません。当然疑問に感じています。

メールコミュニケーションの時代は、返信が来なかろうが
「もう寝たんだろう」「海外旅行中なんだろう」「ケータイ紛失したんだろう」
「深爪してメールを打つのも困難なんだろう」などと、
あり得ないほどオプティミストな思考を人誰しもが身につけて生きてきたかと思いますが、
今のチャットアプリの時代では「既読」がありますので、そうはいきません。


前置きが長くなりましたが、本稿では、
LINEのやりとりデータをランダムフォレストを用いて機械学習し、
“返信”と”既読スルー”の予測モデルを構築
」します。
これによって、オプティミストな思考をしなくとも、
木曜日20時以降で、伊藤に対して飲みの用事で送ったのであれば78%返信が来なくて当然(キリッ
と論理的に判断する事ができ、もう一歩踏み込むと、既読スルーされないためには、
何曜日何時、誰に、どのようなトピックでLINEするべきか、がわかります。


以下が今回分析に用いた私の直近のLINEデータとなります。
※本物のデータを用いないと(私のプライベートにとって)意味がありませんので、そのままのデータです。
 相手の名前のみ偽名です。
※LINEデータの取得方法は過去エントリー「LINEのトーク履歴を取得する。」を参照くださいませ。
※ローデータから、各種前処理・分類をしてパラメータを生成しております。
※やり取りが発生以降は、連続したセッションとしてまとめております。
 相手から最初のレスポンスがあるまでのやりとりのみ抽出しております。


変数は以下の通りです。
week 曜日
time 送信時間
topic drink(飲み)、play(遊び)、question(質問)、
birthday(誕生日祝)、information(情報共有)の5種
to 送信相手
length 文字数
result yes(24時間以内に返信あり)
no(24時間以内に返信無し)


得られたデータは下記となります。
weektimetopictolengthresult
tue22drinksasaki8no
fri23drinksasaki12no
fri14drinksasaki15yes
sat12drinksasaki11yes
sat15drinksasaki19yes
sat15drinksasaki57yes
sat18drinksasaki16yes
sat19drinksasaki13yes
sun19playsasaki25yes
tue11playsasaki17yes
mon9playsasaki36no
sun16questionmatsuda9yes
fri17playmatsuda19yes
wed9playkonishi29yes
wed23playkonishi36no
fri8playkonishi44yes
sat22drinksatomura24yes
fri22playsatomura65yes
sat0birthdayhirata54yes
tue11questionhirata75yes
fri10drinkhirata183no
mon11drinkkuwano60no
wed21playkuwano63no
sat13playkuwano30yes
sat12playkuwano44no
sun1playkuwano40yes
mon3informationsato105yes
sat20drinksato185no
tue15drinksato100no
wed22drinksato86no
wed2drinksato133no
mon11informationsato350yes
wed12informationsato395yes
thu21drinksato165yes
sat12drinksato17yes
sun12drinksato45yes
sat10informationhashimoto187no
sat19drinkhashimoto72yes
thu18drinkhashimoto44yes
fri13drinkhashimoto71yes
thu9drinkhashimoto161yes
thu12drinkfujimoto277yes
wed18informationsaito74no
tue10drinkmatsumura67no
sun4drinkmatsumura83no
thu22drinkmatsumura20no
sat19drinkmatsumura43yes
thu19drinkmatsumura55yes
mon12informationmatsumura89no
fri8questionmurata165yes
sat18drinkmurata124yes


まずは、上記データをRに取り込み、データ分析する前に、可視化してデータ探索してみます。
おなじみggplotを使っていきましょう。



縦軸が送信曜日、横軸が送信時間、
凡例は、緑色が「返信あり」、赤色が「返信なし」です。
※曜日の並びがアルファベット順になっていること、ご注意くださいませ、。

random_forest3



次に、送信時間と送信相手を見てみましょう。



random_forest1

縦軸が送信時間、横軸がLINEの送信先の相手、
凡例は、緑色が「返信あり」、赤色が「返信なし」です。

fujimotoさん、matsudaさん、murataさん、satomuraさん、どんなときもありがとうございます。
matsumuraさん、ほぼ返信がありませんが、どういうことでしょうか。




最後に、縦軸が送信時間、横軸が文字数、
凡例は、緑色が「返信あり」、赤色が「返信なし」です。


random_forest2

文字数が長くなるほど返信があるようで何よりです。
とはいえ、200文字近いLINEを送って既読スルーされたときもあるようです。
これは送る方も、送られた方も、辛いです。



さて、ではこのデータを解析していきましょう。
今回はランダムフォレストを用いようと思います。
ランダムフォレストは決定木の応用となります。

なお決定木は、ジニ係数などの分岐基準を用いて、
最も結果を分けるのにふさわしいパラメータを選択し続けていくものです。

なぜ、決定木では充分ではないのでしょうか。
そこで、ランダムフォレストの簡単な説明もかねて、
まずは決定木で本データを解析してみましょう。






decision_tree


「曜日で分類して、”月”・”火”・”水”に送ると返信来ないことのほうが多いから、
 木曜日以降のほうがいい」という結果です。
データ分析官にデータ依頼して1週間待って出てきたのがこれだけだと震えますよね。
もっと他にいろんな要素とか無いものだろうか、とお思いかと思います。
そこで、決定木では、「繁らせる(分岐を増やす)」ことと「プルーニング(分岐を減らす)」を
適宜行っていく事で、精度および実用性を上げていきます。

R上では、controlとして指定します。
木の複雑さを表すcpが、経験上デフォルトだと厳しいと思いますので、少し緩めてみます。



desicion_tree2


分岐上限が増え、決定木らしくなりました。
上記のようなcpをどの程度にしたら良いかは、別途方法があるのですが、
今回は決定木はここまでにしておきます。


さて、このようにして得られる決定木は解釈が容易な反面、
必ずしも最適なパラメータが選択されるとは限らない、という欠点があります。
たとえば、異常値や影響の大きいパラメータの存在などによって、
最初の分岐が(最適なものではなく)決まると以降の分岐は、その前提で進んでいくことになります。

そこで、全てのパラメータを用いるのではなく、任意のパラメータを部分的にランダムに選択してきて、
そこから決定木を作る、ということを複数回行うことで、
一部のパラメータのみに引っ張られることが無いようにし、
その複数の決定木の結果を多数決(ないしは平均値)で予測するという考えが
ランダムフォレスト、となります。


R上では非常にシンプルに下記で実行できます。




本データでは下記の結果となりました。

Call:
randomForest(formula = result ~ ., data = private_mail)
Type of random forest: classification
Number of trees: 500
No. of variables tried at each split: 2

OOB estimate of error rate: 43.14%
Confusion matrix:
no yes class.error
no 4 14 0.7777778
yes 8 25 0.2424242

noyesclass.error
no4140.77
yes8250.24

yesもnoのときも、どちらもyesと予測してしまっている数が多いようです。
ちなみに現時点でのエラー率は43.14%のようです。


まずは今回、yes=33、no=18と不均衡データとなっておりますので、
重みをつけて調整することにしましょう。
また、ランダムフォレストは、全パラメータの中からどれだけの数を各決定木で選択するかを、
“mtry”、という値で調整することが出来ます。

tuneRFというコマンドを用いることで、自動的に最適なmtryパラメータを探索できます。
最初にパラメータ、次に目的変数を記入します。
※6行目が目的変数で、それ以外が説明変数、yesとnoの数が33:18で逆に重みをかけて調整するときは下記。



tuneRF


縦軸がエラー率ですので、各決定木に使うパラメータ数は2が良いようなので、mtry=2とします。
ではもう一度実行してみましょう。ちなみに作成する決定木の数もntreeで指定する事が出来ます。
この数が少ないと毎回結果がブレますので、大きめに2000としておきましょう。
まとめると下記となります。




Call:
randomForest(formula = result ~ ., data = private_mail, mtry = 2, weight = c(18, 33), ntree = 2000)
Type of random forest: classification
Number of trees: 2000
No. of variables tried at each split: 2

OOB estimate of error rate: 37.25%
Confusion matrix:
no yes class.error
no 4 14 0.7777778
yes 5 28 0.1515152

noyesclass.error
no4140.77
yes5280.15


エラー率は37.25%に改善されました。
yesの時にnoと予測する点が改善されたようです。
本当は、noのときにyesと予測してしまう点を改善したかったのですが。
(返信が来るときでも来ないと予測する機会損失よりも、
返信が来ないのに来ると予測するリスクを改善したかった。
いえ、ある意味、一歩踏み出すためのポジティブな予測モデルとして、
これでよいのかもしれません。)


最後にこのモデルで計算された各変数の重要度(ジニ係数)を下記のようにして算出しておきます。



variableMeanDecreaseGini
week5.235269
time5.070753
topic1.697444
to5.415680
length5.221605

もしパラメータが多いデータの場合は、上記をもとに、
パラメータを重要なものに絞ることが出来ます。
※パラメータを重要なものに絞るためにランダムフォレストを前処理として行い、
 以降を別の処理にかけることも考えられます。

さて、以上で予測モデルは完成となります。
LINEの返答率の結果ですが、
「誰に出すか」が最も重要で、次に「曜日」が影響あるようです。
※あくまで私個人のLINE履歴の傾向であることに留意くださいませ。

作成されたモデルに、新たなデータを入力として用いれば、その入力の結果を予測出来ます。

random_forest5

では、最後に自信を持って、
fujimotoさん、matsudaさん、murataさん、satomuraさん、どんなときもありがとうございます。
matsumuraさん、ほぼ返信がありませんが、どういうことでしょうか。