乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

データサイエンス

先日、大きな選挙が有りましたね。
何故にすぐ当確が出るのでしょう?

その仕組みを解説してみました。
解説動画はこちら




なぜ開票率1%で当確が出るのか?
今回は選挙のお話です。

各局がどのような仕組みで
当確を出しているかは不明ですが

少なくとも投票所の出口調査や
事前アンケートなどの結果などを用いて
統計的なサンプリング調査を元に
最終的な判断をしていると思います。


当確が分かる仕組み
無作為な1%分の投票結果が分かれば
票全体の動向が推計できます。

これはお味噌汁の味見に似ていますね。

ちゃんと掻き回したものを
お玉に一掬い程度味見すれば
味噌汁全体の味はおおよその検討はつくでしょう。

ここで用いられるのが
母比率の信頼区間です。


区間推定「母比率」の信頼区間の求め方

母比率の信頼区間を求める式
スクリーンショット 2021-11-06 16.55.09

なんだかややこしいと思いますが
R:標本比率(得票率)
z:標準化変数(Z-score)
n:標本数(サンプル)
となっています。

また信頼係数というモノがあります。

これは母集団から標本を取って
その平均からXX%信頼区間を
求めるという作業を100回やったとき
XX回はその区間の中に母平均が含まれる
というものです。

通常は95%を用いることが多いですが

信頼係数におけるzの値は
90%で1.64
95%で1.96
99%で2.58
という値になります。


さて、ここで問題を考えてみましょう。

有効投票数10万票の選挙で開票率1%(1000票)時点の
A候補者の得票率は60%でした。

このとき信頼係数95%で
母比率の信頼区間を計算してみよう。

Pythonを使って計算してみました。
#標本比率:
R=0.6
# 標準化変数
z=1.96
# 標本数
n=1000

lower = (R - z*((R*(1-R))/n)**0.5)*100
upper = (R + z*((R*(1-R))/n)**0.5)*100
print('{0:.3f} - {1:.3f}'.format(lower,upper))
56.964 - 63.036

これによると母比率の信頼区間は
最低でも56.9%、最高で63%


他に候補者がどれだけいても
過半数以上を獲得できる見込みになります。

計算式上ではサンプリングした標本数の数で
信頼区間の幅は大きく変わります。

10万票ほどの選挙区ではおよそ1%
1000票もあれば誤差は3%程度で
最終結果を導くことができます。

標本数を増やせば、区間はグッと縮まります。
開票率が上がっていけば
どんどん精度は上がりますね。

候補者同士の結果に差のない場合は
開票率100%に近付かないと
結果が出ないこともありますが

元々大差がついている選挙区では
開票率が低くても当確が出るようですね。

さてここからは
選挙区のシミュレーションをしてみましょう。

神奈川11区でシミュレーション

元セクシー担当大臣のいる選挙区の結果を用いて
シミュレーションしてみましょう。

結果は
小泉 進次郎氏 14万7634票
林 伸明氏 3万8843票
となっていました。

データ化してみます。
# 小泉票
k = ['K']*147634
# 林票
h = ['H']*38843
# 全体の票
v = k+h

print(len(v))
print(v.count('K'))
print(v.count('H'))
186477
147634
38843

これで票のデータを用意できました。
ランダムサンプリングして結果を見てみましょう。
1%でランダムサンプリング
import random

n = len(v)//100
d = random.sample(v , n)

print(len(d))
print(d.count('K'))
print(d.count('H'))
1864
1469
395

1%時の票数は1864
小泉氏は1469票獲得
林氏は395票獲得で
小泉氏の圧勝・・・・・・

一回やっただけでは分からないですよね・・・

頑張って100回対決してみましょう。
import random
import pandas as pd

n = len(v)//100
df = pd.DataFrame(columns=['小泉票','林票'])
for i in range(100):
    d = random.sample(v , n)
    k_count = d.count('K')
    h_count = d.count('H')
    tmp = pd.DataFrame([[k_count,h_count]],columns=['小泉票','林票'])
    df = df.append(tmp)

