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

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

Vol.8 回帰分析の練習、線形回帰

今回は今やっている回帰分析の練習について書いていこうと思います。


今回使ったデータは
東京の降水量(2017年1月1日~3月31日) と、
東京のユーザーの書式ダウンロード数 です。

本当はロジスティック回帰の練習をしようと思っていたのですが、
そもそもロジスティック回帰は確率を予想する分析方法なので、
上のデータを使うのであれば降水量からダウンロード数を予想する線形回帰のほうがいいのではないかと思って、線形回帰の練習にしました。


降水量とダウンロード数はたぶん全く関係がないので
線形回帰でなく相関をとったほうがいい気もしますが、
とりあえず練習ということでやってみました。


①データの読み込み

東京の降水量のデータは
気象庁HPからCSV形式でダウンロードし、
「20170530work.rain」という名前で保存していました。

rain_df = 
pd.read_csv(‘20170530work.rain.csv', names=('date','rain'), encoding="SHIFT-JIS" )

f:id:yuka_nakayama:20170906121246p:plain
はじめにCSVファイルを読み込んだとき、日本語が文字化けしてしまったので
「encoding=”SHIFT-JIS”」で直しました。



ダウンロード数のデータはBigQueryにあるので
SQLでテーブルを作ってそのままデータフレームに変換しました。

下の表は勤務地が東京にあるユーザーによるダウンロード数合計を表します。
f:id:yuka_nakayama:20170906121334p:plain


②データの整理

⑴まず日付の表記を統一する

降水量のデータは2017/01/01なのに対し
ダウンロード数のデータは2017-01-01になっています。

のちに日付列で表をコンカットしたいので、表記を揃えます。

tokyo_dl_df.date = tokyo_dl_df.date.apply(lambda date: date.replace ("-", "/"))

lambdaを使うと無名関数を作ることができて、
(lambda X : y)で、Xが引数、yが戻り値になります。

「もとの日付データ」をX、「-を/に表記を変えた日付データ」をy と定義した無名関数にdate関数を適用して、表記を変えた日付データyを返します。

⑵日付以外の表記を統一する

ダウンロード数も降水量も
見たかんじだと数字で統一されているようですが、
ダウンロード数は数値(int64)、
降水量は文字列(str)で表されていました。

文字列だと分析ができないので数値に直します。

整数だけに変換するときはint、
小数も含めて変換したいときはfloatを使うようですが、
うまくできなかったのでpandasのto_numericを使いました。

⑶表をくっつける

恐らく、くっつけなくても分析はできるとは思うのですが、
こうしたほうが確しやすいかなと思ったのでconcatで表をくっつけます。

f:id:yuka_nakayama:20170906121437p:plain

ダウンロード数の表「tokyo_dl_df2」にも降水量の表「rain_df3」にも日付データがあって、
さっき表記を統一していたので、このようにコンカットすることができました。

③線形回帰(liner_Regression)

【自分メモ】

2種類のデータで線形回帰を行うとき、
データをXとyであらわす。y=aX+bの形にするので。
Xは独立変数、yは説明変数。
yをXで説明する。Xがyに(一方的に)影響している。

【自分メモおわり】


今回の分析では
ダウンロード数を降水量で説明したいので、次のように定義します。

f:id:yuka_nakayama:20170906121525p:plain
※reshapeはデータの標準化
標準化は、平均値の違う2変数を比較するときに使います。

あとから調べてみて気づいたのですが、
おそらく今回のような単回帰分析では標準化の必要はなく、
重回帰分析をするときにどの変数が結果にどれくらい影響しているか知るために使うようです。



やっとのことで線形回帰してみるとこんな感じになります。


・linear_modelというライブラリのLinearRegression関数を使って「clf」と定義

clf = linear_model.LinearRegression(fit_intercept=True, normalize=False, copy_X=True, n_jobs=1)

fit_intercept=Trueで切片が求められ、
normalize=Falseで説明変数を正規化しない、
copy_X=Trueでデータを複製してからつかう、
n_jobs=1で使うジョブの数を1にする。

説明変数の正規化も、おそらく単回帰ではなく重回帰に使うもので、
たとえば、単位がkmの変数とmmの変数があるとき、mmのほうは見た目の数字が大きく、結果への影響も大きく見えてしまうため、正規化する必要があるそうです。


・clfと定義したものにX(降水量)とy(書式ダウンロード数)を代入

clf.fit(X, y)


・回帰係数Rを求める

clf.coef_
  • 0.547 でした


・切片を求める

clf.intercept_

734.595 でした


以上より、東京のユーザーによる書式ダウンロード数を東京の降水量で説明する直線の式はy=-0.547X+734.595 となります。



f:id:yuka_nakayama:20170906121615p:plain

注)グラフ横軸は東京の降水量、
横軸は東京のユーザーによるダウンロード数



今回は降水量と書式ダウンロード数という全く相関関係がないデータを使っての回帰分析でした。
ただ、回帰分析の流れはだいたい分かったので、
データ量を増やしたり説明変数を増やしたり、応用していければなと思いました。



※ 降水量データ
出所)国土交通省気象庁ホームページ
http://www.data.jma.go.jp/gmd/risk/obsdl/index.php#!table

※ 無名関数の説明
参考)LIFE WITH PYTHON「Pythonno無名関数」(2017年9月11日閲覧)
http://www.lifewithpython.com/2013/01/python-anonymous-function-lambda.html

※ 線形回帰の説明
参考)Pythonでデータサイエンス「scikit-earnで線形回帰(単回帰分析・重回帰分析)」(2017年9月11日閲覧)http://pythondatascience.plavox.info/scikit-learn/%E7%B7%9A%E5%BD%A2%E5%9B%9E%E5%B8%B0





.

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