Energy Consumption Forecasting with Prophet

Analyze hourly energy consumption data, explore daily and weekly patterns, and build a forecasting model using Prophet.

This page shows an AI data analysis example. A workflow is a list of prompts (instructions) used to complete one analysis task step by step. We send the same prompt list to multiple AI models, then compare their notebook outputs and scores.

What this AI workflow does

This AI Data Analyst workflow loads the AEP hourly energy consumption CSV and plots the full time series to inspect the overall trend. It computes and visualizes average consumption patterns by hour of day and by day of week to highlight recurring seasonality. It fits a Prophet model to the historical data, forecasts the next 30 days, and displays forecast components including trend and seasonal effects.

Who this example is for

This is for analysts and data scientists who need a quick, reproducible baseline forecast for hourly electricity demand. It is also useful for practitioners learning how to translate exploratory time-series patterns into a Prophet forecasting workflow.

Expected analysis outcomes

These are the results the AI workflow is expected to generate.

  • Line chart of the full hourly consumption time series
  • Bar chart of average consumption by hour of day
  • Bar chart of average consumption by day of week
  • 30-day Prophet forecast plot with uncertainty intervals
  • Prophet components plot showing trend and seasonalities

Tools and libraries used

Main Python packages and tooling used to run this AI data analysis task.

  • pandas
  • numpy
  • matplotlib
  • seaborn
  • prophet

Prompt sequence

This is the exact list of prompts used in this workflow. The same prompt sequence is sent to each model so outputs and scores can be compared fairly.

  1. 1load AEP hourly energy data from https://raw.githubusercontent.com/pplonski/datasets-for-start/refs/heads/master/aep-hourly-energy-consumption/AEP_hourly.csv and plot overall trend
  2. 2show average consumption by hour of day and by day of week
  3. 3fit a hourly Prophet model and forecast for the next 30 days
  4. 4show the forecast components: trend, weekly, yearly seasonality

Model Comparison

We compared several LLM models on the same analysis task. The table below shows their scores side by side. You can also open each model run to check the full conversation and notebook results.

Model SourceScoreTaskExecutionOutputReasoningReliabilityRun
gemma4:31b10/102/22/23/32/21/1Open gemma4:31b conversation
glm-5.110/102/22/23/32/21/1Open glm-5.1 conversation
gpt-5.410/102/22/23/32/21/1Open gpt-5.4 conversation
gpt-oss:120b10/102/22/23/32/21/1Open gpt-oss:120b conversation
qwen3.5:397b10/102/22/23/32/21/1Open qwen3.5:397b conversation
qwen3-coder-next3/101/20/21/31/20/1Open qwen3-coder-next conversation
How scoring works

Each run is graded on five dimensions:

  • Task Completion (0-2): how fully the workflow answered the requested analysis task and completed required steps.
  • Execution Correctness (0-2): whether generated code is correct, consistent, and likely runnable without major fixes.
  • Output Quality (0-3): how well tables, charts, and results match the expected analytical outcomes.
  • Reasoning Quality (0-2): clarity and correctness of interpretation, explanation, and conclusions.
  • Reliability (0-1): robustness of the run, including low hallucination risk and stable behavior.

The final score is the sum of these dimensions, so total score = 0-10. Higher values indicate more complete, correct, and reliable notebook results for this workflow.

AI Analysis with gpt-oss:120b

This run uses AI with a step-by-step list of prompts to perform the analysis task Energy Consumption Forecasting with Prophet. Below, you can review the full conversation generated with gpt-oss:120b. After the conversation, we show detailed score cards for this run.

load AEP hourly energy data from https://raw.githubusercontent.com/pplonski/datasets-for-start/refs/heads/master/aep-hourly-energy-consumption/AEP_hourly.csv and plot overall trend
Thinking

We need to load the CSV from URL, show shape, columns, head maybe, then plot overall trend. According to guidelines, first step: load and inspect only. So load into df, then output shape, columns, head. Provide code block and stop.

Load AEP consumption data
import pandas as pd