df = df.astype('int')
df.describe()
小泉票林票
count100.000000100.000000
mean1473.930000390.070000
std18.06176618.061766
min1427.000000351.000000
25%1462.000000376.000000
50%1475.000000389.000000
75%1488.000000402.000000
max1513.000000437.000000

結果から言えば
林氏はどんなに頑張っても
437票しかとれず小泉氏が圧勝です。

箱髭図にして差し上げました。
スクリーンショット 2021-11-06 17.26.44

1%の時点でこれなんで
まあ、諦めろって事ですwwwwwww

政策や人柄、人気などで
相手を上回るしかないでしょうね
まあほぼ無理ゲーでしょうけど。


まとめ
1000票ほどを集めれば
おおよその結果が予想できてしまう

得票率1%の時点で
大きく差がついていたら諦めろ

ただしデータに偏りが無いことが大前提!

あくまでもこの推定はデータに偏りがない所で
サンプリングされたデータを用いての推定です。

恣意的に歪められた結果を用いれば
作為的に誘導することも出来てしまいます。

使う側も見る側も
注意が必要なポイントです。


今回は選挙にまつわる
統計のお話でした。

それでは。

100日後に亡くなってしまう確率を考えてみました。

解説動画はこちら




人が死ぬ確率をどう出すか

一番詳しいのは
保険会社だと聞いています。

死亡した方のデータを持っていて
そこから会社が損しないように
保険料を算出していますので
そのやり方でいきます。

平成30年簡易生命表(男)が発表されているので
それを用いて行きます。

まずはデータ化します。
import numpy as np

rate = [0.00196,0.00025,0.00019,0.00014,0.00011,0.00010,0.00008,0.00007,0.00007,0.00007,
0.00007,0.00008,0.00009,0.00011,0.00014,0.00016,0.00020,0.00024,0.00029,0.00035, 
0.00040,0.00044,0.00047,0.00049,0.00049,0.00049,0.00049,0.00050,0.00051,0.00053, 
0.00055,0.00057,0.00060,0.00063,0.00065,0.00068,0.00072,0.00076,0.00081,0.00087,
0.00094,0.00102,0.00112,0.00123,0.00136,0.00149,0.00164,0.00181,0.00200,0.00221, 
0.00245,0.00274,0.00305,0.00336,0.00368,0.00401,0.00437,0.00481,0.00532,0.00589, 
0.00651,0.00717,0.00788,0.00864,0.00948,0.01047,0.01159,0.01283,0.01419,0.01555, 
0.01695,0.01854,0.02036,0.02240,0.02465,0.02709,0.02964,0.03261,0.03621,0.04053, 
0.04565,0.05149,0.05812,0.06555,0.07417,0.08412,0.09532,0.10774,0.12093,0.13456, 
0.14830,0.16406,0.18130,0.20011,0.22060,0.24284,0.26691,0.29287,0.32075,0.35056, 
0.38229,0.41587,0.45119,0.48808,0.52633]

データは10万人あたりの死亡者数を元に
死亡率を弾き出しています。

40才くらいまで生き残れる確率は?
1-sum(rate[0:40])
0.98

日本に生まれたら
9割以上の確率で40才くらいまでは
生き残れるようです。


死亡率が一番低いのは何歳の時?
np.argmin(rate) , rate[np.argmin(rate)]
(7,7e-05)


7才の時が一番死亡率は低いようです。

逆に死亡率が一番高いのは?
 np.argmax(rate) , rate[np.argmax(rate)]
(104 , 0.52633)

当たり前ですが、高齢になれば生き残る確率も
下がりますね。


20才の時の死亡率は?
 '{:f}%'.format(rate[20] * 100)
0.040000%

20才であれば年間で1万人に4人ほどしか
亡くならない計算ですね。

40才だと死亡率はその2倍以上になっています。

明日なくなる確率は?
 '{:f}%'.format(rate[20] / 365 * 100)
0.000110%


20の人が明日亡くなる確率は
年間の確率を365で割れば良いと思います。

1日あたりだと100万人に一人くらいでしょう。


100日後(まで)に死ぬ確率は?
 '{:f}%'.format(rate[20] / 365 * 100 * 100)
0.010959%

単純に100倍すれば良いかなと

