機械学習を使って何か予測してみたいなと考えたとき、ドラマの視聴率は予測できないのかなと思い実際にやってみることにしました。
今回はJupyter notebookを使って実装しました。
データセット
https://artv.info/
ここから2015年と2016年のドラマの視聴率を取ってきてcsvファイルにする テストデータとして2017年のデータを取ってくる
データの説明
データの総数:176
目的変数:ドラマの視聴率の平均
説明変数:放送された年、曜日、時間帯、テレビ局、1回目の視聴率
1回目の視聴率はドラマのタイトルや出演者、CMの量などが影響してきていると考えられるので、今回はデータとして含めることができないそうした要素を加味するために加えることにした。 また、ドラマの名前は日本語でそのままでは扱いずらいので削除した。 まず必要なライブラリーをまとめてインストールして、作ったcsvファイルをインポート
import pandas as pd
import numpy as np
import seaborn as sns
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
%matplotlib inline
from sklearn.ensemble import RandomForestRegressor
data = pd.read_csv('train.csv')
test=pd.read_csv("test2.csv")
data.describe()
|
year
|
time
|
1
|
average
|
count
|
176.000000
|
176.000000
|
176.000000
|
176.000000
|
mean
|
14.920455
|
21.693182
|
10.503977
|
0.090645
|
std
|
0.824227
|
0.790775
|
3.527239
|
0.033990
|
min
|
14.000000
|
20.000000
|
2.700000
|
0.029300
|
25%
|
14.000000
|
21.000000
|
8.275000
|
0.069225
|
50%
|
15.000000
|
22.000000
|
10.100000
|
0.084500
|
75%
|
16.000000
|
22.000000
|
12.900000
|
0.107550
|
max
|
16.000000
|
23.000000
|
26.500000
|
0.229400
|
|
year
|
time
|
1
|
average
|
year
|
1.000000
|
0.120151
|
-0.195264
|
-0.111665
|
time
|
0.120151
|
1.000000
|
-0.408886
|
-0.361273
|
1
|
-0.195264
|
-0.408886
|
1.000000
|
0.896363
|
average
|
-0.111665
|
-0.361273
|
0.896363
|
1.000000
|
import seaborn as sns
plt.figure(figsize=(9,9))
sns.heatmap(data.corr(), annot=True, square=True, fmt='.2f')
plt.show()
sns.pairplot(data )
plt.show()
1回目の視聴率との相関がかなり高いことがわかります。(それはそうかな)
データの前処理
文字列のものはこのままでは学習できないので、ダミー変数化をします。
def dum(data,name):
dum=pd.get_dummies(data[name])
del(data[name])
data = pd.concat([data, dum], axis=1)
return data
data=dum(data,"season")
data=dum(data,"day")
data=dum(data,"TV")
同様にテストデータもダミー変数化する
test=dum(test,"season")
test=dum(test,"day")
test=dum(test,"TV")
学習できるようにnumpyの形に変形
X_train=data.drop("average",axis=1).values
y_train=data.loc[:,"average"].values
X_test=test.drop("average",axis=1).values
y_test=test.loc[:,"average"].values
学習
線形の重回帰をする
from sklearn.linear_model import LinearRegression
model_lin = LinearRegression()
model_lin.fit(X_train, y_train)
平均二乗平方誤差を求める
predict=model_lin.predict(X_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
出力:0.013120566634344053
視聴率にして大体1%くらい予測がずれていことがわかります。
次に2次関数で回帰を試してみます。
quad = PolynomialFeatures(degree=2)
quad_data= quad.fit_transform(X_train)
quad_test= quad.fit_transform(X_test)
model_quad = LinearRegression()
model_quad.fit(quad_data, y_train)
予測すると
predict=model_quad.predict(quad_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
0.03321449872832119
誤差が少し増えてしまいました。
おそらく原因は1回目の視聴率と視聴率の平均の間に強い線形関係があったからだと思われます。
次に、少しアルゴリズムが異なる決定木を用いたモデルを試してみました。
今回はRandomForestを選択
rf=RandomForestRegressor(n_estimators=100)
rf.fit(X_train,y_train)
rf.score(X_test,y_test)
predict=rf.predict(X_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
0.011895995071281513
線形回帰より少し精度のよい予測ができました。そこで、実際に予測結果を出力してみます。
y_train=test.loc[:,"average"]
result=pd.concat([y_train,predict],axis=1)
result
予測結果を出力してみるとこのような感じになります。
average predict
0 0.0665 0.080544
1 0.1130 0.100812
2 0.0890 0.093390
3 0.1139 0.126772
4 0.0974 0.094629
5 0.0653 0.074575
6 0.0342 0.037060
7 0.0832 0.091118
8 0.0646 0.062421
9 0.0963 0.106813
10 0.0499 0.063101
11 0.1455 0.123757
12 0.0930 0.093373
13 0.0877 0.097061
14 0.1058 0.124737
15 0.1125 0.101169
16 0.0917 0.077444
17 0.1409 0.153647
18 0.0644 0.077785
19 0.0333 0.035912
20 0.0879 0.087770
21 0.0545 0.061309
22 0.0955 0.095583
23 0.1360 0.117685
24 0.0535 0.059104
25 0.0768 0.093071
26 0.1478 0.135021
27 0.0607 0.065102
28 0.1017 0.099923
29 0.1147 0.096129
30 0.1097 0.107221
31 0.1145 0.097105
32 0.0450 0.047390
33 0.0345 0.035188
34 0.0733 0.086210
35 0.0400 0.053883
36 0.0870 0.094185
37 0.0973 0.092584
38 0.0654 0.078296
39 0.0858 0.074088
40 0.0674 0.086991
41 0.0575 0.076933
42 0.0773 0.093607
43 0.1265 0.095393
44 0.2087 0.213110
45 0.0653 0.059748
46 0.0348 0.036104
47 0.1187 0.114391
48 0.0483 0.054790
49 0.0877 0.087007
50 0.1600 0.140068
51 0.0619 0.064881
1回目の視聴率があるとかなり予測しやすいので、1回目の視聴率を除いて予測してみます。
del data["1"]
del test["1"]
X_train=data.drop("average",axis=1).values
y_train=data.loc[:,"average"].values
X_test=test.drop("average",axis=1).values
y_test=test.loc[:,"average"].values
まず線形回帰モデルを試します。
from sklearn.linear_model import LinearRegression
model_lin = LinearRegression()
model_lin.fit(X_train, y_train)
predict=model_lin.predict(X_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
0.029758171793322862
誤差は視聴率にして3%程度であまりよくないです。
次に、2次関数で試してみます
quad = PolynomialFeatures(degree=2)
quad_data= quad.fit_transform(X_train)
quad_test= quad.fit_transform(X_test)
model_quad = LinearRegression()
model_quad.fit(quad_data, y_train)
predict=model_quad.predict(quad_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
0.034900994501918164
さらに精度が落ちてしまいました。
ダミー変数化している特徴量が多いので精度が下がってしまったのかもしれないです。
最後にRandomForestを試してみます
rf=RandomForestRegressor(n_estimators=100)
rf.fit(X_train,y_train)
predict=rf.predict(X_test)
predict=pd.DataFrame(predict, columns=["predict"])
for i in range(len(predict)):
if predict.loc[i,"predict"]<0:
predict.loc[i,"predict"]=0
elif predict.loc[i,"predict"]>1:
predict.loc[i,"predict"]=1
np.sqrt(mean_squared_error(y_test,predict))
0.020205388662540902
誤差2%程度で、そこそこ予測できているが、今回は標準偏差が大きくないデータでの予測なので、あまり精度が良くないと思われます。実際に2017年の平均視聴率を予測して比較してみると
y_train=test.loc[:,"average"]
result=pd.concat([y_train,predict],axis=1)
result
average predict
0 0.0665 0.094750
1 0.1130 0.084270
2 0.0890 0.090770
3 0.1139 0.108972
4 0.0974 0.130336
5 0.0653 0.076118
6 0.0342 0.036407
7 0.0832 0.074656
8 0.0646 0.065642
9 0.0963 0.097014
10 0.0499 0.072753
11 0.1455 0.099682
12 0.0930 0.087231
13 0.0877 0.088673
14 0.1058 0.080945
15 0.1125 0.085320
16 0.0917 0.124294
17 0.1409 0.116720
18 0.0644 0.064154
19 0.0333 0.035253
20 0.0879 0.088994
21 0.0545 0.072029
22 0.0955 0.083854
23 0.1360 0.147650
24 0.0535 0.066186
25 0.0768 0.084481
26 0.1478 0.100631
27 0.0607 0.071729
28 0.1017 0.077674
29 0.1147 0.114791
30 0.1097 0.085149
31 0.1145 0.102019
32 0.0450 0.072888
33 0.0345 0.032152
34 0.0733 0.061983
35 0.0400 0.061864
36 0.0870 0.074883
37 0.0973 0.100981
38 0.0654 0.065103
39 0.0858 0.082320
40 0.0674 0.086221
41 0.0575 0.086326
42 0.0773 0.122406
43 0.1265 0.122159
44 0.2087 0.191780
45 0.0653 0.074801
46 0.0348 0.035069
47 0.1187 0.104266
48 0.0483 0.071953
49 0.0877 0.085039
50 0.1600 0.115844
51 0.0619 0.077719
まとめ
1回目の視聴率は平均視聴率に大きく関係していることがわかりました
つまり、ドラマの視聴率を上げるためには、1回目の視聴率がかなり大切になってくる気がします。
そのために、ドラマが始まる前にたくさんCMを打つことは視聴率を上げるうえで効果がありそうだなと感じました。