FANCOMI Ad-Tech Blog

株式会社ファンコミュニケーションズ nend・新規事業のエンジニア・技術ブログ

機械学習を用いた天気予報 その2

こんにちは、気象予報士のy_kawasaki(取得してもう早、15年経つらしい)です。

前回は、精度が向上するどころか、悪化するという大惨事で終わりましたが、精度向上を目指します。

ここで、日本付近の気象学的知識を投入して考えましょう。そう、天気は西から変わるんです!というわけで、安直に、静岡くらいの天気情報を入れたいと思います。

東京と静岡を読み込んで、風向をDropします。


df_tokyo = pd.read_csv('Tokyo.csv')
df_shizuoka = pd.read_csv('Shizuoka.csv')

df_tokyo = df_tokyo.drop(['WindDirection', 'WindDirection_most', 'Date'], axis=1)
df_shizuoka = df_shizuoka.drop(['WindDirection', 'WindDirection_most', 'Date'], axis=1)

このままでは、カラム名が、東京と静岡で被ってしまうので、変更します。

df_tokyo.columns = ['T_Temp_ave', 'T_Temp_max', 'T_Temp_min', 'T_Prec', 'T_Prec_is', 'T_SunDuration', 'T_SunDuration_is', 'T_WindSpeed', 'T_WindSpeed_max', 'T_RH', 'T_RH_min', 'T_Cloud']
df_shizuoka.columns = ['S_Temp_ave', 'S_Temp_max', 'S_Temp_min', 'S_Prec', 'S_Prec_is', 'S_SunDuration', 'S_SunDuration_is', 'S_WindSpeed', 'S_WindSpeed_max', 'S_RH', 'S_RH_min', 'S_Cloud']

ラベルを作ります。

y_label = []
for i in range(len(df_tokyo) -1):
    y_label.append(0 if df_tokyo.T_Prec[i + 1] < 1. else 1)
df_label = pd.DataFrame({'label': y_label})

ラベルを結合して、

df = pd.concat([df_tokyo, df_shizuoka, df_label], axis=1).dropna()

シャッフルして、

df = df.reset_index(np.random.permutation(df.index), drop=True)

切片をつけて

df['intercept'] = 1

cross validationをします。

cross_val_score(LogisticRegression(), df.drop("label", axis=1), df.label, cv=5).mean()
0.71747329443213614

だめです。まだだめですね。ここでめげてはいけません。名古屋を追加して、風向をDropして、東京と静岡と名古屋とラベルを結合します。そしてシャッフル。切片項を付与!

df_nagoya = pd.read_csv('Nagoya.csv')

df_nagoya = df_nagoya.drop(['WindDirection', 'WindDirection_most', 'Date'], axis=1)

df = pd.concat([df_tokyo, df_shizuoka, df_nagoya, df_label], axis=1).dropna()
df = df.reset_index(np.random.permutation(df.index), drop=True)
df['Intercept'] = 1

で、恒例のcross validation、

cross_val_score(LogisticRegression(), df.drop("label", axis=1), df.label, cv=5).mean()
0.74850792065819993

精度が改善しました!念のため大阪も入れてみましょう。

df_osaka = pd.read_csv('Nagoya.csv')

df_osaka = df_osaka.drop(['WindDirection', 'WindDirection_most', 'Date'], axis=1)

df = pd.concat([df_tokyo, df_shizuoka, df_nagoya, df_osaka, df_label], axis=1).dropna()
df = df.reset_index(np.random.permutation(df.index), drop=True)
df['Intercept'] = 1
cross_val_score(LogisticRegression(), df.drop("label", axis=1), df.label, cv=5).mean()
0.74257704829365201

特に大きな変化は見られません。
流石に変数が多すぎて、良いモデルとはいえないので、変数選択をして、変数を減らしてみました。

df_t1 = df_tokyo['T_Prec_is']
df_s1 = pd.concat([df_shizuoka['S_SunDuration'], df_shizuoka['S_WindSpeed'], df_shizuoka['S_WindSpeed_max'], df_shizuoka['S_Cloud']], axis=1)
df_n1 = pd.concat([df_nagoya['N_Prec'], df_nagoya['N_Prec_is'], df_nagoya['N_WindSpeed_max'], df_nagoya['N_RH']], axis=1)
df_o1 = pd.concat([df_osaka['O_RH'], df_osaka['O_Cloud']], axis=1)

df = pd.concat([df_t1, df_s1, df_n1, df_o1, df_label], axis=1).dropna()
df = df.reset_index(np.random.permutation(df.index), drop=True)
df['Intercept'] = 1
cross_val_score(LogisticRegression(), df.drop("label", axis=1), df.label, cv=5).mean()
0.74668767747371056

他の指標も確認しておきます。

指標名 スコア
見逃し率 0.15034168564920272
空振り率 0.07061503416856492
スレットスコア 0.3618421052631579
バイアススコア 0.7107438016528925
スキルスコア 0.3922010819143318

全体的に改善はしています。

このアプローチでは、この程度が限界のようです。なかなか難しいですね。