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

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

vol.10 ロジスティック回帰の練習

今回はロジスティック回帰の練習をしてみたので、
そこで分かったことや気づいたことを書いていきたいと思います。


使ったデータ
・入会日からの日数(4年未満のユーザーに限定)
・役職(position)
・職種(job)
・性別(gender)
・bizoceanの書式ダウンロード数(2017年1~3月)


分析の流れ
①役職、職種、性別は連続しないので真偽値に変換
②ダウンロード数データから、ダウンロードした(True)かしてないか(False)に変換
③1月2月のダウンロード状況と3月のダウンロード状況の関係を分析



①役職、職種、性別を真偽値に変換

まずユーザー情報、
f:id:yuka_nakayama:20170911153509p:plain

これら3つは連続値ではないので、「get_dummies」で1か0に変換します。

変換の仕方は

pd.get_dummies(df, columns = 真偽値に変換したいカラム)

で、実際には

bool_columns = ["position", "job", "gender"]

member_df_dum = pd.get_dummies(member_df_drop, columns=bool_columns)

member_df_dum.head()

3つとも同じ処理をするので
あらかじめ「bool_columns」という名前で括っておくと便利。


f:id:yuka_nakayama:20170906161441p:plain
変換できた~

②ダウンロードしたかしてないかを1,0で表す

ここではbool関数を使うことによって、
ダウンロード数0をFalse、それ以外をTrueと表すことができます。

f:id:yuka_nakayama:20170906161515p:plain

ichigatuDL_drop["DL_count1_bool"] = ichigatuDL_drop["DL_count1"].map(lambda x:bool(x))


f:id:yuka_nakayama:20170906161600p:plain


2月と3月も同様にやって、ユーザー情報のデータフレームとコンカット。

member_dl = pd.concat([member_df_dum,ichigatuDL_drop,nigatuDL_drop,sangatuDL_drop],axis=1)

このとき、「inplace=True」にすると元のデータフレームに上書きされてしまって
DL_count列がなくなるので、
DL_count_boolもなくなってエラーになります。


あと、たくさんNaNが出てきたので「dropna」で消すのですが、
ユーザー情報のNaNを0で埋めて、
ダウンロード数のNaNをFalseで埋めてから
それでも残ってるNaNを消します。

NaNが残っていると回帰分析をするときに「数字じゃないと分析できないよ」というエラーがでてくるからです。

#ユーザー情報のNaNを0で埋める
member_dl.DL_count1.fillna(0, inplace=True)
member_dl.DL_count2.fillna(0, inplace=True)
member_dl.DL_count3.fillna(0, inplace=True)

#ダウンロードのNaN(つまりダウンロードしてない)をFalseで埋める
member_dl.DL_count1_bool.fillna(False, inplace=True)
member_dl.DL_count2_bool.fillna(False, inplace=True)
member_dl.DL_count3_bool.fillna(False, inplace=True)

#それでも残っているNaNをdropする
member_dl = member_dl.dropna()
③1月2月のダウンロード状況と3月のダウンロード状況の関係を分析

3月のダウンロード状況をdropする

X_df = member_dl.drop(["DL_count3_bool"],axis=1)

Xを標準化する、標準化することで平均値の違うデータを比較できるようになる
(Xはユーザー情報と1,2月のダウンロード状況)

sc= StandardScaler()
X_std = sc.fit_transform(X_df)

yは3月のダウンロード状況

y_df = member_dl["DL_count3_bool"]

scikit learnライブラリの「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_std(Xを標準化したもの)とyを、それぞれ8:2に分割して、
X_stdの8をX_train、X_stdの2をX_test、
yの8をy_train、yの2をy_testと定義する。

random_state=Noneにすると毎回違う値で回帰分析ができて、
random_state=0だと毎回同じ値で分析することになります。


ロジスティック回帰

from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
clf = LogisticRegression(n_jobs=-1, verbose=1)

clf.fit_transform(X_train, y_train)

f:id:yuka_nakayama:20170906161647p:plain

f:id:yuka_nakayama:20170911153801p:plain

前回までの降水量と書式ダウンロード数の線形回帰よりは
比較的それっぽい分析の練習になったかなと思います。
本当は、上の緑色のグラフがきれいな曲線になればよかったのですが、なんか変な感じになってしまったので、データ量を増やしたり計算を少し変えたりすれば変わるかなーと思いました。




.

vol.9 線形回帰の練習・続き

前回の練習は


データ…2017年1月1日~3月31日の
    東京の降雨量と、東京にいるユーザーのダウンロード数


