この記事は「【リアルタイム電力予測】需要・価格・電源最適化ダッシュボード構築記」シリーズの二日目です
Day1はこちら | 全体構成を見る
電力の需要は大きく 「需要の実績」+「気象」+「カレンダー要因」 の3つで決まります。
ここでは、それぞれのデータ源と取得方法をまととめていきます!
電力需要予測に必要な特徴量
1. 電力消費量
取得元:東京電力パワーグリッド でんき予報 CSVダウンロード
-
2023年1月迄
→ 年単位CSV(1年1ファイル) -
2023年度2月以降
→ 月単位 CSV(発電内訳も細分化されている)
2. 気象情報
取得元:気象庁 過去の気象データ・ダウンロード
地点「東京」を選び、必要項目(気温・風・湿度・全天日射など)を選択してダウンロードします。
なお、このCSVは1時間間隔なのでそのままでは電力消費量データ(30分間隔)と揃いません。
3. 日付
取得元:jpholidayライブラリ
- 日本の祝日 →
jpholiday.is_holiday() - 祝日名 →
jpholiday.is_holiday_name()
ただし、年末年始(12/29–1/3)やお盆(8/13–15)は祝日扱いされないため自作フラグを追加する必要があります。
データの取得と整形
ここからは実際のPythonコードでデータ整形の流れを示します。
フォルダ構成としては、data/rawにCSVを保存した前提です。
1. 電力消費量
2023年度1月までと、2月以降でファイル構成が違うので別々に読み込んでいく必要があります。
ここでは、カラム数が多い2023年度2月以降の処理を記載します。
- カラム名を指定
power_2024_columns = [
"date", "time", "demand", "nuclear",
"lng", "coal", "oil", "th_other",
"hydro", "geothermal", "biomass",
"pv", "pv_curtailed", "wind", "wind_curtailed",
"pstorage", "battery", "tie", "misc", "total"
]
- ファイル一覧を取得
power_csv_2024_2025 = []
power_csv_2024 = glob.glob("../data/raw/eria_jukyu_2024*.csv")
power_csv_2025 = glob.glob("../data/raw/eria_jukyu_2025*.csv")
power_csv_2024_2025.extend(power_csv_2024)
power_csv_2024_2025.extend(power_csv_2025)
- CSV読み込みと結合
data_list = []
# 各ファイルを読み込んで結合
for file in power_csv_2024_2025:
# 13~39行目をスキップ
df = pd.read_csv(
file, skiprows=2, encoding="MacRoman", engine="python"
)
# データフレームをリストに追加
data_list.append(df)
# 全てのデータフレームを結合
power_2024 = pd.concat(data_list, ignore_index=True)
power_2024.columns = power_2024_columns
- timestamp列の作成
power_2024["timestamp"] = pd.to_datetime(power_2024["date"] + " " + power_2024["time"])
- demand を float へ(重要!)
後の計算でエラーの原因になりやすいので必ず変換します。
また、今後予測と実績を同じdfで扱う際に分かりづらくなるのでdemand→realized_demandとしました。
power_2024["demand"] = power_2024["demand"].astype("float64")
power_2024.rename(columns={"demand": "realized_demand"}, inplace=True)
- timestampをインデックスに設定し保存
保存形式はcsvではなくparquet形式にします。
メリットは、csvだとdatetime型が引き継がれませんが、parquetだと引き継がれることです。つまり、読み込んだ際の型変換の手間がなくなります。
power_2024 = power_2024.sort_values(by="timestamp").reset_index(drop=True)
power_2024.set_index("timestamp", inplace=True)
power_2024.to_parquet("../data/actual.parquet", index=True)
2. 気象情報
今回は、気温、風、日照時間、全天日射、湿度の項目をダウンロードしていきます。
項目以外は共通の構造をしているので、ここでは気温データの整形を記載します。
- CSV を読み込んで結合
年ごとの気温をダウンロードしてきた前提で、2022_temperature.csvのように名前を変えて保存しています。
csv_files = glob.glob("../data/*temperature.csv") # CSVファイルのパスを指定
# 空のリストを用意してデータを格納
data_list = []
# 各ファイルを読み込んで結合
for file in csv_files:
# 13~39行目をスキップ
df = pd.read_csv(file, skiprows=4, encoding="SHIFT_JIS", engine="python")
# データフレームをリストに追加
data_list.append(df)
# 全てのデータフレームを結合
temperature_all = pd.concat(data_list, ignore_index=True)
- (補足)エンコードの判定例
ダウンロードする項目によってエンコードが違うことがあるので事前に確認します。
import chardet
with open(file_path, "rb") as f:
rawdata = f.read()
result = chardet.detect(rawdata)
encoding = result["encoding"]
print(f"推測されたエンコーディング: {encoding}")
- カラム整理
temperature_all.rename(columns={"Unnamed: 0": "datetime", "Unnamed: 1": "temperature"}, inplace=True)
temperature_all = temperature_all[["datetime", "temperature"]]
temperature_all["timestamp"] = pd.to_datetime(temperature_all["timestamp"])
temperature_all = temperature_all.sort_values(by="timestamp").reset_index(drop=True)
temperature_all
- 欠損値補完(前方埋め)
print(temperature_all.isna().sum())
temperature_all = temperature_all.ffill()
print(temperature_all.isna().sum())
timestamp 0
temperature 5
dtype: int64
timestamp 0
temperature 0
dtype: int64
- 30分間隔へ変換(電力消費量データに揃える)
気象庁のデータは1時間ごとのデータになっているので、reindex + ffill で 30 分ごとに整形していきます。
def to_30min(df):
df.set_index('timestamp', inplace=True)
new_index = pd.date_range(start=df.index.min(), end=df.index.max(), freq='30min')
df = df.reindex(new_index).ffill()
df = df.reset_index()
df.rename(columns={'index': 'timestamp'}, inplace=True)
return df
temperature_all = to_30min(temperature_all)
以上の気象データを湿度、風速、日照時間、全天日射の各データを最後に結合します。
dfs = [power_all, temperature_all, humidity_all, wind_all, sunshine_all, sradiation_all]
all_data = reduce(lambda left, right: pd.merge(left, right, on="timestamp", how="outer"), dfs)
all_data = all_data[all_data['timestamp'] < '2025-04-01 00:00:00']
順番にmergeする方法もありますが、ここではreduce関数を用いて結合しました。reduce(function, iterable[, initializer]) が基本構文で、iterableであるdfsを順番にmergesする処理をして集約していく関数です。
以下の記事にたくさん例が載っていました。
また、今回は2016年4月~2025年3月の9年分のデータを用いていきます。
全体像は以下のようになりました。