# Load the AEP hourly energy consumption data
url = "https://raw.githubusercontent.com/pplonski/datasets-for-start/refs/heads/master/aep-hourly-energy-consumption/AEP_hourly.csv"
df = pd.read_csv(url)

# Show basic info
df.shape, df.columns, df.head()
((121273, 2),
 Index(['Datetime', 'AEP_MW'], dtype='str'),
               Datetime   AEP_MW
 0  2004-12-31 01:00:00  13478.0
 1  2004-12-31 02:00:00  12865.0
 2  2004-12-31 03:00:00  12577.0
 3  2004-12-31 04:00:00  12517.0
 4  2004-12-31 05:00:00  12670.0)
Thinking

The user wants to plot overall trend. So far we have loaded data and displayed shape and head. No plot yet. So we need to generate a plot of overall trend. That is missing. So we should choose Path B, provide brief insight from current outputs (e.g., dataset has 121,273 rows, two columns: Datetime and AEP_MW, first few rows). Then give code to plot overall trend (time series). Use seaborn/matplotlib line plot. Provide code block. No need for user to run again? We must provide code block, we assume they will run. It's okay.

The data contains 121 273 hourly records with timestamps (Datetime) and megawatt values (AEP_MW). Let’s create a line chart to visualise the overall consumption trend over time.

Plot AEP time series
import matplotlib.pyplot as plt
import seaborn as sns

# Ensure Datetime is parsed as a datetime type
df['Datetime'] = pd.to_datetime(df['Datetime'])

# Plot the time series
plt.figure(figsize=(12, 4))
sns.lineplot(data=df, x='Datetime', y='AEP_MW', linewidth=0.7)
plt.title('AEP Hourly Energy Consumption – Overall Trend')
plt.xlabel('Date')
plt.ylabel('AEP (MW)')
plt.tight_layout()
plt.show()
Thinking

We need to decide if current outputs answer original goal: load data and plot overall trend. They have loaded data, plotted line chart. The goal is to "load AEP hourly energy data ... and plot overall trend". The plot is shown (figure). That's sufficient. No further code needed. Provide final answer with insights: dataset size, time range, overall trend (perhaps decreasing/increasing). Provide concise description and perhaps note trend: from 2004 onward showing seasonal patterns etc. Provide final answer without code.

The dataset was successfully loaded (121 273 rows, two columns Datetime and AEP_MW). After converting Datetime to a proper datetime type, a line chart of the hourly AEP consumption was generated. The figure shows the overall consumption pattern over the full time span, revealing the long‑term behaviour and the characteristic seasonal fluctuations in the hourly load. This satisfies the request to load the AEP hourly energy data and plot its overall trend.

show average consumption by hour of day and by day of week
Thinking

User wants average consumption by hour of day and by day of week. We have df with Datetime and AEP_MW. Need to compute groupby hour (0-23) and day of week (Monday=0). We can add columns hour and weekday, then compute mean. Show two tables or maybe a heatmap. Provide code to compute and display. Probably show two tables: avg by hour, avg by day of week. Could also plot line chart for hour of day and bar for day of week. Provide code.

Below we add hour‑of‑day and weekday columns, then compute the mean AEP (MW) for each hour and each weekday. Two tables are displayed:

  • hour_avg – average consumption for each of the 24 hours (0 – 23)
  • weekday_avg – average consumption for each weekday (Monday = 0 … Sunday = 6)
Compute hourly and weekday averages
# Add hour and weekday columns
df['hour'] = df['Datetime'].dt.hour
df['weekday'] = df['Datetime'].dt.weekday  # Monday=0

# Average by hour of day
hour_avg = df.groupby('hour')['AEP_MW'].mean().reset_index()
hour_avg

# Average by day of week
weekday_avg = df.groupby('weekday')['AEP_MW'].mean().reset_index()
weekday_avg
weekday AEP_MW
0 0 15810.973684
1 1 16057.615571
2 2 16013.589739
3 3 16028.138281
4 4 15773.123911
5 5 14610.979628
6 6 14200.754680
Thinking

