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

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

vol.14 メルマガクリック率の予測、インターン終了

今回は、会員の属性と過去のメルマガクリック率をもとにモデルを作成し、
今後新しく増えるユーザーのクリック率はどうなるのか、予測の分析をしました。



ここでは、説明変数である会員属性が数値ではないので、
数量化1類という考え方で分析していきます。

数値に変換したあと、考え方は線形回帰と同じです。

数値への変換はロジスティック回帰のときと同じなので省略します。
f:id:yuka_nakayama:20171226151558p:plain

from sklearn import linear_model
clf = linear_model.LinearRegression()
#mail_loansの確率分析
loans_drop = ["mail_business","mail_health","mail_resource","mail_english","mail_software","mail_invest"]
cross_loans = cross_dum.drop(loans_drop,axis=1)

#X_loansを会員属性、Y_loansをクリック率とする
X_loans=cross_loans.drop("mail_loans",axis=1)
Y_loans=cross_loans["mail_loans"]
clf_loans=clf.fit(X_loans,Y_loans)
loans_predict=clf_loans.predict(X_loans)

分析によって出されたモデルに会員属性を当てはめて、
(理論上の)クリック率を計算すると、以下のようになります。

loans_predict

f:id:yuka_nakayama:20171226151629p:plain

ほかのメルマガも同じようにクリック率を予測して、
データフレームをつなげるとこんな感じになります。
f:id:yuka_nakayama:20171226151648p:plain

実際のクリック率との差が大きいユーザーもいて、
分析に用いる属性の項目やユーザー数を増やせば
もっと精度の高い分析ができるのかなと思いました。




あと、私事ですが、
本日をもって、ビズオーシャンでのインターンシップを終了することになりました。
今年2月にインターンを始め、もう1年近く経っていて自分でも驚いています。

大学の授業やゼミの関係であまり来られない日もありましたが、
得られたものはかなり大きかったです。

前まではプログラミングそのものに触れたことがなかったため、本当に0からのスタートでしたが、
様々なコードを書いていくことでプログラミングの感覚というか、そういったものを少し掴めたような気がします。

また、データ分析のプロセスのなかで、
分析前のデータの処理と分析後の解釈が大事なんだと気付くこともできました。
精度の高い分析を速くするためには前処理(要するにプログラミングスキル)が必要で、
分析結果を正確に読み取るには統計学の知識や考え方が必要なんだなぁと思いました。

大学卒業後にどんな仕事をするのかはまだ分かりませんが、
この経験を生かしていきたいと思います。

vol.13 データの加工

今回は分析ではなく、データの加工についてです。

ビズオーシャンの会員属性別に、
どういう書式がダウンロードされているのか、ランキング表みたいなのを作りたかったので
その過程を書いていきます。


業種別、職種別、役職別、男女別、企業規模(従業員数)別、都道府県別、年収別、年代別に表を作ったのですが、

業種、職種、役職、男女、企業規模、都道府県、年収はほとんど同じ作り方だったので、
ここには役職別のものを書こうと思います。
年代別は少し違ったので、そこを詳しく書いていこうと思います。

①役職別

⑴会員データ

・会員データを読み込んで、ユーザーIDをindexに指定。
・普通のカラムに残ったユーザーIDと役職以外の属性項目をdropで消す。
ここではとくに新しいことをやっていないので、
どういうコードか、とかは省きます。

会員の役職のデータはこんな感じです。
f:id:yuka_nakayama:20171226140501p:plain

⑵書式データ

書式IDと書式カテゴリ名が同じデータテーブルになかったので、
・書式IDと書式カテゴリIDのデータテーブル
・書式カテゴリIDと書式カテゴリ名のデータテーブル
を作って、concatでつなげました。

こんな感じになります。
f:id:yuka_nakayama:20171226140503p:plain

⑶ユーザーIDと書式カテゴリ名

上の2つをつなげて、
ユーザーのIDと、ダウンロードした書式のカテゴリ名を見る。

f:id:yuka_nakayama:20171226140640p:plain


で、uidをindexに指定して、役職データとつなげる。
ついでに、NaNを含む行を消す。
f:id:yuka_nakayama:20171226140957p:plain

⑷重複する列をグルーピング

最終的に見たいランキング表にユーザーIDは不要なので、
役職と書式のカテゴリ名の組み合わせを見て
重複しているものはまとめていきます。

position_dlcate_g = position_dlcate_drop.groupby(["position","category_name"]).uid.unique()
position_dlcate_df = pd.DataFrame(position_dlcate_g)

#position_dlcate_gは⑶で作ったデータフレーム

f:id:yuka_nakayama:20171226141054p:plain
※ぼかしてるのはユーザーIDです。
係長・主任クラスの人で寒中見舞いカテゴリの書式をダウンロードしたユーザーのIDはこれこれだよ、と表示されます。

で、そのユーザーをカウントする。

position_dlcate_df["uid"] = position_dlcate_df["uid"].map(lambda x:len(x))

f:id:yuka_nakayama:20171226141121p:plain

⑸役職ごとにデータフレームを切りとる

#たとえば、経営者・役員クラスのユーザーだけ切りとる
position_dlcount_1 = position_dlcate_df.query(u"position=='経営者・役員クラス'")

ダウンロードしている人が多い順にならべて、上から5件だけ表示すると、
以下のようになる。
f:id:yuka_nakayama:20171226141143p:plain

※ここでの「uid」はユーザーIDではなく、
同じ役職で同じカテゴリの書式をダウンロードしたユーザーの数のことです。


②年代別

業種別や都道府県別は
役職別と同じようにできますが、

年代別は⑴のところで年齢を10代、20代、30代、、、と
グループに分けていく作業が必要でした。


