H2O.aiのAutoMLを使って電気使用量を学習し、気象予報値取得APIを使って数日後の電気使用量を予測してみる

# はじめに

もう何度も同じネタではありますが、今回はH2O.aiのAutoMLを使って電気使用量を予測してみます。

[H2O documentation](https://docs.h2o.ai/h2o/latest-stable/h2o-docs/index.html)

# 動作環境

- mac0S 10.14
- Python3

# インストール

## Java

macOS用のJavaをOracleの公式サイトからダウンロードして、インストールします。

https://docs.oracle.com/en/java/javase/11/install/overview-jdk-installation.html

## H2O

以下のURLを参照しながらインストールします。

https://docs.h2o.ai/h2o/latest-stable/h2o-docs/downloading.html

必要なライブラリをインストール。

```
pip install requests
pip install tabulate
pip install "colorama>=0.3.8"
pip install future
```

ちなみに、僕のMacに足りないのはtabulateだけでした。
必要なライブラリをインストール後、H2Oをインストールします。

```
pip install -f http://h2o-release.s3.amazonaws.com/h2o/latest_stable_Py.html h2o
```

# データ読込

まずは、中国電力から電気使用量実績と気象庁から広島と松江の時別気温のデータをダウンロードして、CSVで保存しておきます。

保存したCSVを以下のコードで読み込みます。

```py:
from glob import glob
import pandas as pd

##### 電気使用量実績の読込

files = glob("juyo-*.csv")
files.sort()

df_kw = pd.DataFrame()

for f in files:
    df_kw = pd.concat([df_kw, pd.read_csv(f,encoding="Shift_JIS",skiprows=2)])

df_kw = df_kw.reset_index(drop=True)
df_kw["MW"] = df_kw["実績(万kW)"] * 10

df_kw.index = df_kw.index.map(lambda _: pd.to_datetime(df_kw.DATE[_] + " " + df_kw.TIME[_]))

##### 気温データの読込

# 気温データ読込関数
def read_temp(city):
    
    df_tmp = pd.DataFrame()
    
    files = glob("data-{}-*.csv".format(city))
    files.sort()
    
    for f in files:
        df_tmp = pd.concat([df_tmp, pd.read_csv(f,encoding="Shift_JIS",skiprows=4)])
    
    df_tmp = df_tmp.reset_index(drop=True)
    df_tmp.columns = ["DATETIME","TEMP","品質情報","均質番号"]
    
    df_tmp.DATETIME = df_tmp.DATETIME.map(lambda _: pd.to_datetime(_))
    df_tmp.index = df_tmp.DATETIME
    
    return df_tmp

# 広島
df_tmp_hiroshima = read_temp("hiroshima")

# 松江
df_tmp_matsue = read_temp("matsue")

##### データの結合

# データの複製
df = df_kw.copy()

# 広島の気温を結合
df["TMP_hiroshima"] = df_tmp_hiroshima.TEMP
# 松江の気温を結合
df["TMP_matsue"] = df_tmp_matsue.TEMP
```

# データ加工

```py:
# 月、週、時間の値を個別に取得
df["MONTH"] = df.index.month
df["DAY"] = df.index.day
df["WEEK"] = df.index.weekday
df["HOUR"] = df.index.hour

cols = ['MW', 'TMP_hiroshima', 'TMP_matsue', 'MONTH', 'DAY', 'WEEK', 'HOUR']
df = df[cols]

df = df.dropna()

# One-hotエンコーディング

cols = ["MONTH","WEEK","HOUR"]
for col in cols:
    df = df.join(pd.get_dummies(df[col], prefix=col))

# 学習用と検証用に分割

df_valid = df["2020-12-01":]
df = df[:"2020-11-30"]

# 学習に使用する変数の列名を指定

x_cols = ['TMP_hiroshima', 'TMP_matsue', 'DAY']

for i in range(12):
    x_cols.append("MONTH_{}".format(i+1))

for i in range(7):
    x_cols.append("WEEK_{}".format(i))

for i in range(24):
    x_cols.append("HOUR_{}".format(i))
```

# 学習

```py:
import h2o
from h2o.automl import H2OAutoML

h2o.init()

kw = h2o.H2OFrame(df[x_cols + ["MW"]])

predictors = kw.columns[:-1]
response = "MW"

train, test = kw.split_frame(ratios = [0.8], seed = 1234)

aml = H2OAutoML(max_runtime_secs = 60)
aml.train(x = predictors, y = response,
          training_frame = train,
          leaderboard_frame = test)

lb = aml.leaderboard
print(lb)
```

実行結果

```
AutoML progress: |████████████████████████████████████████████████████████| 100%
model_id	mean_residual_deviance	rmse	mse	mae	rmsle
StackedEnsemble_AllModels_AutoML_20210130_220026	55238.9	235.029	55238.9	179.543	0.0360048
StackedEnsemble_BestOfFamily_AutoML_20210130_220026	55576.7	235.747	55576.7	180	0.0361164
XGBoost_grid__1_AutoML_20210130_220026_model_1	55711.4	236.033	55711.4	180.202	0.0361703
XGBoost_2_AutoML_20210130_220026	78478.5	280.14	78478.5	216.709	0.0425244
GBM_grid__1_AutoML_20210130_220026_model_1	81868.2	286.126	81868.2	219.097	0.0433825
XGBoost_1_AutoML_20210130_220026	117797	343.216	117797	263.161	0.0515385
XGBoost_3_AutoML_20210130_220026	134151	366.266	134151	278.607	0.0557366
DRF_1_AutoML_20210130_220026	140472	374.796	140472	287.559	0.0551513
DeepLearning_grid__1_AutoML_20210130_220026_model_1	168062	409.953	168062	295.05	0.0587799
GBM_grid__1_AutoML_20210130_220026_model_2	222553	471.755	222553	368.56	0.0710886
Out[34]:
```

# 評価

```py:
from sklearn.metrics import r2_score

y_test = test.as_data_frame().MW
y_pred = aml.predict(test).as_data_frame().predict

print(r2_score(y_test, y_pred))
```

実行結果

```
stackedensemble prediction progress: |████████████████████████████████████| 100%
0.9570278780866834
```

# 検証

```py:
kw_valid = h2o.H2OFrame(df_valid[x_cols])
y_pred = aml.predict(kw_valid).as_data_frame().predict
y_valid = df_valid["MW"]

print(r2_score(y_valid, y_pred))
```

実行結果

```
Parse progress: |█████████████████████████████████████████████████████████| 100%
stackedensemble prediction progress: |████████████████████████████████████| 100%
0.759670901243868
```

![Unknown.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114240/a88c3924-96f9-b5c3-df47-98c8864ca725.png)

ま、こんなものかな。

# 予測

以下のAPIを利用して明後日の電気使用量を予測します。

[気象予報値などをAPIで取得できるサービスのテスト版を公開してみた](https://qiita.com/mix_dvd/items/c3c40f8ed6445a16a3fe)

```py:
import requests
import io

headers = {'Authorization': 'Token [自分用のトークンを使用]'}
url = "https://iot.blueomega.jp/spnext/api/v1/weather/forecast.csv"

df_pred = pd.DataFrame()
target_date = ""

for city in ["Hiroshima", "Matsue"]:
    
    payload = {
        "point": city
    }
    if len(target_date) > 0:
        payload["date"] = target_date
        
    rs = requests.get(url, headers=headers, params=payload)
    df = pd.read_csv(io.StringIO(rs.content.decode('utf-8')), header=0)
    
    df["flg"] = True
    for i in range(len(df) - 1):
        if df.iloc[i]["forecast_time"] == df.iloc[i+1]["forecast_time"]:
            df.loc[i+1, "flg"] = False
    df = df[df.flg == True]
    
    df.index = pd.to_datetime(df.forecast_time)
    
    df_pred["TMP_{}".format(city.lower())] = df["temperature"]
    
df_pred["MONTH"] = df_pred.index.month
df_pred["DAY"] = df_pred.index.day
df_pred["WEEK"] = df_pred.index.weekday
df_pred["HOUR"] = df_pred.index.hour

cols = ["MONTH","WEEK","HOUR"]
for col in cols:
    df_pred = df_pred.join(pd.get_dummies(df_pred[col], prefix=col))

for col in x_cols:
    if col not in df_pred.columns:
        df_pred[col] = 0.0

kw_pred = h2o.H2OFrame(df_pred[x_cols])

df_pred["MW"] = aml.predict(kw_pred).as_data_frame().predict.tolist()
df_pred.MW.plot(figsize=(15,5))
```

![Unknown.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114240/eb75bb95-1600-ef1f-0d38-0df2a475986d.png)

できた！