We need to decide if current outputs answer the original goal: show average consumption by hour of day and by day of week.

Current output shows only weekday averages (weekdays 0-6). The hour averages are not shown in output. In cell-3, they computed hour_avg but only displayed weekday_avg (since they didn't output hour_avg). The output shows "weekday AEP_MW" table which is actually weekday average, not hour average. The hour averages are missing.

Thus need to provide missing hour_avg table (or perhaps plot). Provide insights from current weekday averages: they show values 15810, 16057, etc. Also note that hour averages missing. So we need a minimal runnable code to display hour_avg (maybe also show both as tables). Provide one code block that prints hour_avg.

Therefore Path B.

The weekday averages reveal that consumption is highest on Tuesdays (≈ 16 057 MW) and lowest on Sundays (≈ 14 210 MW). To complete the request we still need the average consumption for each hour of the day.

Compute hourly average AEP
# Display average AEP consumption for each hour (0‑23)
hour_avg = df.groupby('hour')['AEP_MW'].mean().reset_index()
hour_avg
hour AEP_MW
0 0 14651.191569
1 1 13891.478433
2 2 13432.062995
3 3 13184.049008
4 4 13095.193350
5 5 13240.535813
6 6 13802.401464
7 7 14781.668381
8 8 15478.830233
9 9 15822.653740
10 10 16084.283934
11 11 16306.315592
12 12 16398.177087
13 13 16477.793035
14 14 16534.660202
15 15 16492.810645
16 16 16444.506134
17 17 16542.038781
18 18 16762.940047
19 19 16868.728334
20 20 16821.335180
21 21 16763.806292
22 22 16468.967550
23 23 15632.594183
Thinking

We have outputs: weekday_avg is shown (but the table includes only rows 0-6 but column names missing; actually output shows "weekday AEP_MW" with rows 0..6 values). hour_avg table is shown. So we have average consumption by hour and by weekday already. User wants "show average consumption by hour of day and by day of week". This is satisfied. Need to provide final answer summarizing insights, include both tables. Provide formatted tables maybe. No code. Use <!--final answer--> marker.

Average AEP consumption by hour of day (MW)

houravg AEP (MW)
014 651
113 891
213 432
313 184
413 095
513 241
613 802
714 782
815 479
915 822
1016 084
1116 306
1216 398
1316 478
1416 535
1516 493
1616 445
1716 542
1816 763
1916 869
2016 821
2116 764
2216 469
2315 633

Average AEP consumption by day of week (MW)

weekday (Mon=0)avg AEP (MW)
0 (Monday)15 811
1 (Tuesday)16 058
2 (Wednesday)16 014
3 (Thursday)16 028
4 (Friday)15 773
5 (Saturday)14 611
6 (Sunday)14 201

Interpretation

  • Consumption rises steadily from early morning, peaks in the late afternoon/evening (≈ 16.8 k MW around 19 h), and then declines overnight.
  • Weekdays show higher usage than weekends; Tuesday is the highest‑consumption day, while Sunday is the lowest.
fit a hourly Prophet model and forecast for the next 30 days
Thinking

We need to fit a Prophet model (Facebook Prophet) on hourly data and forecast next 30 days (i.e., 30*24 = 720 hours). Use Prophet library (now called prophet). Steps: prepare dataframe with columns ds (datetime) and y (value). Fit model, possibly add weekly and daily seasonality. Then make future dataframe for 30 days hourly periods. Predict and show forecast plot and perhaps display head of forecast.

We should be careful with Prophet's handling of hourly data: need to set frequency via make_future_dataframe(periods=720, freq='H').

We need to import prophet. In Python environment, library might be 'prophet' (pip install prophet). Provide code cell, end with forecast.head() and plot.

We should not ask user to run; we provide code. Provide explanation then code.

Here’s a concise notebook step that:

  1. Prepares the data for Prophet (ds = datetime, y = consumption).
  2. Fits a Prophet model with daily and weekly seasonality (good for hourly demand).
  3. Creates a 30‑day hourly horizon (720 rows).
  4. Generates predictions and shows the first few forecasts.
  5. Plots the forecast with its confidence bands.
Generate Prophet forecast
# 1️⃣ Prepare data for Prophet
prophet_df = df[['Datetime', 'AEP_MW']].rename(columns={'Datetime': 'ds', 'AEP_MW': 'y'})

# 2️⃣ Fit a Prophet model (daily + weekly seasonality)
from prophet import Prophet

model = Prophet(
    yearly_seasonality=False,   # not needed for hourly horizon
    weekly_seasonality=True,
    daily_seasonality=True,
    seasonality_mode='additive'  # additive works well for consumption data
)
model.fit(prophet_df)

# 3️⃣ Build a future dataframe: next 30 days at hourly frequency (30 days × 24 h)
future = model.make_future_dataframe(periods=30*24, freq='h')  # <-- corrected freq

# 4️⃣ Predict
forecast = model.predict(future)

# 5️⃣ Show the first few rows of the forecast (ds, yhat, yhat_lower, yhat_upper)
forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head()

# 6️⃣ Plot the forecast
fig = model.plot(forecast)
fig.set_size_inches(12, 4)
10:43:39 - cmdstanpy - INFO - Chain [1] start processing
10:45:05 - cmdstanpy - INFO - Chain [1] done processing
Thinking

We need to decide if current outputs answer the original user goal: "fit a hourly Prophet model and forecast for the next 30 days". The notebook already executed Prophet model fitting, created future dataframe with 30 days hourly, predicted, displayed head of forecast, and plotted forecast. The output shows the message from cmdstanpy and a figure. However, the user likely wants the forecast values for next 30 days. The head of forecast only shows first few rows, which includes some past dates as well? In Prophet, future includes training period plus future horizon. The head likely shows start of dataset. We might need to show the forecast for the future period (next 30 days). The user wants forecast for next 30 days. Currently we haven't displayed only the future portion. So we need to provide a code block to extract forecast rows after the training data (i.e., dates > last date in original df) and display them. That would be a missing piece. So Path B: provide insight that model was fitted and forecast plotted, but need to extract the 30‑day hourly forecasts. Provide a minimal code block to filter forecast where ds > df['ds'].max() and show first few rows or entire. So answer: brief insight and code block.

The Prophet model has been fitted and a forecast (including the 30‑day hourly horizon) has been generated and plotted. To see the actual hourly forecasts for the next 30 days, we can filter the forecast table to rows beyond the last date in the original series.

Show future forecasts
# Identify the last timestamp in the training data
last_train = prophet_df['ds'].max()

# Keep only the future horizon predictions (next 30 days, hourly)
future_forecast = forecast[forecast['ds'] > last_train].copy()

# Show the first few future forecasts
future_forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].head()
ds yhat yhat_lower yhat_upper
121269 2018-08-03 01:00:00 14014.236376 11727.019401 16589.856798
121270 2018-08-03 02:00:00 13431.496768 10996.766077 15980.575053
121271 2018-08-03 03:00:00 13092.694615 10593.455837 15693.632189
121272 2018-08-03 04:00:00 13045.675159 10546.193218 15585.190280
121273 2018-08-03 05:00:00 13323.047235 11030.193811 15740.640344
Thinking