1万人に1人の確率で
100日後に亡くなります。

正直な話
ピンとはこないですよね。

ただし
人が亡くなる時は突然です。

いきなりやってきます。

心構えもなく唐突にです。

家族が亡くなるのは避けられない事です。

その時に思うことは
「何でもっと沢山話しておかなかったんだろう」です。

死ぬ事について考えることは
普通はほとんど無いかと思います。

でも時を重ねるにつれ
その確率はどんどん上がっていきます。

いつかは誰しも経験する事になるのです。

それが明日かもしれないし
100日後かもしれない。

わからないから今日1日を
大切にした方が良いんだと思いますね。

今日はここまでです
それでは

さて、今年も受験シーズンですねーー

数学の問題をプログラムで解くという
無駄なことをやっていきたいと思います。

解説動画はこちら





2020年の数学1の問題です。

さてまず最初は

スクリーンショット 2020-01-26 16.48.25

問題見るだけだと
なんだかよく分からないですよねー

まずはこの直線L君を作図してあげましょう。
そうすれば分かりやすくなるはずです。

Python言語では
数学的な作図が非常に楽です。

数式用、作図用のライブラリを
インポートします。
import numpy as np
import warnings
import matplotlib.pyplot as plt
warnings.simplefilter('ignore')
%matplotlib inline

作図では直線を描くので
x軸、y軸の値が必要になります。

y軸の値は数式を元に生成されるので
x軸の値を適当に生成します。

numpyのlinspaceで
数値を適当に生成することができます。
a=1
x = np.linspace(-5, 5, 100)
y = (a**2-2*a-8)*x + a
plt.plot(x, y,label=str(a))
plt.legend()
plt.show()
download



aが1の時を作図してみると
傾きは右肩下がりで負の値になっています。

今度はaが8の時を試してみます。
download-1

そうすると右肩上がりで
傾きが正の値になりました。

どこかに境界線があるはずです。

aの値を複数用いて
まとめて作図してみます。
for a in range(1,6):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    plt.plot(x, y,label=str(a))

plt.legend()
plt.show()
download-2
aの値を1から5までで作図すると
4の時に平行になったように見えます。
5になると正の値になっているように見えます。

と言うことで4が境界線に
なっていると言えそうです。

ただし、aの値は負の値を取ることも
考えられますので、aが負の時も
作図してみます。
for a in range(-6,0):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    plt.plot(x, y,label=str(a))

plt.legend()
plt.show()
download-3
aが負の場合
-2で平行になっているように見えますね。

合わせると回答すべきポイントは
アイ< a < ウなので

-2 < a < 4

と言う感じになりますね。

ただしこれだと
図からの判断なので
実際に傾きを求めてみましょう。

傾きは
スクリーンショット 2020-01-26 16.48.46
にて求めることができます。

x,yの共分散は
np.cov(x,y)[0,1]

xの分散は
np.var(x)

これで求めることができます。

これでaを入れて傾きを求めてみると
for a in range(-5,6):
    x = np.linspace(-5, 5, 100)
    y = (a**2-2*a-8)*x + a
    
    # 傾き = x,yの共分散 / xの分散
    coef = np.cov(x,y)[0,1] / np.var(x)
    print(a,'\t',coef)
-5 	 27.27272727272728
-4 	 16.161616161616156
-3 	 7.070707070707068
-2 	 0.0
-1 	 -5.050505050505051
0 	 -8.080808080808078
1 	 -9.090909090909092
2 	 -8.080808080808078
3 	 -5.050505050505051
4 	 0.0
5 	 7.070707070707068




はい、これで-2と4のところで
ちゃんと傾きは0で平行になっていますので
それを越えれば傾きが正になることが確認できました。


続いて

スクリーンショット 2020-01-26 16.48.55

先ほどの直線Lとx軸の交点と言っているので
x軸を作図してあげれば
交点bが分かりやすくなります。

プログラム上では
単純にy=0とし、xを適当な値で結んで
直線を描けばx軸になります。
a=1
x = np.linspace(-5, 5, 100)
y = (a**2-2*a-8)*x + a

