最近、統計検定を終えて一息ついたので、よく出てくるアイリスとかのおもちゃデータではなく実際の生のデータでの機械学習をやってみたいと思い天気予測をやってみようと思った。僕は彦根市に住む大学生なのでとりあえず彦根市の予測できたらいいと思います。
気象庁|過去の気象データ検索
このサイトから過去十年間の6月1日から7月1日の気温(平均、最高、最低)気圧、風速、天気概況、平均雲量、降水の合計をダウンロードしてきました。ダウンロードしたものをpandasで読み込んでみたところ、ダウンロードした項目はせいぜい10個くらいなのに異様に項目の数が多かった。どうやらそれぞれの項目について品質情報と品質番号というものがあるようです。一応気象庁の説明のページを読んでみましたが、とりあえず消してよさそう(もし気を付けることがありましたら、下のコメントにアドバイス欲しいです。)とおもい、すべて消去しました。あと、積雪とかもダウンロードしてきてしまったのでそれも消去しました。
import numpy as np
import pandas as pd
df_org=pd.read_csv("data.csv",encoding="shiftjis",header=2).drop([0,1],axis=0).reset_index(drop=True)
df = df_org.drop(['平均気温(℃).1','平均気温(℃).2','最高気温(℃).1','最高気温(℃).2','最低気温(℃).1','最低気温(℃).2','天気概況(昼:06時〜18時).1','天気概況(昼:06時〜18時).2','天気概況(夜:18時〜翌日06時).1','天気概況(夜:18時〜翌日06時).2','降水量の合計(mm).1','降水量の合計(mm).2','降水量の合計(mm).3','日照時間(時間).1','平均風速(m/s).1','平均風速(m/s).2','平均現地気圧(hPa).1','平均現地気圧(hPa).2','最深積雪(cm)','最多風向(16方位)','日照時間(時間).2','日照時間(時間).3','最深積雪(cm).1','最深積雪(cm).2','最深積雪(cm).3','最多風向(16方位).1','最多風向(16方位).2','平均雲量(10分比).1','平均雲量(10分比).2'], axis =1)
###テキストデータの整形
今回は天気概況の部分が文字になっているのでこいつを数値にしてあげます。一文字目が"晴"または"快"の時1、"曇"の時2、"雨"の時3、それ以外は4にしました。本当は天気がひどくなるにつれて数字を上げていったほうが良いのでしょうけれど、ちょっと大変だったのでさぼってしまいました。
#めんどくさそうなので一文字目の漢字で分けてしまおう晴:1曇:2雨:3その他:4
for i in range(len(df)):
s = [df['天気概況(昼:06時〜18時)'][i],df['天気概況(夜:18時〜翌日06時)'][i]]
for j in range(2):
if s[j][0] == '晴' or s[j][0] == '快':s[j] = 1
elif s[j][0] == '曇':s[j] = 2
elif s[j][0] == '雨':s[j] = 3
else:s[j] = 4
df['天気概況(昼:06時〜18時)'][i],df['天気概況(夜:18時〜翌日06時)'][i] = s[0],s[1]
###データの作成
さっきの天気概況を数字に変換したものの、2以下なら1、3以上なら0として教師データを作成しました。(今思えばせっかく降水量合計があったんだからそれを使えばよかったと後悔)
#雨(3以上の数字が入ってなければ1)
ans_ls = [0 for i in range(len(df))]
for i in range(len(df)):
s = [df['天気概況(昼:06時〜18時)'][i],df['天気概況(夜:18時〜翌日06時)'][i]]
if s[0] <= 2 and s[1] <= 2:
ans_ls[i] = 1
else:ans_ls[i] = 0
最後に教師データと前日のデータを配列に入れてやります。
data_conp = []
for i in range(len(df)):
_,m,d = df.iloc[i,0].split('/')
if int(d) <= 1 and m == '7':continue
data_conp.append([])
ls = data_conp[-1]
ls.append(ans_ls[i+1])
for k in range(i-1,i):
for j in range(10):
ls.append(df.iloc[k,j+1])
y = np.array([0]*len(data_conp))
x = np.array([np.array([0.0 for i in range(len(data_conp[0]) - 1)]) for i in range(len(data_conp))])
for i in range(len(data_conp)):
for j in range(len(data_conp[0])):
if j == 0:
y[i] = data_conp[i][j]
else:
x[i][j-1] = data_conp[i][j]
とりあえずこれで当日の天気と一日前の気象データを持ったデータが出来上がりました。これを機械学習にかけます。便利ツールsklearnのRandomForestClassifierを使います。1列目から10列目各組合せをそれぞれ学習を行い三十回の平均をとって精度を算出しました。(アドバイスなどあったらコメントお願いします。)
#過去何日分のデータを取り扱っているか
days = 1
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.ensemble import RandomForestClassifier
import warnings
warnings.filterwarnings('ignore')
#組み合わせを全列挙する関数
def conv(n):
ls = [[] for i in range(2 << (n-1))]
for i in range(2 << (n-1)):
for j in range(n):
if (i >> j) & 1:
ls[i].append(j)
return ls
#ランダムに分割したデータを機械学習する
#30回の平均をとる
def score_forest(x,y,convLs = None,treeNum = 10,depth = 10,jobs = 1):
scoreMean_train = 0
scoreMean_test = 0
for i in range(30):
if convLs == None:
x_train, x_val, y_train, y_val = train_test_split(x, y, stratify=y, train_size=0.8)
else:
x_train, x_val, y_train, y_val = train_test_split(x[:,convLs], y, stratify=y, train_size=0.8)
clf = RandomForestClassifier(n_estimators = treeNum,max_depth=depth,n_jobs = jobs)
clf.fit(x_train, y_train)
y_train_pred = clf.predict(x_train)
y_val_pred = clf.predict(x_val)
train_score = accuracy_score(y_train, y_train_pred)
test_score = accuracy_score(y_val,y_val_pred)
#print('{}回目のスコア : {}'.format(i+1,train_score))
scoreMean_train += train_score
scoreMean_test += test_score
#print('組み合わせ : {} 十回の平均 : {}'.format(convLs,scoreMean / 10))
return (convLs,scoreMean_train / 30,scoreMean_test / 30)
convLs = conv(10)
del convLs[0]
ls = []
ls = [score_forest(x,y,[i+j*10 for j in range(days) for i in convLs[k]],depth = 2,treeNum = 4,jobs = 1) for k in range(len(convLs))]
#テストデータの精度が高い順にソート
ls.sort(key=lambda x:x[2],reverse = True)
print(ls[:5])
結果、最高で0.58でした。過去のデータが前日だけでは弱いのかなと思い、過去3日と過去7日でも試してみましたが改善は見られませんでした。まあ期待値は0.5だから一応機械学習の意味はあるのかなとこの記事を書き始める1時間前くらいまでは思ってました。でも晴れの日と雨の日、五分五分じゃないですよね。砂漠とかずっと晴れてるし。とおもい、期待値を出してみました。
one = 0
for i in y:
if i == 1:one += 1
one/len(y)
結果、0.58でした。予測できてないやん!!という結果に...
###改善に向けて
そもそも今回のように一地点のみの気象データを使っているのが悪いのではないだろうか。とりあえず今回これを書きながら出てきた反省点をふまえつつ東西南北(愛知、兵庫、大阪、福井)のデータを使って予測ができないかをこの土日にでもやってみようと思います。