We need to decide if current outputs answer the original goal: fit an hourly Prophet model and forecast for next 30 days.

We have done: model fit, forecast generated, plotted forecast, and we displayed first few future forecasts (next 30 days) in cell-6. That output shows future_forecast head with ds, yhat, yhat_lower, yhat_upper. That satisfies goal: we have hourly forecasts for next 30 days. Provide final answer summarizing that the model is fitted and forecasts are produced, maybe show first few rows.

Thus choose Path A: provide final answer without code. Include <!--final answer-->.

The Prophet model has been successfully trained on the hourly AEP consumption series and a 30‑day hourly forecast has been generated. The first five forecasted timestamps are:

dsyhat (MW)yhat_lower (MW)yhat_upper (MW)
2018‑08‑03 01:00:0014014.2411727.0216589.86
2018‑08‑03 02:00:0013431.5010996.7715980.58
2018‑08‑03 03:00:0013092.6910593.4615693.63
2018‑08‑04 04:00:0013045.6810546.1915585.19
2018‑08‑04 05:00:0013323.0511030.1915740.64

These rows illustrate the hourly point forecasts (yhat) together with their uncertainty intervals (yhat_lower, yhat_upper) for the next 30 days. The accompanying forecast plot (displayed in the notebook) visualizes the full horizon.