f:id:yuka_nakayama:20171226143059p:plain

age = member_drop2["age"]
bins = [10,20,30,40,50,60,70,80,90,100]
group_names = ["10_","20_","30_","40_","50_","60_","70_","80_","90_"]

age_cut = pd.cut(age,bins,labels=group_names)

データフレームに直すとこんな感じになります。
f:id:yuka_nakayama:20171226143255p:plain


これ以降はほかの属性項目と同じで、
以下のようにできました。
f:id:yuka_nakayama:20171226143541p:plain

vol.12 重回帰分析の練習、続き

今回も、少し前にやった内容です。

ビズオーシャンのユーザーの入会日、クリック状況、ダウンロード状況のでデータを使って、
入会日からの日数とクリック数(説明変数)が
ダウンロード数(目的変数)にどれくらい影響しているか、分析してみました。


uid:ユーザーID
duration:入会日からの日数
click_sum:クリック数
dl_sum:ダウンロード数


データはいずれも2017年の1月1日~8月31日までのもので、
その期間にクリックがあったユーザーに限定,
さらにランダムに200人に絞りました。

以前、別の分析の練習のときですが、
30万人分ぐらいのデータを使ったら、ひとつの処理に10分くらいかかってしまったので、
今回は200人にまで減らしました。




①データの読み込み

#入会日からの日数のデータ

member_duration = pd.read_csv("20170919work.gz")
member_duration.head()

f:id:yuka_nakayama:20171226105609p:plain

#クリック数のデータ

member_click = pd.read_csv("20171003click.gz")
member_click.head()

f:id:yuka_nakayama:20171226105623p:plain

#ダウンロード数のデータ

member_dl = pd.read_csv("20171003dl.gz")
member_dl.head()

f:id:yuka_nakayama:20171226105633p:plain


②3つともuidをindexに指定する、200人抽出、join

#duration
member_duration.index = member_duration["uid"]
member_duration_drop = member_duration.drop("uid", axis=1)

#click
member_click.index = member_click["uid"]
member_click_drop = member_click.drop("uid", axis=1)

#download
member_dl.index = member_dl["uid"]
member_dl_drop = member_dl.drop("uid",axis=1)


#join
duration_click = member_duration_drop.join(member_click_drop)

#click_sumにNaNが含まれるユーザーを排除
duration_click_drop = duration_click.dropna()


duration_click_drop

f:id:yuka_nakayama:20171226105750p:plain

#ランダム抽出

sample = np.random.permutation(len(duration_click_drop))
member_random = duration_click_drop.take(sample[:200])
#ランダム抽出したものにダウンロード数データをjoin

member_join = member_random.join(member_dl_drop)
member_join

f:id:yuka_nakayama:20171226105816p:plain

#dl_sumにNaNがあったので、それは0で埋める

member_fill = member_join.fillna(0)


③分析する

#Xを日数、クリック数とする

X_df = member_fill.drop("dl_sum",axis=1)

f:id:yuka_nakayama:20171226105924p:plain

#yをダウンロード数とする

y_df = member_fill.drop(["duration","click_sum"],axis=1)

f:id:yuka_nakayama:20171226105955p:plain

#Xとyを標準化する
std = StandardScaler()
>||
#Xの標準化

X_std = std.fit_transform(X_df)

f:id:yuka_nakayama:20171226110013p:plain

#yの標準化

y_std = std.fit_transform(y_df)

f:id:yuka_nakayama:20171226110027p:plain

import statsmodels.api as sm
import matplotlib.pyplot as plt

x = X_std
x = sm.add_constant(x)
y = y_std
#add_constantにより定数項を求められる

model = sm.OLS(y, x)
results = model.fit()

results.summary()

f:id:yuka_nakayama:20171226110047p:plain




④結果の解釈

入会日からの日数とクリック数がダウンロード数にどれくらい影響するか、分析の結果の式は
[dl_sum] = (7.497e-05)[duration] + (0.8271)[click_sum] + (-8.327e-17)  で、

説明変数の係数は目的変数への影響の大きさを表していて、
0に近いほど影響は小さく、1に近いほど影響は大きいと言えます。



[duration]の係数は7.497e-05なので、
入会からの日数はDL数にほとんど影響しない、
[click_sum]の係数は0.8271なので、
クリック数はDL数に影響する、と解釈できます。


ただ、決定係数が0.681なので、この式の当てはまりは68%ぐらい。

なので、入会日からの日数とクリック数だけでDL数を説明しようとするならば、
全体の68%しか説明できない。ということです。

説明変数を増やせば、当てはまり具合は100%に近づきます。



〈メモ〉

[95%Conf.Int.]95% confidence interval
95%信頼区間のこと

今回、2017年1月~8月にクリックしたユーザーから、200人だけを無作為に抽出し分析に使いました。
何回か無作為抽出をするとき、標本平均はそのたびに変わっていきますが、
何度も行うことで、(大数の法則により)母平均に近づいていきます。


95%信頼区間とは、
何度も(例えば100回)無作為抽出したとき、そのうちの95%(つまり95回)は
この区間に標本平均が含まれる、ということで、

正確にはズレているらしいのですが、ごく簡単に解釈するとすれば、
「95%の確率でその区間に母平均が含まれる」というような感じです。


つまり、
上と同じような分析を何度も繰り返せば、そのうちの95%では、
定数項は-0.079と0.079の間に含まれる、
[duration]の係数は-0.081と0.081の間に含まれる、
[click_sum]の係数は0.746と0.908の間に含まれる、
ということを意味します。

参考)http://d.hatena.ne.jp/kiwamu_i/20121203/1354520561