# 直線Lを作図
plt.plot(x, y,label=str(a))
# x軸を作図
plt.plot([-5,5], [0,0],c='red')
plt.legend()
plt.show()
download-4

はい、赤線と青線の交わる部分が交点bですねー

さてaが変われば傾きが変わり
交わる部分も変わってくるのですが

求めたい交点bについては
yの値は0なのでxの値を求めてあげれば
良いと言うことになります。

と言うことで
直線の式を変形して
xの値を求めてみましょう。

式を変形すると
y = (a**2-2*a-8)*x + a

y-a = (a**2-2*a-8)*x

(y-a)/(a**2-2*a-8) = x

x = (y-a)/(a**2-2*a-8)
y=0なので、yの部分を0に変えてあげれば
xの値をすぐに求めることができますね。

aを変えてxを求めてみましょう。

まずは a > 0 の場合

for a in range(0,11):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print(a,'\t',x)
0 	 -0.0
1 	 0.1111111111111111
2 	 0.25
3 	 0.6
5 	 -0.7142857142857143
6 	 -0.375
7 	 -0.25925925925925924
8 	 -0.2
9 	 -0.16363636363636364
10 	 -0.1388888888888889

これでみると4のところが無くて
5から負の値になっていますね。

4の値をもう少し細かく見てみましょう。
for a in np.linspace(3.9, 4.1, 21):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print('{:.03}'.format(a),'\t',x)
3.9 	 6.610169491525415
3.91 	 7.3510058281631725
3.92 	 8.277027027027007
3.93 	 9.467598169115819
3.94 	 11.054994388327726
3.95 	 13.277310924369653
3.96 	 16.610738255033528
3.97 	 22.166387493020434
3.98 	 33.277591973244355
3.99 	 66.6110183639381
4.01 	 -66.72212978369552
4.02 	 -33.38870431893784
4.03 	 -22.277501381979285
4.04 	 -16.72185430463576
4.05 	 -13.388429752066106
4.06 	 -11.166116611661277
4.07 	 -9.578724405742625
4.08 	 -8.388157894736848
4.09 	 -7.462141944900592
4.1 	 -6.721311475409841

と言うことで4を越えると
xが0未満になり成立しません。

a > 0 の場合、b > 0となるのは エ < a < オ
なので

答えは

0 < a < 4

ですね。


a<=0の場合は
for a in range(-10,0):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print(a,'\t',x)
-10 	 0.08928571428571429
-9 	 0.0989010989010989
-8 	 0.1111111111111111
-7 	 0.12727272727272726
-6 	 0.15
-5 	 0.18518518518518517
-4 	 0.25
-3 	 0.42857142857142855
-1 	 -0.2

-2で負の値に切り替わりますね。
-2付近を細かく見ると
 for a in np.linspace(-3, -1, 21):
    if (a**2-2*a-8)!=0:
        x = (0-a)/(a**2-2*a-8)
        print('{:.03}'.format(a),'\t',x)
-3.0 	 0.42857142857142855
-2.9 	 0.466988727858293
-2.8 	 0.5147058823529413
-2.7 	 0.5756929637526651
-2.6 	 0.6565656565656565
-2.5 	 0.7692307692307693
-2.4 	 0.9375000000000004
-2.3 	 1.2169312169312176
-2.2 	 1.774193548387094
-2.1 	 3.4426229508196755
-1.9 	 -3.220338983050848
-1.8 	 -1.551724137931033
-1.7 	 -0.9941520467836252
-1.6 	 -0.7142857142857142
-1.5 	 -0.5454545454545454
-1.4 	 -0.4320987654320987
-1.3 	 -0.3504043126684635
-1.2 	 -0.28846153846153844
-1.1 	 -0.2396514161220043
-1.0 	 -0.2

はい、これで-2を界に
負の値になることが確認できたので

a <= 0 の場合、b > 0となるのは a < カキ

a < -2

となりました。

ふう
普通に問題を解くのに比べて
3倍ほどは時間がかかるんじゃないでしょうかね

このように
無駄なコードを書くことで
プログラミングを上達させることが
できるかもしれません。

お暇であれば試してみるのもいかがでしょうか?

今回はこれまでです。
それでは

さて