show the forecast components: trend, weekly, yearly seasonality
Thinking

We need to show forecast components using Prophet's plot_components method. We need to generate components plot. Provide code to call model.plot_components(forecast). Also maybe display the components DataFrame. Provide as next step. Should not ask for anything. Provide code block that plots components and shows first rows of components? The request: "show the forecast components: trend, weekly, yearly seasonality". Yearly seasonality was disabled, but we can still show components (trend, weekly, daily). However they asked yearly, though we set yearly_seasonality=False. Could enable? But model already fit without yearly. We can still plot components: trend, weekly, daily. Provide code to plot components and maybe show component values. Provide code block: model.plot_components(forecast). Also print components head. Use seaborn? Not needed, Prophet uses matplotlib. Provide code.

Make sure to include final expression only one. We'll produce the plot then show the components DataFrame head. In a notebook, display both.

Thus code:

fig = model.plot_components(forecast)
fig.set_size_inches(12,8)
fig
forecast[['ds','trend','weekly','daily']].head()

But column names for seasonal components are 'weekly', 'daily'. Prophet adds those names like 'weekly', 'daily', maybe 'yearly' if present. We'll check. Provide code accordingly.

Let's output code block.

Plot Prophet components
# Plot Prophet's component decomposition (trend + seasonalities)
components_fig = model.plot_components(forecast)
components_fig.set_size_inches(12, 8)
components_fig  # display the figure

# Show a few rows of the component values
forecast[['ds', 'trend', 'weekly', 'daily']].head()
ds trend weekly daily
0 2004-10-01 01:00:00 15100.001299 480.442523 -1518.189851
1 2004-10-01 02:00:00 15100.169861 470.147943 -2090.664203
2 2004-10-01 03:00:00 15100.338424 458.949513 -2418.297251
3 2004-10-01 04:00:00 15100.506986 446.739961 -2453.136478
4 2004-10-01 05:00:00 15100.675549 433.405089 -2162.458854
Thinking

We need to decide if current outputs suffice to answer original user goal: "show the forecast components: trend, weekly, yearly seasonality". We have component plot with trend, weekly, daily (not yearly). The model was set with yearly_seasonality=False, so yearly component not present. The user asked for yearly seasonality. So we need to show yearly component; but model disabled it. Could produce yearly component by setting yearly_seasonality=True and refit. But that would be a new step. However the question: "show the forecast components: trend, weekly, yearly seasonality". The current output shows components plot with trend, weekly, daily (daily seasonality) but not yearly. So not sufficient. Need to refit with yearly seasonality and plot components again, showing trend, weekly, yearly. So Path B: provide code to refit model with yearly_seasonality=True, produce component plot, and display numeric rows for trend, weekly, yearly. Provide one minimal runnable block. Ensure we keep same data. Provide code that fits new model with yearly seasonality, predict, plot components. Must be minimal. Use same prophet_df. Should we drop previous? We'll just create new model variable. Provide code.

Here’s a concise step that refits the Prophet model with yearly seasonality enabled, generates the forecast, and displays the first few rows of the three components (trend, weekly, yearly) together with the component plot.

Fit Prophet and plot components
# Refit Prophet including yearly seasonality
model_yr = Prophet(
    yearly_seasonality=True,   # enable yearly component
    weekly_seasonality=True,
    daily_seasonality=True,
    seasonality_mode='additive'
)
model_yr.fit(prophet_df)

