Прогнозирование нагрузки на энергосистему с визуализацией
Last updated
В этом руководстве мы покажем, как использовать NeuralProphet для прогнозирования нагрузки энергосистемы и визуализации результатов в интерактивном инструменте. Прогноз основан на , который моделирует континентальную электроэнергетическую систему Европы, включая потребности в энергии и притоки возобновляемой энергии за период с 2012 по 2014 год. Общий набор данных содержит 1494 узла, которые мы сократим до 5 узлов для целей данного примера.
# Импорт библиотек для работы с данными:
import pandas as pd # Библиотека pandas для анализа и обработки данных
import numpy as np # Библиотека numpy для численных вычислений
# Импорт библиотек NeuralProphet для прогнозирования временных рядов:
from neuralprophet import NeuralProphet, set_log_level # Класс NeuralProphet и функция set_log_level
# Импорт библиотек Plotly для создания визуализаций:
import plotly.graph_objects as go # Модуль go из Plotly для создания статических элементов визуализации
import plotly.express as px # Модуль px из Plotly для создания выразительных диаграмм с минимальным кодом
from plotly.subplots import make_subplots # Функция make_subplots из Plotly для создания подсюжетов
# Импорт библиотек ipywidgets для создания интерактивных элементов:
import ipywidgets as widgets # Модуль widgets из ipywidgets для создания интерактивных элементов в Jupyter Notebook
from ipywidgets import interact_manual # Функция interact_manual из ipywidgets для создания интерактивных элементов управления
# Установка режима визуализации по умолчанию:
plotting_backend = "plotly-static" # Установить режим визуализации по умолчанию на "plotly-static" для статических изображений
Набор данных включает для каждого автобуса и каждого часа года следующую информацию:
нагрузка (МВт)
фактическое производство солнечной энергии (МВт)
прогнозируемое производство солнечной энергии (МВт)
The forecasts are at hour 00 of every day for the next 24 hours.
# Загрузка данных
df = pd.read_csv(
"https://raw.githubusercontent.com/ourownstory/neuralprophet-data/main/datasets/multivariate/ER_Europe_subset_10nodes.csv",
# Чтение данных из CSV файла по ссылке
)
df["ID"] = df["ID"].astype(str) # Преобразование столбца "ID" в строковый тип
IDs = df["ID"].unique() # Получение уникальных значений из столбца "ID"
df["ds"] = pd.to_datetime(df["ds"]) # Преобразование столбца "ds" в формат даты и времени
# Использование данных за один год для ускорения обучения
df = df[df["ds"] > "2014-01-01"] # Фильтрация данных с датой позже 01.01.2014
# Вывод первых 5 строк измененного dataframe
df.head()
ds
ID
y
solar
solar_fcs
17545
2014-01-01 01:00:00
1
72.3142
0.0
0.0
17546
2014-01-01 02:00:00
1
67.5617
0.0
0.0
17547
2014-01-01 03:00:00
1
63.0677
0.0
0.0
17548
2014-01-01 04:00:00
1
59.9401
0.0
0.0
17549
2014-01-01 05:00:00
1
58.9296
0.0
0.0
# Построение графика первого месяца данных
fig = px.line( # Создать линейный график с помощью plotly.express
df[df["ds"] < "2014-02-01"], # Фильтрация данных за январь 2014
x="ds", # Значения по оси X - дата
y="y", # Значения по оси Y - энергопотребление
color="ID", # Цвет линии определяется по столбцу "ID"
)
# Настройка оформления графика
fig.update_layout(
xaxis_title="Дата", # Заголовок оси X - Дата
yaxis_title="Потребление энергии", # Заголовок оси Y - Потребление энергии
legend_title="ID", # Заголовок легенды - ID
height=600, # Высота графика
width=1000, # Ширина графика
)
# Отображение графика в зависимости от режима визуализации
if plotting_backend == "plotly-static":
fig.show("svg") # Показать график в формате SVG (статическое изображение)
1. Подготовка данных
Чтобы лучше улавливать разные времена года и дни недели, мы добавляем условную сезонность. Для каждого времени года (лето, зима, осень и весна) и будни/выходные моделируется отдельная сезонность.
Лучшие параметры для модели можно получить с помощью настройки гиперпараметров. Установив квантили, моделируется интервал прогнозирования, а не точный прогноз.
# Модель и прогнозирование
quantiles = [0.015, 0.985] # Установить квантили для прогнозных интервалов (1.5% и 98.5%)
# Параметры модели
params = {
"n_lags": 7 * 24, # Количество прошлых наблюдений для прогноза (7 дней * 24 часа)
"n_forecasts": 24, # Количество прогнозируемых шагов (24 часа)
"n_changepoints": 20, # Количество точек изменения тренда
"learning_rate": 0.01, # Скорость обучения
"ar_layers": [32, 16, 16, 32], # Слои авторегрессии с шириной 32, 16, 16, 32 единиц
"yearly_seasonality": 10, # Учитывать сезонность с периодом 10 (вероятно, подразумевается год)
"weekly_seasonality": False, # Игнорировать недельную сезонность
"daily_seasonality": False, # Игнорировать суточную сезонность
"epochs": 40, # Количество эпох обучения
"batch_size": 1024, # Размер пакета данных для обучения
"quantiles": quantiles, # Квантили для прогнозных интервалов
}
# Создать модель NeuralProphet с указанными параметрами
m = NeuralProphet(**params)
# Установить режим визуализации для модели
m.set_plotting_backend(plotting_backend)
# Отключить вывод сообщений ниже уровня ERROR
set_log_level("ERROR")
2.1 Запаздывающие и будущие регрессоры
С дополнительной информацией наша модель может стать лучше. Мы используем данные о фактическом производстве солнечной энергии за последние 10 дней в качестве отстающего регрессора и прогноз солнечной активности в качестве будущего регрессора.
# Добавление запаздывающего регрессора
m.add_lagged_regressor(names="solar", n_lags=10 * 24) # Добавить солнечную активность за 10 дней (24 часа/день)
# Добавление регрессора для будущих значений
m.add_future_regressor(name="solar_fcs") # Добавить прогноз солнечной активности ("solar_fcs")
<neuralprophet.forecaster.NeuralProphet at 0x12cc57b20>
<neuralprophet.forecaster.NeuralProphet at 0x12cc57b20>
3. Обучение модели
Для последующего тестирования нашей модели данные разделяются на обучающие и тестовые. Тестовые данные составляют последние 5% от всего массива данных. Модель обучается на обучающих данных.
# Разделение данных на обучающую и тестовую выборки
df_train, df_test = m.split_df(df, valid_p=0.05, local_split=True) # Разделить данные на 95% обучающих и 5% тестовых
print(f"Размер обучающей выборки: {df_train.shape}") # Вывод формы обучающей выборки
print(f"Размер тестовой выборки: {df_test.shape}") # Вывод формы тестовой выборки
Train shape: (41545, 17)
Test shape: (3090, 17)
# Обучение модели
metrics_fit = m.fit(df_train, freq="H", metrics=True) # Обучить модель на обучающей выборке (freq - частота данных - часы)
# Прогнозирование
forecast = m.predict(df) # Сделать прогнозы на основе всего dataframe (df)
# Извлечение столбцов с сезонностью и трендом и преобразование в числовой формат
season_cols = [col for col in forecast.columns if "season" in col or "trend" in col] # Поиск столбцов с "season" или "trend" в названии
# Преобразование значений в столбцах season_cols в числовой формат
forecast[season_cols] = forecast[season_cols].apply(pd.to_numeric)
Давайте изучим данные более интерактивно. Выбрав дату, будет показан прогноз на следующие 24 часа, что соответствует стандартам индустрии.
def get_day_ahead_format(date, ID, column_name):
"""
Функция извлекает прогноз на следующие сутки для указанной даты, ID узла и названия столбца.
Args:
date (pandas.Timestamp): Дата, для которой требуется прогноз.
ID (str): ID узла, для которого требуется прогноз.
column_name (str): Название столбца с прогнозируемой величиной (например, "y" или "yhat").
Returns:
pandas.DataFrame: Таблица с прогнозами на следующие сутки.
Raises:
ValueError: Ошибка, если запрашиваемая дата отсутствует в прогнозе.
"""
# Создать пустой DataFrame для хранения значений прогноза
values = pd.DataFrame(columns=["ds", column_name])
# Отфильтровать прогнозы по ID узла
forecast_ID = forecast[forecast["ID"] == ID]
# Цикл по 24 часам для формирования прогноза на следующие сутки
for i in range(0, 24):
# Формирование названия столбца с прогнозом для i-го часа
if "%" in column_name:
step_name = f"yhat{i+1} {column_name}" # Например, yhat10 1.5% (прогноз с 10-го часа, 1.5% квантиль)
else:
step_name = f"{column_name}{i+1}" # Например, yhat10 (прогноз на 10-й час)
# Дата следующего часа
ds = date + pd.Timedelta(hours=i + 1)
# Проверка наличия даты в прогнозах для данного ID
if ds not in forecast_ID["ds"].values:
raise ValueError(
f'Дата {ds} отсутствует в прогнозе. Выберите дату в диапазоне от {forecast_ID["ds"].min()} до {forecast_ID["ds"].max()}'
)
# Получение значения прогноза для i-го часа
step_value = forecast_ID[forecast_ID["ds"] == ds][step_name].values[0]
# Добавление строки с прогнозом для i-го часа в DataFrame
values = pd.concat([values, pd.DataFrame({"ds": ds, column_name: step_value}, index=[0])], ignore_index=True)
return values
@interact_manual # с этой строкой кода прогноз становится интерактивным, позволяя вам выбирать идентификатор и дату
def show_forecast_uncertainty(ID=IDs, date=widgets.DatePicker()):
start = pd.to_datetime(date)
end = start + pd.DateOffset(days=1)
# получаем прогноз
np_yhats = get_day_ahead_format(start, ID, "yhat")
np_yhats_lower = get_day_ahead_format(start, ID, "1.5%")
np_yhats_upper = get_day_ahead_format(start, ID, "98.5%")
np_ar = get_day_ahead_format(start, ID, "ar")
# получаем фактические значения, температуру и прогноз температуры
forecast_window = forecast[forecast["ID"] == ID]
forecast_window = forecast_window[(forecast_window["ds"] >= start) & (forecast_window["ds"] < end)]
df_window = df[df["ID"] == ID]
df_window = df_window[(df_window["ds"] >= start) & (df_window["ds"] < end)]
# получаем компоненты
seasonalities_to_plot = []
for season in season_cols:
if np.abs(forecast_window[season].sum()) > 0:
seasonalities_to_plot.append(season)
fig = make_subplots(
rows=3,
cols=1,
shared_xaxes=True,
subplot_titles=("Фактические значения и прогноз", "Солнечная энергия", "Компоненты прогноза"),
)
# рисуем фактические значения
fig.add_trace(
go.Scatter(
x=forecast_window["ds"], y=forecast_window["y"], name="Фактические значения", mode="markers", marker=dict(symbol="x")
),
row=1,
col=1,
)
# рисуем прогноз и неопределенность
fig.add_trace(
go.Scatter(
x=np_yhats_lower["ds"],
y=np_yhats_lower["1.5%"],
fill=None,
mode="lines",
line_color="#A6B168",
name="1.5% квантиль",
),
row=1,
col=1,
)
fig.add_trace(
go.Scatter(
x=np_yhats_upper["ds"],
y=np_yhats_upper["98.5%"],
fill="tonexty",
mode="lines",
line_color="#A6B168",
name="98.5% квантиль",
),
row=1,
col=1,
)
fig.add_trace(go.Scatter(x=np_yhats["ds"], y=np_yhats["yhat"], name="Прогноз", line_color="#7A863B"), row=1, col=1)
# рисуем солнечную энергию
fig.add_trace(
go.Scatter(x=df_window["ds"], y=df_window["solar"], mode="lines", name="Фактическое производство солнечной энергии"), row=2, col=1
)
fig.add_trace(
go.Scatter(x=df_window["ds"], y=df_window["solar_fcs"], mode="lines", name="Прогноз производства солнечной энергии"),
row=2,
col=1,
)
# рисуем различные сезоны из seasonalities_to_plot
for season in seasonalities_to_plot:
fig.add_trace(go.Scatter(x=forecast_window["ds"], y=forecast_window[season], name=season), row=3, col=1)
fig.add_trace(go.Scatter(x=np_ar["ds"], y=np_ar["ar"], name="Авторегрессия"), row=3, col=1)
fig.update_layout(
title=f"Прогноз для ID {ID} на {date}",
yaxis=dict(title="Нагрузка (МВт)"),
width=1200,
height=800,
)
if plotting_backend == "plotly-static":
fig.show("svg")
else:
fig.show()
NeuralProphet включает функции построения графиков для изучения прогнозируемых данных. Для обзора функций построения графиков см.