先日のM-1グランプリみなさん見ましたか?

自分は「かまいたち」を優勝予想したのですが
見事に外れました!!!!

見事王者に輝いたのは
初出場の精鋭

ミルクボーイ

このネタが優秀すぎたので
判別モデルを作ってみました。

解説動画はこちら




まずはデータです。

ミルクボーイのネタをみて文字起こし!!
データ化します。

かどうか説明文
コーンフレーク甘くてカリカリしてて牛乳とかかけて食べるやつ
違う死ぬ前の最後のご飯もそれでいい
コーンフレークまだ寿命に余裕があるから食べてられんねん
コーンフレーク最期のごはんに任命されたら荷が重いよ
コーンフレークなんであんなに、栄養バランスの五角形が広いんかわからん
コーンフレークパッケージに書いてる五角形めちゃくちゃでかいんやから
コーンフレーク自分の得意な項目だけで勝負してる
コーンフレーク牛乳の栄養素を含んだうえでの五角形
違う晩御飯ででてきても全然いい
コーンフレーク晩飯で出てきたらちゃぶ台ひっくり返す
コーンフレークまだ朝の寝ぼけてるときやから食べてられる
コーンフレーク食べてるうちにだんだん目が覚めてくるから、最後にちょっとだけ残してまう
コーンフレーク子どもの頃、なぜかみんな憧れたらしい
コーンフレークミロとフルーチェは憧れた
コーンフレークトランシーバーも憧れました
違うお坊さんが修行も時に食べてる
違う精進料理にカタカナのメニューなんかでえへん
コーンフレーク朝から楽して腹を満たしたいという煩悩の塊
コーンフレークあれみんな煩悩に牛乳かけてる
コーンフレークパフェとかの傘増しに使われているらしい
コーンフレークあれは法律スレスレぐらいはいっとる
違うジャンルでいうなら中華
違う中華だけではない
コーンフレーク回るテーブルの上においたら回したとき全部飛び散る
コーンフレーク食べてるときに誰に感謝したらええかわからん
コーンフレークコーンフレークは生産者さんの顔がうかばへん
コーンフレーク浮かんでくるのは腕組んでるトラの顔だけ
コーンフレーク赤いスカーフのトラの顔だけ
違うおかんがではないと言えばちゃう
こいつをそのままコピーしてcsvファイル化してもらえば
できると思います。


できたらモデル作りに入ります。

まずはライブラリのインポート
import codecs
import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix

import warnings
warnings.simplefilter('ignore')
from janome.tokenizer import Tokenizer
t = Tokenizer()

お次は
データの読み込みです。
df = pd.read_table('corn_model_data.csv')
df.head()
mb1



データが読めたら次は
辞書を作ります。

このモデルは形態素解析を用いて
文章を単語化し、その単語をモデル用に使います。

単語辞書を先に作ります。
word_dict = {}
for i,v in df.iterrows():
    for token in t.tokenize(v[1]):
        h = token.part_of_speech.split(',')[0]
        if any([h=='名詞',h=='動詞',h=='形容詞']):
            key = token.surface
            word_dict[key]=0

さて用意ができたら
学習データの作成に入ります。
df['y'] = df['かどうか'].replace({'コーンフレーク':1,'違う':0})

目的変数は0,1でないといけないので
コーンフレークを1,違えば0にして設定します。

次に元のデータの文章を形態素解析して
単語が有れば1,なければ0と言うデータフレームを作り
元のデータフレームに結合します。

columns = [k for k,v in sorted(word_dict.items())]
tf_df = pd.DataFrame(columns=columns)
for i,v in df.iterrows():
    tmp = word_dict.copy()
    for token in t.tokenize(v[1]):
        h = token.part_of_speech.split(',')[0]
        if any([h=='名詞',h=='動詞',h=='形容詞']):
            key = token.surface
            if key in tmp:
                tmp[key]=1
    tmp_list = [v for k,v in sorted(tmp.items())]
    tmp_df = pd.DataFrame(data=np.array(tmp_list).reshape(1,len(tmp_list)),columns=columns)
    tf_df = tf_df.append(tmp_df)