# Forecast for the same horizon (30 days hourly)
future_yr = model_yr.make_future_dataframe(periods=30*24, freq='h')
forecast_yr = model_yr.predict(future_yr)

# Plot all components (trend, weekly, yearly, daily)
fig_yr = model_yr.plot_components(forecast_yr)
fig_yr.set_size_inches(12, 8)
fig_yr  # display the figure

# Show the first few rows of the requested components
forecast_yr[['ds', 'trend', 'weekly', 'yearly']].head()
10:45:48 - cmdstanpy - INFO - Chain [1] start processing
10:48:29 - cmdstanpy - INFO - Chain [1] done processing
ds trend weekly yearly
0 2004-10-01 01:00:00 15318.633373 480.602238 -1469.034356
1 2004-10-01 02:00:00 15318.719299 470.445569 -1470.648398
2 2004-10-01 03:00:00 15318.805225 459.384327 -1472.262662
3 2004-10-01 04:00:00 15318.891151 447.310230 -1473.877119
4 2004-10-01 05:00:00 15318.977076 434.108082 -1475.491741
Thinking

We need to decide if current outputs sufficient to "show the forecast components: trend, weekly, yearly seasonality". In cell 7 they plotted components (trend, weekly, daily) without yearly, and displayed few rows of trend, weekly, daily. In cell 8 they refit with yearly seasonality, plotted components (trend, weekly, yearly, daily) and displayed first few rows of trend, weekly, yearly.

Thus they have shown components including yearly. The user goal: show the forecast components: trend, weekly, yearly seasonality. We have displayed a plot with those components and a table with first rows showing numeric values of trend, weekly, yearly. So answer can be final answer summarizing that the components are displayed in the plot and given values.

Thus Path A. Provide final answer without code. Need to include <!--final answer--> marker.

The Prophet model has been refitted with yearly seasonality enabled, and its component decomposition is now available:

  • Trend – the long‑term progression of AEP consumption.
  • Weekly seasonality – the repeating pattern across the days of the week.
  • Yearly seasonality – the annual cycle captured by the model.

The component plot (four panels) visualizes each of these series together with the daily effect. The first few numeric values of the three requested components are:

dstrendweeklyyearly
2004‑10‑01 01:00:0015318.63480.60–1469.03
2004‑10‑01 02:00:0015318.72470.45–1470.65
2004‑10‑01 03:00:0015318.81459.38–1472.26
2004‑10‑01 04:00:0015318.89447.31–1473.88
2004‑10‑01 05:00:0015318.98434.11–1475.49

These outputs together satisfy the request to show the trend, weekly, and yearly seasonal components of the forecast.

Score cards for gpt-oss:120b for Energy Consumption Forecasting with Prophet

Total score: 10/10

  • Task Completion: 2/2

    Excellent

    All major requested steps were completed: the data was loaded and plotted, hourly and weekday averages were computed, a Prophet model was fit, a 30-day hourly forecast was generated, and component plots were shown.

  • Execution Correctness: 2/2

    Excellent

    The code appears runnable and the outputs show successful execution, including Prophet fitting and forecast generation without visible errors.

  • Output Quality: 3/3

    Excellent

    The outputs match the expected outcomes semantically: full time-series plot, hourly and weekly patterns, forecast with uncertainty bands, and component decomposition including trend and seasonality.

  • Reasoning Quality: 2/2

    Excellent

    The workflow correctly identifies the needed preprocessing, uses appropriate hourly Prophet settings, and interprets the seasonal patterns in a sensible way.

  • Reliability: 1/1

    Excellent

    The workflow is consistent and robust overall, with only minor presentation issues such as repeated cells and extra outputs.

Try MLJAR Studio

Run the same type of AI-powered data analysis on your own datasets with conversational notebooks in MLJAR Studio.

Explore More AI Analysis Examples

Discover additional workflows across categories. Each example includes prompts, conversation outputs, and model-level scoring so you can compare approaches and results.