3. 日付
電力需要は 曜日・祝日・年末年始・お盆などで大きく変わるため、特徴量化していきます。
- 曜日
all_data["week"] = all_data["timestamp"].dt.dayofweek
- 祝日(jpholoidayライブラリ)
import jpholiday
all_data["is_holiday"] = all_data["timestamp"].dt.date.apply(jpholiday.is_holiday)
all_data["holiday_name"] = all_data["timestamp"].dt.date.apply(jpholiday.is_holiday_name)
- 年末年始およびお盆作成関数
def is_year_end(date):
return (date.month == 12 and date.day >= 29) or (date.month == 1 and date.day <= 3)
all_data["is_year_end"] = all_data["timestamp"].dt.date.apply(is_year_end)
def is_obon(date):
return date.month == 8 and date.day in [13, 14, 15]
all_data["is_obon"] = all_data["timestamp"].dt.date.apply(is_obon)
- フラグ整理
all_data["is_holiday"] = all_data["is_holiday"] | all_data["is_year_end"] | all_data["is_obon"]
all_data["year"] = all_data["timestamp"].dt.year
all_data["month"] = all_data["timestamp"].dt.month
all_data["hour"] = all_data["timestamp"].dt.hour
all_data["is_holiday"] = all_data["is_holiday"].astype(int)
補足: datetime型
pd.to_datetime() で datetime型にすると、.dtで日付や時刻を取り出すことができます。
例えば、sがdatetime型のカラムの場合以下のようにアクセスできます。
s.dt.year # 年(int)
s.dt.month # 月(1〜12)
s.dt.day # 日(1〜31)
s.dt.hour # 時(0〜23)
s.dt.minute # 分(0〜59)
s.dt.second # 秒(0〜59)
s.dt.microsecond # マイクロ秒
s.dt.dayofweek # 月曜=0, 日曜=6 の整数
s.dt.weekday # dayofweek と同じ
s.dt.day_name() # "Monday", "Tuesday" など
s.dt.month_name() # "January" など
※ datetime型のカラムをインデックスにすると、.index.yearのようにアクセスできます。
端(月初・月末・四半期・年)に関するフラグも取得できます。
s.dt.is_month_start # 月初なら True
s.dt.is_month_end # 月末なら True
s.dt.is_quarter_start # 四半期の頭
s.dt.is_quarter_end
s.dt.is_year_start
s.dt.is_year_end
日数や週番号も取得できます。
s.dt.dayofyear # その年の何日目か(1〜365/366)
s.dt.daysinmonth # その月の日数(28〜31)
また、フィルタリングも以下のように簡単にできます。
# 平日のみ
weekday = all_data[all_data["timestamp"].dt.dayofweek < 5]
# 土日・祝日
holiday_or_weekend = all_data[
(all_data["timestamp"].dt.dayofweek >= 5)
]
# 夏(7〜9月)
summer = all_data[
all_data["timestamp"].dt.month.isin([7, 8, 9])
]
# 夜間(22時〜翌5時)
night = all_data[
(all_data["timestamp"].dt.hour >= 22) |
(all_data["timestamp"].dt.hour <= 5)
]
明日
取得した9年分のデータを使って簡単なEDA(探索的データ解析)を行っていきます!![]()