分析の流れ…東京にいるユーザーのIDのデータテーブルと
      そのユーザーによる3か月分のダウンロード数のデータテーブルをJOIN
       ↓
      降雨量データの読み込み
       ↓
      降雨量データとダウンロード数データをコンカット         
       ↓
      線形回帰

という感じでした。

降水量によって書式ダウンロード数は変わるのか、回帰分析してみたのですが、
直線の傾きが横軸とほぼ平行だったのと、
データがかなりバラついていたので、改めてですが降水量による影響は無しと分かりました。



今回はその延長で、


データ…2016年9月1日~2017年3月31日の
    関東圏(1都6県)の降雨量と、そこにいるユーザーのダウンロード数


分析の流れ…東京にいるユーザーのIDのデータテーブルと
      そのユーザーによる7か月分のダウンロード数データテーブルをJOIN
       ↓
      東京の降雨量データの読み込み
       ↓
      降雨量データとダウンロード数データをコンカット
       ↓
      (その他6県も同様に)
       ↓
      7つのデータフレームを縦にコンカット
       ↓
      線形回帰

という感じです。


これをやって新しく覚えたことを
メモみたいに書いていきます。

〇データフレームを縦にコンカット

f:id:yuka_nakayama:20170906122339p:plain

はじめ、


赤丸のところ(ダウンロード数のcolumns名)を
「東京」や「千葉」などと個別に設定していたのですが、
それだと横にコンカットされてNaNだらけになります。

縦にコンカットするにはcolumns名が同じである必要があるらしく、
「count」に統一しました。
(あとで詳しく書きますが、「count」じゃない名前にすればよかったです)


どのみち分析するときには県名とか関係なくなるので、
最初から同じcolumns名にしたほうがすっきりするような気もします。

f:id:yuka_nakayama:20170906122404p:plain


1都6県×7か月分で全部で1483行あって、
真ん中の列がcount(ダウンロード数)、右の列がrain(降雨量)です。


*オレンジ色下線部

DL_rain.reset_index(drop=True, inplace=True)

分析のときは日付データも要らないので消しました。

indexは、消すというよりも、
「indexを降り直して、日付を普通の列に戻して、
その日付の列をdropする」感じです。

()内、drop=Falseだと普通の列に戻ったやつは消えません。


*青色下線部

pd.set_option("display.max_rows", x)


行数が多いと省略で表示されてしまうのですが、
これで最大x行まで表示できるようになります。


途中、データにNaNが混ざっている疑惑があって、
これくらいなら目視で確認できるだろうということで
最大1500行表示できるようにしました。


〇(目視でなく、)NaNがないか確認する

DL_rain.isnull().values.any()

実行したらFalseと表示されたので
NaNは無かったということです…

〇ダウンロード数のcolumns名を「count」以外にしたほうがよかった


説明変数Xを降雨量、従属変数yをダウンロード数にして、
線形回帰にあてはめようとしたら、
データの型がだめみたいなそんな感じのエラーが出ました。


降雨量データもダウンロード数データも数字なはずなのに???となったんですが、

y = DL_rain.count

で実行しようとしていて、
それだと「count」がデータフレームのcolumns名としてではなく
pandasのcount関数として認識されているとのことでした。

[“”]でとじるとcolumns名の「count」を指定できて、
データ型を確認するとちゃんとintegerになってます。




これで線形回帰ができるようになりました。

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

DL_rain_fit = clf.fit(X, y)

import matplotlib.pyplot as plt

plt.scatter(X, y)
plt.plot(X, clf.predict(X))


f:id:yuka_nakayama:20170906122645p:plain

回帰係数…clf.coef_ で 0.96、
切片…clf.intercept_  で 790.89  
直線の式は y=0.96x+790.89 です。


前回も書きましたが、
降雨量とダウンロード数には因果関係が無いので、
分析方法としては、ほんとうは線形回帰でなく相関のほうが適しています。


もし、降水量と書式ダウンロード数に因果関係がある場合、
相関係数と回帰係数は1に近づきます。
が、「雨が降ったら書式をダウンロードしたくなる」ということを論理的に説明しなければいけなくなります。
実際、雨が降っても降らなくても、書式が必要ならダウンロードするし、不要であればダウンロードしないので、これらには因果関係が無いということができます。

参考)リクルートマネジメントソリューションズ 連載・コラム「『回帰分析』で因果関係を分析する」(2017年9月11日閲覧)
https://www.recruit-ms.co.jp/issue/column/0000000564/



.

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





.