読者です 読者をやめる 読者になる 読者になる

ビズオーシャンでのインターン日記

プログラミング未経験の文系大学生がインターンシップで頑張る日記です

vol.7 ロジスティック回帰 クリックの確率の予測

今回は、

ユーザーの過去のクリック状況をもとに
今後のクリック状況を予測し、
クリックする確率の高いユーザーに的を絞ってDMを送れば
より効率的にクリック数を得られるのではないか?


ということで、
ユーザーのクリック状況をロジスティック回帰で分析していきます。





ロジスティック回帰とは回帰分析のうちのひとつですが、
量的データを分析する線形回帰とは違い、

分類に注目して発生確率を予測する分析方法です。


ここではユーザー情報の分類に注目して
クリックするかしないか、する確率を予測します。






今回のロジスティック回帰の手順としては、

①データの整理
②トレーニングデータの作成
③トレーニングとテスト
④予測

という感じです。



①データの整理

ユーザー情報を選択肢2つで分類して整理します。
(Trueなら1、Falseなら0と表示)



あるユーザーは男性かそうでないか、営業職かそうでないか、2017年2月にクリックしたかしてないか、など、
ほぼ全ての情報を1と0で表します。

(ただ、年齢や最終ログイン日から今日までの日数などは普通にそのままの数字で)



選択肢は2つしか設けられないので、
今はクリック数は見ません。クリック有無だけです。




整理して表にするとこんな感じになります。


f:id:yuka_nakayama:20170523115250p:plain


②トレーニングの作成


今回は、
説明変数X:1,2月のクリック状況
従属変数y:3月のクリック状況  として、

1,2月のクリック状況が3月のクリック状況を決定する、と考えます。



また、ロジスティック回帰には

・モデル作成のためのトレーニング
・モデルの精度を確かめるテスト

という段階があるので
データをトレーニング用とテスト用に分けます。




分け方に関しては
過去のデータを全体的にトレーニング用・テスト用に分ける、
過去のデータのより古いほうをトレーニングに使ってより新しいほうのデータでテストする、
など いくつかありますが、今回は前者の分け方です。




トレーニング用とテスト用に分けるには、
関数「train_test_split」を使います。

from sklearn.cross_validation import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X_std, y_df, test_size=0.2, random_state=0)

 
※X_train:トレーニング用の1,2月のデータ
 y_train:トレーニング用の3月のデータ
 X_test:テスト用の1.2月のデータ
 y_test:テスト用の3月のデータ
 X_std:1月のクリックと2月のクリックを標準化したもの
 y_df:3月のクリックの有無


ここではユーザーの20%をランダムに選んで、
その20%クリック状況をテスト用にします。
残りの80%はトレーニング用です。



ちなみに、80%20%というのは特に決まりはないようです。


③トレーニングとテスト



X_trainとy_trainから
Xとyの関係を表す式(つまりモデル)を求めて、

その式にX_testを当てはめて求まるyと
実際のy_testを比較して精度を確認します。


④予測

精度を確認したうえで、
X_testから3月のクリックの確率を予測します。

f:id:yuka_nakayama:20170523133209p:plain




確率が高い順にユーザーを並べて
上位の50%のユーザーによるクリック数予測を見てみると、

分析に用いた全ユーザーによる実際のクリック数の81%を占めていました。


f:id:yuka_nakayama:20170523133242p:plain





つまり、DMを送るユーザーを半分に減らしても、
減らす前の80%のクリック数は得られる!ということです。





<メモ>

平均値の異なるデータを比較したいときは
それぞれのデータを標準化する。

標準化…平均値0、分散1になるように値を変換すること。