tf_df = tf_df.reset_index(drop=True)
data_df = pd.concat([df,tf_df],axis=1)

これでデータは揃いました。

学習用には
説明変数と目的変数が必要なので
いらないものを除外して設定します。
x = data_df.iloc[0:,3:]
y = data_df['y']

次は学習ですね

機械学習史上最も雑な判別モデルの作成です。

とにかく全部データ使ってモデルを作ります。
model = LogisticRegression(fit_intercept = False)
model.fit(x,y)

predict = model.predict(x)
print('Accuracy : {0}%'.format(accuracy_score(y, predict)*100))
print(confusion_matrix(y, predict, labels=[0,1]))
Accuracy : 100.0%
[[ 7 0]
[ 0 22]]

判別モデルの検証も適当にしていますが
正解率100%と言うギャグのようなモデルが出来上がります。

最後に
このモデルを使えるようにしましょう!!!

ここでは
widgetを使ってjupyter上で使えるようにしています。
from __future__ import print_function
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets

def callback(word):

    if len(word)>0:
        data = word_dict.copy()
        for token in t.tokenize(word):
            h = token.part_of_speech.split(',')[0]
            if any([h=='名詞',h=='動詞',h=='形容詞']):
                key = token.surface
                if key in tmp:
                    data[key]=1

        input_list = [v for k,v in sorted(data.items())]
        input_df = pd.DataFrame(data=np.array(input_list).reshape(1,len(input_list)),columns=columns)
        input_predict = model.predict(input_df)

        if input_predict==1:
            print('それはコーンフレークや!!!')
        else:
            print('コーンフレークちゃうやないかい!!')
    else:
        print()

interact(callback,word='')

実行してみると
mb2

入力値に対して判別結果が出力されます。


mb3

mb4


はい
このような判別モデルですが
データさえあれば
なんとかなるもんですね

まあ、学習用のデータに用いたものしかコーンフレーク認定されない
雑な学習モデルですが
初学者の学習用にはなかなか面白いかもしれません。

ミルクボーイのネタはまだまだ沢山あるので
以下のようなものもイケますね!!
・モナカかモナカ違うか
・サイゼかサイゼちゃうか
・俳優か男優か etc


M-1が始まる前までは
個人的には「かまいたち」を 優勝予想していたものの

ミルクボーイのネタの完成度には 正直驚きましたねーーー

その他の細かい予想は当たったので
データからお笑いに関する予測するということも
面白い研究分野の一つになっています。

最後に データからM-1で優勝する方法について
需要が有ればやりたいですねー

芸人の方々、ご連絡くださいませ!!

それでは

さて、今回は機械学習のお話です。

一応なんちゃって
MLエンジニアなのでねwwwwwww

食べログさんのお店をデータ化したら
星の予測モデルが作れました。

解説動画はこちら





以前の動画では
食べログ分布というものについて触れました。
前回の記事はこちら食べログ分布は有るのか

今回は
実際に星を予測するモデルを作っていきましょう。

さて
こういった機械学習を用いた
予測モデルを作成するには
まずはデータが必要です。


こちらは
みなさん頑張って集めてみて下さい!!!!!

データとしては
こんなデータです。

スクリーンショット 2019-10-26 21.13.18

コメント数、ブックマーク数や価格
コメントを残した人の星の平均などです。

そして予測をするのは
「星」のデータとなります。


予測モデルの作成に使用したコードは
次のようなコードになります。
import warnings
warnings.filterwarnings('ignore')

import pandas as pd
import numpy as np

from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
import xgboost as xgb
import lightgbm as lgm

from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

import matplotlib.pyplot as plt
%matplotlib inline


data_df = pd.read_csv('tabelog_star_data.csv')

data_df = data_df[data_df['星']>2.9]

X = data_df.drop(['星'],axis=1)
Y = data_df['星']
x_train, x_test, y_train, y_test = train_test_split(X,Y, test_size=0.3)

models = []
models.append(("DesicionTree",DecisionTreeRegressor(max_depth=8)))
models.append(("RandomForest",RandomForestRegressor(max_depth=8)))
models.append(("LightGBM",lgm.LGBMRegressor(max_depth=8)))
models.append(("XGBoost",xgb.XGBRegressor(max_depth=8,objective='reg:squarederror')))

