vol.11 重回帰分析の練習
少し前にやった内容です。
降水量、気圧、気温のデータを使って、
降水量と気圧(説明変数)が気温(目的変数)にどれくらい影響しているか、線形回帰で分析しました。
まずデータの読み込み
weather = pd.read_csv("20170914.csv", encoding="SHIFT-JIS")
カラム名が長いので、適当に分かりやすく短くする
temp = weather.tempareture rain = weather.rain atm = weather.atmosphere weather_rename = weather.rename(columns = {"tempareture":"temp","atmosphere":"atm"})
説明変数のデータフレームを「X_df」、目的変数のデータフレームを「y_df」
X_df = weather_rename.drop("temp",axis=1) drop_columns = ["rain","atm"] y_df = weather_rename.drop(drop_columns,axis=1)
降水量と気圧は平均値が違うので標準化する、
気温も標準化しておく
std = StandardScaler() X_std = std.fit_transform(X_df) y_std = std.fit_transform(y_df)
〈①まずPandasでやってみる〉
import pandas as pd
model_ols = pd.ols(y=weather_rename["temp"],x=weather_rename.drop(["temp"],axis=1),intercept=True)
「ols」は「Ordinary Least Squares (Regression)」の略で、
最小二乗法のことです。つまり線形回帰のことを言います。
どうやらxとyで同じデータフレームにする必要があるっぽいので、
上記のように「X_df」「y_df」と分けなくて良かったです。
もともと別のデータフレームなのであれば、あらかじめconcatしておくと便利そう。
で、結果がこれです。
このモデルの数式は
[temp] = (-0.0778)[rain] + (-0.0125)[atm] + 18.51565
Numbere of Obserbations標本数は31
Number of Degress of Freedom自由度は3
R-square決定係数(寄与率)は0.0308なので
説明変数は目的変数の約3.1%しか説明できていない
Adj R-squared自由度調整済みの決定係数
Rmse平均標本二乗誤差
参考)https://mathwords.net/rmsemae
〇F検定 モデル全体の妥当性の検討
P-valueF値の有意確率は0.7859
→説明変数の係数が0である確率が78.6%なので、この分析で出したモデルは78.6%の確率で妥当とは言えない、と考えることができる。
〈②statsmodelを使ってやってみる〉
import statsmodels.api as sm
とりあえず
x = X_std x = sm.add_constant(x) y = y_std
と定義する。
「add_constant」でy切片も計算することができます。
statsmodelのolsに当てはめてみる。
model = sm.OLS(y, x) results = model.fit() results.summary()
標準化した数値をols関数に入れることができました。
で、結果がこれです。
このモデルの数式は
[temp] = (-0.1290)×[rain] + (-0.0410)×[atm] - 1.305e-15
Df Residuals(residual degrees of freedom)残差の自由度は28
Df Model(degree of freedom)自由度は2
R-squared決定係数は0.017
→説明変数は目的変数の1.7%しか説明できていないことになる
Adj.R-squared自由度調整済み決定係数は-0.053
〇F検定 モデル全体の妥当性の検討
Prob(F-statistic)有意確率は0.786
→結果は78.6%の確率で妥当だとは言えない
恐らく計算は間違っていないと思うのですが、
あまりいい結果が得られなかったので、
データを増やしたり説明変数を増やしたりすればもっといい結果になったかなと思います…。
vol.10 ロジスティック回帰の練習
今回はロジスティック回帰の練習をしてみたので、
そこで分かったことや気づいたことを書いていきたいと思います。
使ったデータ
・入会日からの日数(4年未満のユーザーに限定)
・役職(position)
・職種(job)
・性別(gender)
・bizoceanの書式ダウンロード数(2017年1~3月)
分析の流れ
①役職、職種、性別は連続しないので真偽値に変換
②ダウンロード数データから、ダウンロードした(True)かしてないか(False)に変換
③1月2月のダウンロード状況と3月のダウンロード状況の関係を分析
①役職、職種、性別を真偽値に変換
まずユーザー情報、
これら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」という名前で括っておくと便利。
変換できた~
②ダウンロードしたかしてないかを1,0で表す
ここではbool関数を使うことによって、
ダウンロード数0をFalse、それ以外をTrueと表すことができます。
ichigatuDL_drop["DL_count1_bool"] = ichigatuDL_drop["DL_count1"].map(lambda x:bool(x))
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)
前回までの降水量と書式ダウンロード数の線形回帰よりは
比較的それっぽい分析の練習になったかなと思います。
本当は、上の緑色のグラフがきれいな曲線になればよかったのですが、なんか変な感じになってしまったので、データ量を増やしたり計算を少し変えたりすれば変わるかなーと思いました。
.
vol.9 線形回帰の練習・続き
前回の練習は
データ…2017年1月1日~3月31日の
東京の降雨量と、東京にいるユーザーのダウンロード数
分析の流れ…東京にいるユーザーのIDのデータテーブルと
そのユーザーによる3か月分のダウンロード数のデータテーブルをJOIN
↓
降雨量データの読み込み
↓
降雨量データとダウンロード数データをコンカット
↓
線形回帰
という感じでした。
降水量によって書式ダウンロード数は変わるのか、回帰分析してみたのですが、
直線の傾きが横軸とほぼ平行だったのと、
データがかなりバラついていたので、改めてですが降水量による影響は無しと分かりました。
今回はその延長で、
データ…2016年9月1日~2017年3月31日の
関東圏(1都6県)の降雨量と、そこにいるユーザーのダウンロード数
分析の流れ…東京にいるユーザーのIDのデータテーブルと
そのユーザーによる7か月分のダウンロード数データテーブルをJOIN
↓
東京の降雨量データの読み込み
↓
降雨量データとダウンロード数データをコンカット
↓
(その他6県も同様に)
↓
7つのデータフレームを縦にコンカット
↓
線形回帰
という感じです。
これをやって新しく覚えたことを
メモみたいに書いていきます。
〇データフレームを縦にコンカット
はじめ、
赤丸のところ(ダウンロード数のcolumns名)を
「東京」や「千葉」などと個別に設定していたのですが、
それだと横にコンカットされてNaNだらけになります。
縦にコンカットするにはcolumns名が同じである必要があるらしく、
「count」に統一しました。
(あとで詳しく書きますが、「count」じゃない名前にすればよかったです)
どのみち分析するときには県名とか関係なくなるので、
最初から同じcolumns名にしたほうがすっきりするような気もします。
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))
回帰係数…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/
.