{(データの任意の値)ー(データの平均値)}/(標準偏差


関数はStandScalerを使う。




clf = LogisticRegression(n_jobs=-1, verbose=1)

n_jobs=1で並列計算ができる。
並列計算すると、問題を解く過程をより小さいものにできるので、効率が上がる。らしい。(ウィキペディア参考)

vol.6 データを数字に変換


4月に入ってからは
機械学習でデータ分析するための下準備を進めていますが、

機械学習でデータ分析をするには
すべてのデータを数字に変換する必要があるので
今回はそれを中心に書いていって、


そのほか、
わたしが新しく知った関数がいくつかあるので、
最後のほうに備忘録として書こうと思います。




ユーザーに関するあらゆるデータを数字に変換


といっても、ユーザーのデータには

①連続値:数字で説明できるデータ(従業員数や年収など)
②離散値:数字で説明できないデータ(役職や業種など)

があって、、、



①従業員数や年数など


これらは順番に並べられそうなので、
その順番通りに数字を振っていく。



例えば従業員数、

# employee_scale
member_df["employee_scale_num"] = member_df['employee_scale'].map({
    '1~9名':0,
    '10~49名':1,
    '50~99名':2,
    '100~299名':3,
    '300~499名':4,
    '500~999名':5,
    '1000~2999名':6,
    '3000~4999名':7,
    '5000~9999名':8,
    '10000名以上':9,
  })
#member_df


「1~9名」や「10~49名」は
そのままでは単なる文字列なので、
「map」で連続値に変換し、意味を持たせる。



②役職や業種など

こっちは数字にして並べることができないので、
真偽値に変換してデータフレームの表を埋めていく。

真偽値に変換するには
Pandasの「get_dummies()」を使用

bool_columns = ['managerial_position', 'job_spec1', 'industry']
pd.set_option("display.max_columns", 560)
member_df = pd.get_dummies(member_df, columns=bool_columns)


・bool_columns = ['managerial_position', 'job_spec1', 'industry']
役職、職種、業種などで同じ作業をしていくので、
「bool_columns」で括ると1回の作業で済む

・pd.set_option("display.max_columns", 560)
columns数を560に設定

・member_df = pd.get_dummies(member_df, columns=bool_columns)
get_dumiesで真偽値に変換
(既成のmember_dfのcolumns部分に[bool_columns]をもってきて)真偽値に変換、
変更を上書きされたデータフレームとしてのmember_dfを表示



f:id:yuka_nakayama:20170425110155p:plain


これはデータフレーム全体のごく一部でしかないので
分かりずらいが、


1番上のユーザーの役職は「その他」、
上から5番目のユーザーの役職は「一般社員・職員」

であることを示す

(真であれば1、偽であれば0)






備忘録

⑴ランダム抽出

機械学習のための下準備をするうえで
データが大きすぎると時間やお金がかかってしまうので、


ユーザー数を少なめにして、データを小さくしてスキャンを試みる。


たくさんのユーザーからたとえば100人のみをランダムに抽出するとき、

SELECTで
 rand() を使って全ユーザーに0以上1未満の数をランダムに振る、
 (振った数を random とおく)
ORDER BYで
 random を降順に並べる

で、LIMIT 100で絞ると、
結果としてユーザー100人をランダム抽出できたことになる。


⑵日数の差を求める

例えば入会日から現在までの日数を求めるとき、


DATEDIFFで期間(日数)を求める
期間…CURRENT_TIMESTAMP(サーバーの現在の日時) と
   TIMESTAMP(create_date)(入会した日時)  の期間

DATEDIFF(CURRENT_TIMESTAMP(),TIMESTAMP(create_date)) as create_date_duration

vol.5 Pandasとdataframe


3月13日は、作成したデータフレームからグラフを作ってみましたが、

その前に備忘録として


Pandasとは何なのか?
何のためにdataframeするのか?


自分なりの解釈で書いていきたいと思います。




Pandasについて(備忘録1)

ウィキペディア( https://ja.wikipedia.org/wiki/Pandas )によると、pandasとは
プログラミング言語Pythonにおいて、データ解析を支援する機能を提供するライブラリ」らしいです。なるほど。


いや、プログラミングで使うライブラリとは?


プログラミング初心者は、ライブラリとか言われても、
プログラミング自体とライブラリとの関係性がいまいち理解できないというか、
ほかの人はどうか分かりませんが私はそうでした。



そこで、ライブラリに関して分かりやすい記事を見つけたので
シェアしておきます。
http://wa3.i-3-i.info/word1473.html



このページに書かれていることをもとにpandasを改めて説明すると、

Pythonでデータ解析をするとき、
自分で地道にコードを書いていくことも出来なくはないけど、
事前にpandasをインポートしておくとPythonのままpandasの機能(解析に便利なやつ)を使えるよ、   

という感じでしょうか。

dataframeについて(備忘録2)


dataframeに関しては、
正直なところ調べてもよく分かりませんが、

とりあえず現時点で調べて分かったことをメモ程度に書いていきます。

参考にしたページはこちら
http://cse.naro.affrc.go.jp/takezawa/r-tips/r/39.html 
  http://qiita.com/kiimiiis/items/0e1646adf0dab0061845



pandasでは主なデータ構造が2つあって、
1つは一次元(Series)、
もう1つは二次元(dataframe)、
これに関してはまだはっきりとは分からないというか、掴めないです。


ただ、
単純に文字と数字を表のように並べて書いたものがSeries、

その表のようなものを構成する文字や数字に、
行ごとに列ごとに意味を付けて表にしたものがdataframe、という感覚です。


dataframeは文字や数字をただ並べただけのものではないので
列ごとに整理することもできますし、
平均値や標準偏差を出したり、グラフを作ったりすることもできます。
クロス集計もできるみたいです。






dataframe、グラフ作成

さて本題に入ります。

まず、書式IDをそのダウンロード数と、
企業の従業員数別に表した表をデータフレームで作成して
「pivot_df_sorted」という名前を付けました。

pivot_df=EMPLOYEE_SCALE_NAME_DF.pivot(index="product_id",columns="employee_scale",values="count").fillna(0)
pivot_df_reindex = pivot_df.reindex(
columns=[u"1~9名",
         u"10~49名",
         u"50~99名",
         u"100~299名",
         u"300~499名",
         u"500~999名",
         u"1000~2999名",
         u"3000~4999名",
         u"5000~9999名",
         u"10000名以上",
         u"(未回答)"])
pivot_df_sorted = pivot_df_reindex.sort_values(by=u"1~9名", ascending=False).fillna(0)
pivot_df_sorted


前回、EMPLOYEE_SCALE_NAME_DFというデータフレームを作成しましたが、
ここでpivotを用いて、
列にproduct_id、行にemployee_scale
を指定して改めてデータフレームを作りました。



ただ、これだけではemployee_scaleの並びがバラバラなので、
reindexという機能でcolumnsの名前を指定し、順番に並べていきます。




前回作成したもの
f:id:yuka_nakayama:20170403105800p:plain

今回改めて作成したもの
f:id:yuka_nakayama:20170403105829p:plain




また、行ごと、列ごとに和を出していきます。

pivot_df_sorted['p_sum']=pivot_df_sorted.sum(axis=1)
pivot_df_sorted.sort_values(by="p_sum")

pivot_df_sorted.loc["es_sum"] = pivot_df_sorted.sum()
pivot_df_sorted

上2行が行ごとの和(p_sum)、
下2行が列ごとの和(es_sum)を出すコードです。



1行目に(axis=1)とありますが、
これは行に対する演算をする、という意味です。








これをもとにグラフを作っていきます。

グラフにするコードは比較的シンプルで、

pivot_df_sorted.loc["es_sum"].drop("p_sum").plot.pie(figsize=(8,8))

だけです。




pivot_df_sortedのes_sum(列ごとの和)を指定して、
p_sum(行ごとの和)はdropで省く。

plotでグラフ化、
pieはグラフの種類の指定です。

f:id:yuka_nakayama:20170403110603p:plain


ここでは円グラフを指定しましたが、

たとえば棒グラフならbar、
散布図ならscatterなどを書きます。


f:id:yuka_nakayama:20170403110114p:plain