result = {}
for name,model in models:
    print(name)
    model.fit(x_train, y_train)
    y_predict = model.predict(x_test)
    mae  = mean_absolute_error(y_test, y_predict)
    rmse = np.sqrt(mean_squared_error(y_test, y_predict))
    result[name] = [mae,rmse]
    print('  MAE : ',mae)
    print('RMSE : ',rmse)
    print()

少しづつみていきましょう。

まずは
データを読み込みします。

pd.read_csv()でCSVファイルを読み込みして
データフレームに変換できます。

なおこのデータは
すでに「前処理」と呼ばれる
機械学習用のデータに
整形する工程を終えたデータで
全て数値型のデータになっています。


次に訓練用とテスト用のデータに分けます。

train_test_splitを用いると
簡単に振り分けることができるので楽です。

今回は3割テスト、残りを訓練用としました。

モデルは
今回は自分がよく使っている
決定木,ランダムフォレスト,LightGBM,XGBoost

こちらの4種類を使っていきます。

モデルの作成は
model.fit(x_train, y_train)

これだけで済んでしまいます。
機械学習と言っても
コードはたったこれだけなんですよね・・・

モデルの作成が終わったら
次はテストデータで予測を行い
その精度を検証します。

y_predict = model.predict(x_test)

予測もたったこれ1行だけで済んでしまいます。

あとはこの結果から
どれだけ誤差が出たのかを見て
精度を図ります。

誤差を見るのに
MAE
RMSE
を使います。

今回はざっくり検証なので
どれだけ誤差が出たのかだけを見ることとします。

DesicionTree
  MAE :  0.1047453451900971
RMSE :  0.14276028664764914

RandomForest
  MAE :  0.08806766700129451
RMSE :  0.11329995353232411

LightGBM
  MAE :  0.08365354791749746
RMSE :  0.10874470218772024

XGBoost
  MAE :  0.08733976372263648
RMSE :  0.11181978441280738

MAEはどんだけ誤差が出たかを平均したものです。
MAE0.083なら
+-0.083くらいはズレます。

実際の星が3.5だとして
大体予測が3.4-3.6くらいになるだろうという
モデルができました。

今回一番良かったものは
LightGBMですね。

誤差を可視化してみましょう。
tabe1

平均で0.083ズレですが
大きいところでは0.36くらいはズレてますね

誤差の累積だと
tabe2

80%くらいで0.15以内には収まりそうな感じですね。

予測の精度としては
まあまあなんじゃないでしょうか

これが仕事なら
クロスバリデーションしたり
パラメータいじったり
元々の特徴量作る集計の見直しなど
まだまだやること有ります。

色々すればもう少し
精度は上がるでしょうが
ざっくり検証なのでこれまでです。


さて
このデータのうちで
どれが一番予測に寄与しているでしょうか?

寄与度を出してみました。


191コメント数

181ブックマーク数

1711rate_1000_s_mean

1521rate_100_s_mean

1491rate_5000_s_mean

1341rate_500_s_mean

106夜価格低

1051year_500_s_mean

811rate_2000_s_mean

721year_1000_s_mean

683month_500_s_mean

656month_500_s_mean

631rate_10000_s_mean

55昼価格低

こんな感じでした。

コメント数やブックマークの数
コメントした人の星の平均点
ディナー価格などが
関係しているようです。

実際の星の計算は
もう少し複雑な計算方法で
違うものでは有ると思いますが
近い形の星を算出できたと思います、


これで、ブラックボックスであった
星の計算方法に少し
近づいたんじゃないかなーと思います。

食べログに掲載されているお店さんで
点数を上げたければ
ここら辺がポイントになるのではと
思ってはおりますが・・・

このデータには
お店側が食べログ側に
お金を支払っているかどうか
というデータが含まれておりません。

なので、実はお金を支払っていないから
星が下がったということも
考えられなくは無いので
実際の予測にはこの情報が不可欠です。

どなたか
このフラグ値お持ちでは
無いでしょうかwwwwwwww

今回はここまでです
それでは。


このページのトップヘ