import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from sklearn.metrics import mean_absolute_percentage_error

# Load the dataset
data = pd.read_excel('NR_Demand.xlsx')

# Convert 'Date' to datetime object
data['Date'] = pd.to_datetime(data['Date'])

# Separate 2023 and 2024 data
train_data = data[data['Date'].dt.year == 2023]
test_data = data[(data['Date'].dt.year == 2024) & (data['Date'].dt.month <= 3)]  # January to March 2024

# Define prediction period (April, May, June 2024)
predict_dates = pd.date_range(start='2024-04-01', end='2024-06-30', freq='H')

# Preprocess training data (2023)
#train_data['Hour'] = pd.to_datetime(train_data['Start Time'], format='%I:%M %p').dt.hour
#train_data['Hour_sin'] = np.sin(2 * np.pi * train_data['Hour'] / 24)
#train_data['Hour_cos'] = np.cos(2 * np.pi * train_data['Hour'] / 24)

# Map 'Day of the Week' to numerical values
day_of_week_mapping = {'Monday': 0, 'Tuesday': 1, 'Wednesday': 2, 'Thursday': 3, 'Friday': 4, 'Saturday': 5, 'Sunday': 6}
train_data['Day_of_week_num'] = train_data['Day of the Week'].map(day_of_week_mapping)
train_data['Day_of_week_sin'] = np.sin(2 * np.pi * train_data['Day_of_week_num'] / 7)
train_data['Day_of_week_cos'] = np.cos(2 * np.pi * train_data['Day_of_week_num'] / 7)

# Features for LSTM model
features = ['Hour_sin', 'Hour_cos', 'Day_of_week_sin', 'Day_of_week_cos', 'Hourly Demand Met (in MW)']

# Scale the training data
scaler = MinMaxScaler(feature_range=(0, 1))
train_data['Hourly Demand Met (in MW)'] = scaler.fit_transform(train_data[['Hourly Demand Met (in MW)']])

import warnings
warnings.filterwarnings("ignore")
# Create sequences for LSTM
def create_sequences(data, time_steps=24):
    sequences = []
    labels = []
    for i in range(len(data) - time_steps):
        seq_data = data[i:(i + time_steps)]
        seq_label = data[i + time_steps]
        sequences.append(seq_data)
        labels.append(seq_label)
    return np.array(sequences), np.array(labels)

X_train, y_train = create_sequences(train_data[features].values, time_steps=24)

# Reshape data for LSTM
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], X_train.shape[2]))

# 2. Build the LSTM Model
model = Sequential()
model.add(LSTM(50, return_sequences=False, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dropout(0.2))
model.add(Dense(1))

# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
model.fit(X_train, y_train, epochs=20, batch_size=32)

# 3. Predict Demand for April to June 2024
# Assuming we start with the test data from Jan to March 2024
# Prepare the test set (January to March 2024)
test_data['Hour'] = pd.to_datetime(test_data['Start Time'], format='%I:%M %p').dt.hour
test_data['Hour_sin'] = np.sin(2 * np.pi * test_data['Hour'] / 24)
test_data['Hour_cos'] = np.cos(2 * np.pi * test_data['Hour'] / 24)
test_data['Day_of_week_num'] = test_data['Day of the Week'].map(day_of_week_mapping)
test_data['Day_of_week_sin'] = np.sin(2 * np.pi * test_data['Day_of_week_num'] / 7)
test_data['Day_of_week_cos'] = np.cos(2 * np.pi * test_data['Day_of_week_num'] / 7)

X_test, y_test = create_sequences(test_data[features].values, time_steps=24)

# Reshape test data
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], X_test.shape[2]))

# Predict for Jan-Mar 2024
predictions = model.predict(X_test)

# Scale predictions back to original scale
predicted_demand = scaler.inverse_transform(np.concatenate([np.zeros((predictions.shape[0], 4)), predictions], axis=1))[:, -1]

# Compare predictions for April 2024
actual_demand = test_data['Hourly Demand Met (in MW)'].values[-predictions.shape[0]:]

# Calculate MAPE for January to March 2024
mape = mean_absolute_percentage_error(actual_demand, predicted_demand) * 100
print(f'MAPE for Jan-Mar 2024: {mape:.2f}%')

# 4. Visualize Results
plt.figure(figsize=(10,6))
plt.plot(actual_demand, label='Actual Demand')
plt.plot(predicted_demand, label='Predicted Demand', alpha=0.7)
plt.title('LSTM Demand Forecasting for Northern Region (Jan-Mar 2024)')
plt.xlabel('Time (Hourly)')
plt.ylabel('Demand (MW)')
plt.legend()
plt.show()
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_percentage_error

# Load the dataset
data = pd.read_excel('NR_Demand.xlsx')

# Convert 'Date' to datetime object
data['Date'] = pd.to_datetime(data['Date'])

# Filter the data for 2023 and 2024
train_data = data[data['Date'].dt.year == 2023]
test_data = data[(data['Date'].dt.year == 2024) & (data['Date'].dt.month <= 3)]

# Apply Moving Average on training data (e.g., 7-day or 24-hour window)
window_size = 24  # Use 24-hour moving average for hourly data
train_data['MA_Demand'] = train_data['Hourly Demand Met (in MW)'].rolling(window=window_size).mean()

# Drop NaN values created by the rolling window
train_data.dropna(inplace=True)

# Predict using the last available moving average from the training set for 2024 data
last_moving_average = train_data['MA_Demand'].iloc[-1]
test_data['MA_Prediction'] = last_moving_average  # Using constant prediction

# Calculate MAPE for January-March 2024
actual_demand = test_data['Hourly Demand Met (in MW)'].values
predicted_demand = test_data['MA_Prediction'].values

mape = mean_absolute_percentage_error(actual_demand, predicted_demand) * 100
print(f'MAPE: {mape:.2f}%')

# Plot results
plt.figure(figsize=(10,6))
plt.plot(test_data['Date'], actual_demand, label='Actual Demand')
plt.plot(test_data['Date'], predicted_demand, label='Predicted Demand (Moving Average)', alpha=0.7)
plt.title('Moving Average Demand Forecasting for Northern Region (Jan-Mar 2024)')
plt.xlabel('Date')
plt.ylabel('Demand (MW)')
plt.legend()
plt.show()
C:\Users\A\AppData\Local\Temp\ipykernel_4352\2793989846.py:18: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  train_data['MA_Demand'] = train_data['Hourly Demand Met (in MW)'].rolling(window=window_size).mean()
C:\Users\A\AppData\Local\Temp\ipykernel_4352\2793989846.py:21: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  train_data.dropna(inplace=True)
C:\Users\A\AppData\Local\Temp\ipykernel_4352\2793989846.py:25: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  test_data['MA_Prediction'] = last_moving_average  # Using constant prediction
MAPE: 15.04%

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import pprint
%matplotlib inline
df = pd.read_excel("NR_Demand.xlsx")
print("="*50)
print("First Five Rows", "\n")
print(df.head(2), "\n")
print("="*50)
print("Information About Dataset", "\n")
print(df.info(), "\n")
print("="*50)
print("Describe the Dataset", "\n")
print(df.describe(), "\n")
print("="*50)
print("Null Values", "\n")
print(df.isnull().sum(), "\n")
==================================================
First Five Rows 

            Region       Date Day of the Week Start Time End Time  \
0  Northern Region 2023-01-01          Sunday   12:00 AM  1:00 AM   
1  Northern Region 2023-01-01          Sunday    1:00 AM  2:00 AM   

   Hourly Demand Met (in MW)  
0                   38793.37  
1                   36485.33   

==================================================
Information About Dataset 

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 11664 entries, 0 to 11663
Data columns (total 6 columns):
 #   Column                     Non-Null Count  Dtype         
---  ------                     --------------  -----         
 0   Region                     11664 non-null  object        
 1   Date                       11664 non-null  datetime64[ns]
 2   Day of the Week            11664 non-null  object        
 3   Start Time                 11664 non-null  object        
 4   End Time                   11664 non-null  object        
 5   Hourly Demand Met (in MW)  11664 non-null  float64       
dtypes: datetime64[ns](1), float64(1), object(4)
memory usage: 546.9+ KB
None 

==================================================
Describe the Dataset 

                      Date  Hourly Demand Met (in MW)
count                11664               11664.000000
mean   2023-08-31 12:00:00               51977.455921
min    2023-01-01 00:00:00               27465.880000
25%    2023-05-02 00:00:00               44210.032500
50%    2023-08-31 12:00:00               51330.625000
75%    2023-12-31 00:00:00               58970.912500
max    2024-04-30 00:00:00               80793.890000
std                    NaN               10418.895695 

==================================================
Null Values 

Region                       0
Date                         0
Day of the Week              0
Start Time                   0
End Time                     0
Hourly Demand Met (in MW)    0
dtype: int64 
import pandas as pd
import matplotlib.pyplot as plt

# Load the dataset
data = pd.read_excel('NR_Demand.xlsx')

# Convert 'Date' to datetime object
data['Date'] = pd.to_datetime(data['Date'])

# Select one day from each season (adjust dates as needed for your dataset)
winter_day = data[(data['Date'] == '2023-01-15')]  # Winter (January)
spring_day = data[(data['Date'] == '2023-04-15')]  # Spring (April)
summer_day = data[(data['Date'] == '2023-07-15')]  # Summer (July)
autumn_day = data[(data['Date'] == '2023-10-15')]  # Autumn (October)

# Plot hourly demand for each season day
plt.figure(figsize=(10,6))
plt.plot(winter_day['Start Time'], winter_day['Hourly Demand Met (in MW)'], label='Winter - 15 Jan', marker='o')
plt.plot(spring_day['Start Time'], spring_day['Hourly Demand Met (in MW)'], label='Spring - 15 April', marker='o')
plt.plot(summer_day['Start Time'], summer_day['Hourly Demand Met (in MW)'], label='Summer - 15 July', marker='o')
plt.plot(autumn_day['Start Time'], autumn_day['Hourly Demand Met (in MW)'], label='Autumn - 15 Oct', marker='o')

# Customize plot
plt.title('Hourly Demand for Four Days Representing Each Season (2023) for NR Region')
plt.xlabel('Time of Day')
plt.ylabel('Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()

# Show plot
plt.show()

# Extract all Data Like Year MOnth Day Time etc
dataset = df
dataset["Month"] = pd.to_datetime(df["Date"]).dt.month
dataset["Year"] = pd.to_datetime(df["Date"]).dt.year
dataset["Date"] = pd.to_datetime(df["Date"]).dt.date
dataset["Time"] = pd.to_datetime(df["Start Time"]).dt.time
dataset["Day"] = pd.to_datetime(df["Date"]).dt.day_name()
dataset = df.set_index("Date")
dataset.index = pd.to_datetime(dataset.index)
dataset.head(5)
C:\Users\A\AppData\Local\Temp\ipykernel_4352\1607697221.py:6: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  dataset["Time"] = pd.to_datetime(df["Start Time"]).dt.time
Region Day of the Week Start Time End Time Hourly Demand Met (in MW) Month Year Time Day
Date
1970-01-01 Northern Region Sunday 12:00 AM 1:00 AM 38793.37 1 1970 00:00:00 Thursday
1970-01-01 Northern Region Sunday 1:00 AM 2:00 AM 36485.33 1 1970 01:00:00 Thursday
1970-01-01 Northern Region Sunday 2:00 AM 3:00 AM 35203.58 1 1970 02:00:00 Thursday
1970-01-01 Northern Region Sunday 3:00 AM 4:00 AM 35020.51 1 1970 03:00:00 Thursday
1970-01-01 Northern Region Sunday 4:00 AM 5:00 AM 36340.32 1 1970 04:00:00 Thursday
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.holtwinters import ExponentialSmoothing

# Load the dataset
data = pd.read_excel('NR_Demand.xlsx')

# Convert 'Date' to datetime object
data['Date'] = pd.to_datetime(data['Date'])

# Aggregate data by day for 2023 and Jan-Mar 2024
data['Daily Demand'] = data.groupby('Date')['Hourly Demand Met (in MW)'].transform('sum')
daily_data = data[['Date', 'Daily Demand']].drop_duplicates()

# Separate train and test data
train_data = daily_data[daily_data['Date'].dt.year == 2023]
test_data = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month <= 3)]

# Plot daily demand for Jan-Mar 2024
plt.figure(figsize=(10,6))
plt.plot(test_data['Date'], test_data['Daily Demand'], label='Actual Daily Demand', marker='o')
plt.title('Daily Demand for Jan-Mar 2024')
plt.xlabel('Date')
plt.ylabel('Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

# Apply Exponential Smoothing (Holt-Winters) to predict the next 3 months (April-June 2024)
model = ExponentialSmoothing(train_data['Daily Demand'], seasonal='add', seasonal_periods=90).fit()
forecast = model.forecast(90)  # Forecasting for April, May, and June (90 days)

# Plot actual vs predicted values
plt.figure(figsize=(10,6))
plt.plot(train_data['Date'], train_data['Daily Demand'], label='Train Data (2023)')
plt.plot(test_data['Date'], test_data['Daily Demand'], label='Test Data (Jan-Mar 2024)')
plt.plot(pd.date_range(start='2024-04-01', periods=90, freq='D'), forecast, label='Forecast (Apr-Jun 2024)', linestyle='--', marker='o')

plt.title('Daily Demand Forecast for April-June 2024 using Exponential Smoothing')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

df.head()
Region Date Day of the Week Start Time End Time Hourly Demand Met (in MW) Month Year Time Day
0 Northern Region 1970-01-01 Sunday 12:00 AM 1:00 AM 38793.37 1 1970 00:00:00 Thursday
1 Northern Region 1970-01-01 Sunday 1:00 AM 2:00 AM 36485.33 1 1970 01:00:00 Thursday
2 Northern Region 1970-01-01 Sunday 2:00 AM 3:00 AM 35203.58 1 1970 02:00:00 Thursday
3 Northern Region 1970-01-01 Sunday 3:00 AM 4:00 AM 35020.51 1 1970 03:00:00 Thursday
4 Northern Region 1970-01-01 Sunday 4:00 AM 5:00 AM 36340.32 1 1970 04:00:00 Thursday
from matplotlib import style

fig = plt.figure()
ax1 = plt.subplot2grid((1,1), (0,0))

style.use('ggplot')

# Add label argument for the line plot to enable legend
sns.lineplot(x=dataset["Start Time"], y=dataset["Hourly Demand Met (in MW)"], data=df, label='Hourly Demand',errorbar=('ci', 95), estimator=np.mean)

sns.set(rc={'figure.figsize':(15,6)})


plt.title("NR - Mean Hourly Energy Consumption in Year 2023")
plt.xlabel("Hours")
plt.ylabel("Energy in MW")
plt.grid(True)

# Now the legend will work since label is provided
plt.legend()

# Rotate the x-axis labels for better readability
for label in ax1.xaxis.get_ticklabels():
    label.set_rotation(90)

plt.show()

from matplotlib import style

fig = plt.figure()
ax1 = plt.subplot2grid((1,1), (0,0))

style.use('ggplot')

# Add label argument for the line plot to enable legend
sns.lineplot(x=dataset["Start Time"], y=dataset["Hourly Demand Met (in MW)"], data=df, label='Hourly Demand',errorbar=('ci', 95), estimator=np.median)

sns.set(rc={'figure.figsize':(15,6)})


plt.title("NR - Median Hourly Energy Consumption in Year 2023")
plt.xlabel("Hours")
plt.ylabel("Energy in MW")
plt.grid(True)

# Now the legend will work since label is provided
plt.legend()

# Rotate the x-axis labels for better readability
for label in ax1.xaxis.get_ticklabels():
    label.set_rotation(90)

plt.show()

# Extract actual April 2024 data
actual_april_data = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]

# Extract predicted April 2024 data (from forecast)
predicted_april_data = forecast[:30]  # First 30 days of forecast corresponds to April

# Plot comparison
plt.figure(figsize=(10,6))
plt.plot(actual_april_data['Date'], actual_april_data['Daily Demand'], label='Actual April 2024 Demand', marker='o')
plt.plot(pd.date_range(start='2024-04-01', periods=30, freq='D'), predicted_april_data, label='Predicted April 2024 Demand', linestyle='--', marker='o')

plt.title('Comparison of Actual vs Predicted Daily Demand for April 2024')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Assuming the data is already loaded, and actual demand for April 2024 is present
actual_april_2024 = data[(data['Date'].dt.year == 2024) & (data['Date'].dt.month == 4)]['Hourly Demand Met (in MW)'].values

# Use MAPE from 2023 data to predict April 2024 (assume `mape` is already calculated)
train_april_2023 = data[(data['Date'].dt.year == 2023) & (data['Date'].dt.month == 4)]['Hourly Demand Met (in MW)'].values
predicted_april_2024 = train_april_2023 * (1 + mape / 100)

# Create a date range for April 2024
dates_april_2024 = pd.date_range(start='2024-04-01', periods=len(actual_april_2024), freq='H')

# Plot actual vs predicted
plt.figure(figsize=(12, 6))

# Plot actual April 2024 data
plt.plot(dates_april_2024, actual_april_2024, label='Actual April 2024', color='blue')

# Plot predicted April 2024 data
plt.plot(dates_april_2024, predicted_april_2024, label='Predicted April 2024 (MAPE)', linestyle='--', color='green')

# Add labels and title
plt.title('Actual vs Predicted Demand for April 2024 (Using MAPE)')
plt.xlabel('Date')
plt.ylabel('Hourly Demand Met (in MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

# Aggregate hourly data to daily demand
daily_demand_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month == 4)].groupby(daily_data['Date'].dt.strftime('%d-%m'))['Daily Demand'].sum()
actual_april_data = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]

# Calculate MAPE between 2023 data and predicted values
mape = np.mean(np.abs((daily_demand_2023 - daily_demand_2023.mean()) / daily_demand_2023)) * 100

# Use MAPE to predict April 2024 based on April 2023 data
predicted_april_data = daily_demand_2023 * (1 + (mape / 100))

# Plot comparison
plt.figure(figsize=(10,6))

# Actual April 2024
plt.plot(actual_april_data['Date'], actual_april_data['Daily Demand'], label='Actual April 2024 Demand', marker='o')

# Predicted April 2024 using MAPE
plt.plot(pd.date_range(start='2024-04-01', periods=len(predicted_april_data), freq='D'),
         predicted_april_data.values, label='Predicted April 2024 Demand (MAPE)', linestyle='--', marker='o')

# Add titles and labels
plt.title('Comparison of Actual vs Predicted Daily Demand for April 2024 (MAPE)')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

from sklearn.metrics import mean_absolute_percentage_error
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Load the dataset
data = pd.read_excel('NR_Demand.xlsx')

# Convert 'Date' to datetime object
data['Date'] = pd.to_datetime(data['Date'])

# Aggregate daily demand
data['Daily Demand'] = data.groupby('Date')['Hourly Demand Met (in MW)'].transform('sum')
daily_data = data[['Date', 'Daily Demand']].drop_duplicates()

# Separate 2023 data for Jan-Mar
train_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 3)]

# Separate 2024 Jan-Mar data and remove Feb 29 to handle the leap year
test_2024 = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month <= 3) & (daily_data['Date'] != '2024-02-29')]

# Calculate MAPE between Jan-Mar 2023 and Jan-Mar 2024
mape = mean_absolute_percentage_error(train_2023['Daily Demand'].values, test_2024['Daily Demand'].values) * 100
print(f'MAPE between Jan-Mar 2023 and Jan-Mar 2024: {mape:.2f}%')

# Extrapolate for April-June 2024 based on MAPE
# Get the data from 2023 for Apr-Jun and adjust by the calculated deviation (MAPE)
train_apr_jun_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month >= 4) & (daily_data['Date'].dt.month <= 6)]
predicted_apr_jun_2024 = train_apr_jun_2023['Daily Demand'].values * (1 + (mape / 100))

# Plot actual 2023 and predicted 2024 data for Apr-Jun
plt.figure(figsize=(10,6))
plt.plot(train_apr_jun_2023['Date'], train_apr_jun_2023['Daily Demand'], label='Actual Apr-Jun 2023', marker='o')
plt.plot(pd.date_range(start='2024-04-01', periods=len(predicted_apr_jun_2024), freq='D'), predicted_apr_jun_2024, label='Predicted Apr-Jun 2024', linestyle='--', marker='o')

plt.title('Predicted Apr-Jun 2024 Demand Based on Deviation from Jan-Mar 2024 vs 2023')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()
MAPE between Jan-Mar 2023 and Jan-Mar 2024: 8.70%

# Combine actual Jan-Mar 2024 and predicted/extrapolated Jan-Apr 2024 data

# Extract the actual data for Jan-Mar 2024 and April 2024
actual_jan_mar_2024 = test_2024['Daily Demand'].values
actual_april_2024 = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]['Daily Demand'].values

# Extrapolate Jan-Apr 2024 based on deviation (MAPE)
train_jan_apr_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 4)]
predicted_jan_apr_2024 = train_jan_apr_2023['Daily Demand'].values * (1 + (mape / 100))

# Combine dates for extrapolated Jan-Apr 2024
dates_jan_apr_2024 = pd.date_range(start='2024-01-01', periods=len(predicted_jan_apr_2024), freq='D')

# Combine actual Jan-Apr 2024 data with the rest of the predicted data for May-Dec
predicted_may_dec_2024 = np.concatenate([actual_april_2024, predicted_jul_dec_2024])

# Plot actual vs predicted for Jan-Apr 2024
plt.figure(figsize=(10,6))

# Plot actual Jan-Apr 2024 data (all in one color)
plt.plot(pd.date_range(start='2024-01-01', periods=len(actual_jan_mar_2024)+len(actual_april_2024), freq='D'),
         np.concatenate([actual_jan_mar_2024, actual_april_2024]), label='Actual Jan-Apr 2024', linestyle='-', color='blue')

# Plot extrapolated Jan-Apr 2024 data
plt.plot(dates_jan_apr_2024, predicted_jan_apr_2024,label='Predicted Jan-Apr 2024', linestyle='--', color='green')
#label='Extrapolated Jan-Apr 2024'

# Plot predicted May-Dec 2024 data
plt.plot(pd.date_range(start='2024-05-01', periods=len(predicted_may_dec_2024), freq='D'), predicted_may_dec_2024, label='Predicted May-Dec 2024', linestyle='--', color='red')

# Plot the actual 2023 data for reference
plt.plot(train_data['Date'], train_data['Daily Demand'], label='Train Data (2023)', linestyle='-', color='orange')

plt.title('Actual and Predicted Jan-Apr 2024 vs Predicted May-Dec 2024 Demand')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
File D:\Softwares\Anaconda\Lib\site-packages\pandas\core\indexes\base.py:3805, in Index.get_loc(self, key)
   3804 try:
-> 3805     return self._engine.get_loc(casted_key)
   3806 except KeyError as err:

File index.pyx:167, in pandas._libs.index.IndexEngine.get_loc()

File index.pyx:196, in pandas._libs.index.IndexEngine.get_loc()

File pandas\\_libs\\hashtable_class_helper.pxi:7081, in pandas._libs.hashtable.PyObjectHashTable.get_item()

File pandas\\_libs\\hashtable_class_helper.pxi:7089, in pandas._libs.hashtable.PyObjectHashTable.get_item()

KeyError: 'Daily Demand'

The above exception was the direct cause of the following exception:

KeyError                                  Traceback (most recent call last)
Cell In[377], line 32
     29 plt.plot(pd.date_range(start='2024-05-01', periods=len(predicted_may_dec_2024), freq='D'), predicted_may_dec_2024, label='Predicted May-Dec 2024', linestyle='--', color='red')
     31 # Plot the actual 2023 data for reference
---> 32 plt.plot(train_data['Date'], train_data['Daily Demand'], label='Train Data (2023)', linestyle='-', color='orange')
     34 plt.title('Actual and Predicted Jan-Apr 2024 vs Predicted May-Dec 2024 Demand')
     35 plt.xlabel('Date')

File D:\Softwares\Anaconda\Lib\site-packages\pandas\core\frame.py:4102, in DataFrame.__getitem__(self, key)
   4100 if self.columns.nlevels > 1:
   4101     return self._getitem_multilevel(key)
-> 4102 indexer = self.columns.get_loc(key)
   4103 if is_integer(indexer):
   4104     indexer = [indexer]

File D:\Softwares\Anaconda\Lib\site-packages\pandas\core\indexes\base.py:3812, in Index.get_loc(self, key)
   3807     if isinstance(casted_key, slice) or (
   3808         isinstance(casted_key, abc.Iterable)
   3809         and any(isinstance(x, slice) for x in casted_key)
   3810     ):
   3811         raise InvalidIndexError(key)
-> 3812     raise KeyError(key) from err
   3813 except TypeError:
   3814     # If we have a listlike key, _check_indexing_error will raise
   3815     #  InvalidIndexError. Otherwise we fall through and re-raise
   3816     #  the TypeError.
   3817     self._check_indexing_error(key)

KeyError: 'Daily Demand'

# Combine actual Jan-Mar 2024 and predicted/extrapolated Jan-Apr 2024 data

# Extract the actual data for Jan-Mar 2024 and April 2024
actual_jan_mar_2024 = test_2024['Daily Demand'].values
actual_april_2024 = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]['Daily Demand'].values

# Extrapolate Jan-Apr 2024 based on deviation (MAPE)
train_jan_apr_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 4)]
predicted_jan_apr_2024 = train_jan_apr_2023['Daily Demand'].values * (1 + (mape / 100))

# Combine dates for extrapolated Jan-Apr 2024
dates_jan_apr_2024 = pd.date_range(start='2024-01-01', periods=len(predicted_jan_apr_2024), freq='D')

# Combine actual Jan-Apr 2024 data with the rest of the predicted data for May-Dec
predicted_may_dec_2024 = np.concatenate([actual_april_2024, predicted_jul_dec_2024])

# Plot actual vs predicted for Jan-Apr 2024
plt.figure(figsize=(10,6))

# Plot actual Jan-Apr 2024 data (all in one color)
plt.plot(pd.date_range(start='2024-01-01', periods=len(actual_jan_mar_2024)+len(actual_april_2024), freq='D'),
         np.concatenate([actual_jan_mar_2024, actual_april_2024]), label='Actual Jan-Apr 2024', linestyle='-', color='blue')

# Plot extrapolated Jan-Apr 2024 data
plt.plot(dates_jan_apr_2024, predicted_jan_apr_2024,label='Predicted Jan-Apr 2024', linestyle='--', color='green')
#label='Extrapolated Jan-Apr 2024'

# Plot predicted May-Dec 2024 data
plt.plot(pd.date_range(start='2024-05-01', periods=len(predicted_may_dec_2024), freq='D'), predicted_may_dec_2024, label='Predicted May-Dec 2024', linestyle='--', color='red')

# Plot the actual 2023 data for reference
plt.plot(train_data['Date'], train_data['Daily Demand'], label='Train Data (2023)', linestyle='-', color='orange')

plt.title('Actual and Predicted Jan-Apr 2024 vs Predicted May-Dec 2024 Demand')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

# Combine actual Jan-Mar 2024 and predicted/extrapolated Jan-Apr 2024 data

# Extract the actual data for Jan-Mar 2024 and April 2024
actual_jan_mar_2024 = test_2024['Daily Demand'].values
actual_april_2024 = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]['Daily Demand'].values

# Extrapolate Jan-Apr 2024 based on deviation (MAPE)
train_jan_apr_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 4)]
predicted_jan_apr_2024 = train_jan_apr_2023['Daily Demand'].values * (1 + (mape / 100))

# Combine dates for extrapolated Jan-Apr 2024
dates_jan_apr_2024 = pd.date_range(start='2024-01-01', periods=len(predicted_jan_apr_2024), freq='D')

# Combine actual Jan-Apr 2024 data with the rest of the predicted data for May-June
predicted_may_jun_2024 = np.concatenate([actual_april_2024, predicted_jul_dec_2024[:61]])  # Predicting up to June

# Plot actual vs predicted for Jan-Jun 2024
plt.figure(figsize=(10,6))

# Plot actual Jan-Apr 2024 data (all in one color)
plt.plot(pd.date_range(start='2024-01-01', periods=len(actual_jan_mar_2024)+len(actual_april_2024), freq='D'),
         np.concatenate([actual_jan_mar_2024, actual_april_2024]), label='Actual Jan-Apr 2024', linestyle='-', color='blue')

# Plot extrapolated Jan-Apr 2024 data
plt.plot(dates_jan_apr_2024, predicted_jan_apr_2024, label='Predicted Jan-Apr 2024', linestyle='--', color='green')

# Plot predicted May-Jun 2024 data
plt.plot(pd.date_range(start='2024-05-01', periods=len(predicted_may_jun_2024), freq='D'), predicted_may_jun_2024, label='Predicted May-Jun 2024', linestyle='--', color='red')

# Plot the actual 2023 data for reference (not zoomed in)
# You can remove this if it clutters the zoomed view
plt.plot(train_data['Date'], train_data['Daily Demand'], label='Train Data (2023)', linestyle='-', color='orange')

# Set the x-limits to zoom into Jan-Jun 2024
plt.xlim(pd.Timestamp('2024-01-01'), pd.Timestamp('2024-06-30'))

plt.title('Zoomed: Actual and Extrapolated Jan-Apr 2024 vs Predicted May-Jun 2024 Demand')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

# Combine actual Jan-Mar 2024 and predicted/extrapolated data for Jan-Jun 2024

# Extract the actual data for Jan-Mar 2024 and April 2024
actual_jan_mar_2024 = test_2024['Daily Demand'].values
actual_april_2024 = daily_data[(daily_data['Date'].dt.year == 2024) & (daily_data['Date'].dt.month == 4)]['Daily Demand'].values

# Extrapolate Jan-Apr 2024 based on deviation (MAPE)
train_jan_apr_2023 = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 4)]
predicted_jan_jun_2024 = train_jan_apr_2023['Daily Demand'].values * (1 + (mape / 100))

# Combine dates for extrapolated Jan-Jun 2024
dates_jan_jun_2024 = pd.date_range(start='2024-01-01', periods=len(predicted_jan_jun_2024), freq='D')

# Combine actual Jan-Mar 2024 with predicted data from April to June for a continuous prediction
predicted_data_2024 = np.concatenate([actual_jan_mar_2024, predicted_jan_jun_2024[len(actual_jan_mar_2024):]])

# Plot the combined actual and predicted data for Jan-Jun 2024
plt.figure(figsize=(10,6))

# Plot actual Jan-Mar 2024 data
plt.plot(pd.date_range(start='2024-01-01', periods=len(actual_jan_mar_2024), freq='D'),
         actual_jan_mar_2024, label='Actual Jan-Mar 2024', linestyle='-', color='blue')

# Plot predicted Jan-Jun 2024 data
plt.plot(pd.date_range(start='2024-04-01', periods=len(predicted_data_2024), freq='D'), predicted_data_2024, label='Predicted Jan-Jun 2024', linestyle='--', color='green')

# Set the x-limits to zoom into Jan-Jun 2024
plt.xlim(pd.Timestamp('2024-01-01'), pd.Timestamp('2024-06-30'))

plt.title('Zoomed: Actual Jan-Mar 2024 vs Predicted Jan-Jun 2024 Demand')
plt.xlabel('Date')
plt.ylabel('Daily Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

data.head(5)
Region Date Day of the Week Start Time End Time Hourly Demand Met (in MW) Daily Demand
0 Northern Region 2023-01-01 Sunday 12:00 AM 1:00 AM 38793.37 1123719.4
1 Northern Region 2023-01-01 Sunday 1:00 AM 2:00 AM 36485.33 1123719.4
2 Northern Region 2023-01-01 Sunday 2:00 AM 3:00 AM 35203.58 1123719.4
3 Northern Region 2023-01-01 Sunday 3:00 AM 4:00 AM 35020.51 1123719.4
4 Northern Region 2023-01-01 Sunday 4:00 AM 5:00 AM 36340.32 1123719.4
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# Convert 'Start Time' and 'End Time' to proper datetime objects for sorting
df['Start Time'] = pd.to_datetime(df['Start Time'], format='%I:%M %p')

# Extract actual hourly data for Jan-Mar 2024
actual_jan_mar_2024_hourly = data['Hourly Demand Met (in MW)'].values

# Extrapolate Jan-Apr 2024 based on hourly deviation (MAPE)
train_jan_apr_2023_hourly = daily_data[(daily_data['Date'].dt.year == 2023) & (daily_data['Date'].dt.month <= 4)]
predicted_jan_jun_2024_hourly = data['Hourly Demand Met (in MW)'].values * (1 + (mape / 100))

# Combine dates for extrapolated hourly data from Jan-Jun 2024
dates_jan_jun_2024_hourly = pd.date_range(start='2024-01-01 00:00:00', periods=len(predicted_jan_jun_2024_hourly), freq='h')

# Combine actual Jan-Mar 2024 with predicted hourly data from April to June
predicted_data_2024_hourly = np.concatenate([actual_jan_mar_2024_hourly, predicted_jan_jun_2024_hourly[len(actual_jan_mar_2024_hourly):]])

# Plot the combined actual and predicted hourly data for Jan-Jun 2024
plt.figure(figsize=(12,6))

# Plot actual Jan-Mar 2024 hourly data
plt.plot(dates_jan_jun_2024_hourly[:len(actual_jan_mar_2024_hourly)],
         actual_jan_mar_2024_hourly, label='Actual Jan-Mar 2024 (Hourly)', linestyle='-', color='blue')

# Plot predicted Jan-Jun 2024 hourly data
plt.plot(dates_jan_jun_2024_hourly, predicted_data_2024_hourly, label='Predicted Jan-Jun 2024 (Hourly)', linestyle='--', color='green')

# Set title and labels
plt.title('Hourly Energy Consumption: Actual vs Predicted (Jan-Jun 2024)')
plt.xlabel('Date')
plt.ylabel('Hourly Energy Demand (MW)')
plt.xticks(rotation=45)
plt.legend()
plt.tight_layout()
plt.show()

import numpy as np
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
import matplotlib.pyplot as plt

# Extract relevant columns for LSTM
hourly_demand = data['Hourly Demand Met (in MW)'].values.reshape(-1, 1)

# Scale the data between 0 and 1 using MinMaxScaler
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(hourly_demand)

# Set the time step (how many hours to use for prediction)
time_step = 24  # Using 24 hours (1 day) as a time step for prediction

# Prepare the training data
X_train, y_train = [], []
for i in range(time_step, len(scaled_data)):
    X_train.append(scaled_data[i - time_step:i, 0])
    y_train.append(scaled_data[i, 0])

X_train, y_train = np.array(X_train), np.array(y_train)

# Reshape X_train to be in the format (samples, time_steps, features)
X_train = np.reshape(X_train, (X_train.shape[0], X_train.shape[1], 1))

# Build the LSTM Model
model = Sequential()
model.add(LSTM(units=50, return_sequences=True, input_shape=(X_train.shape[1], 1)))
model.add(LSTM(units=50))
model.add(Dense(1))

# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')

# Train the model
model.fit(X_train, y_train, epochs=20, batch_size=32)

# Predict future values
predictions = model.predict(X_train)

# Inverse transform the predictions back to the original scale
predictions = scaler.inverse_transform(predictions)

# Visualize the actual vs predicted values
plt.figure(figsize=(12,6))
plt.plot(hourly_demand[time_step:], label='Actual')
plt.plot(predictions, label='Predicted')
plt.title('Actual vs Predicted Hourly Demand (LSTM)')
plt.xlabel('Time')
plt.ylabel('Hourly Demand Met (in MW)')
plt.legend()
plt.show()
D:\Softwares\Anaconda\Lib\site-packages\keras\src\layers\rnn\rnn.py:204: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
  super().__init__(**kwargs)
Epoch 1/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 13s 19ms/step - loss: 0.0241
Epoch 2/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 16ms/step - loss: 0.0040
Epoch 3/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 11s 19ms/step - loss: 0.0015
Epoch 4/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 17ms/step - loss: 0.0010
Epoch 5/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 8s 21ms/step - loss: 9.3667e-04
Epoch 6/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 8s 21ms/step - loss: 7.6057e-04
Epoch 7/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 10s 19ms/step - loss: 7.3900e-04
Epoch 8/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 18ms/step - loss: 7.0528e-04
Epoch 9/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 18ms/step - loss: 6.7631e-04
Epoch 10/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 17ms/step - loss: 6.6344e-04
Epoch 11/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 18ms/step - loss: 6.9789e-04
Epoch 12/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 10s 18ms/step - loss: 6.6491e-04
Epoch 13/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - loss: 6.3890e-04
Epoch 14/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 10s 18ms/step - loss: 6.2410e-04
Epoch 15/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 17ms/step - loss: 6.2724e-04
Epoch 16/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - loss: 6.2907e-04
Epoch 17/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 7s 19ms/step - loss: 6.1487e-04
Epoch 18/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 18ms/step - loss: 6.3083e-04
Epoch 19/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 10s 17ms/step - loss: 5.6240e-04
Epoch 20/20
364/364 ━━━━━━━━━━━━━━━━━━━━ 6s 16ms/step - loss: 5.6765e-04
364/364 ━━━━━━━━━━━━━━━━━━━━ 5s 8ms/step

import matplotlib.dates as mdates

# Create a date range for the x-axis based on your data
date_range = pd.date_range(start="2023-01-01", periods=len(hourly_demand), freq='H')

# Plot the results
plt.figure(figsize=(12, 6))

# Training data
plt.plot(date_range[:len(train_data)], train_data, label='Training Data')

# Test data
plt.plot(date_range[len(train_data):len(hourly_demand)], test_data, label='Forecasted Data', color='orange')

# Format x-axis to show dates
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())

# Rotate x-axis labels for readability
plt.gcf().autofmt_xdate()

# Add title and labels
plt.title('SARIMA Forecast for Hourly Demand')
plt.xlabel('Date')
plt.ylabel('Hourly Demand Met (in MW)')
plt.legend()
plt.tight_layout()
plt.show()
C:\Users\A\AppData\Local\Temp\ipykernel_4352\2660867132.py:4: FutureWarning: 'H' is deprecated and will be removed in a future version, please use 'h' instead.
  date_range = pd.date_range(start="2023-01-01", periods=len(hourly_demand), freq='H')

import matplotlib.dates as mdates

# Create a date range for the x-axis based on your data
date_range = pd.date_range(start="2023-01-01", periods=len(hourly_demand), freq='H')

# Plot the results
plt.figure(figsize=(12, 6))

# Training data
plt.plot(date_range[:len(train_data)], train_data, label='Training Data')

# Test data
plt.plot(date_range[len(train_data):len(hourly_demand)], test_data, label='Forecasted Data', color='orange')

# Format x-axis to show dates
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())

# Rotate x-axis labels for readability
plt.gcf().autofmt_xdate()

# Add title and labels
plt.title('SARIMA Forecast for Hourly Demand')
plt.xlabel('Date')
plt.ylabel('Hourly Demand Met (in MW)')
plt.legend()
plt.tight_layout()
plt.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[389], line 13
     10 plt.plot(date_range[:len(train_data)], train_data, label='Training Data')
     12 # Test data
---> 13 plt.plot(date_range[len(train_data):len(hourly_demand)], test_data, label='Forecasted Data', color='orange')
     15 # Format x-axis to show dates
     16 plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))

File D:\Softwares\Anaconda\Lib\site-packages\matplotlib\pyplot.py:3590, in plot(scalex, scaley, data, *args, **kwargs)
   3582 @_copy_docstring_and_deprecators(Axes.plot)
   3583 def plot(
   3584     *args: float | ArrayLike | str,
   (...)
   3588     **kwargs,
   3589 ) -> list[Line2D]:
-> 3590     return gca().plot(
   3591         *args,
   3592         scalex=scalex,
   3593         scaley=scaley,
   3594         **({"data": data} if data is not None else {}),
   3595         **kwargs,
   3596     )

File D:\Softwares\Anaconda\Lib\site-packages\matplotlib\axes\_axes.py:1724, in Axes.plot(self, scalex, scaley, data, *args, **kwargs)
   1481 """
   1482 Plot y versus x as lines and/or markers.
   1483 
   (...)
   1721 (``'green'``) or hex strings (``'#008000'``).
   1722 """
   1723 kwargs = cbook.normalize_kwargs(kwargs, mlines.Line2D)
-> 1724 lines = [*self._get_lines(self, *args, data=data, **kwargs)]
   1725 for line in lines:
   1726     self.add_line(line)

File D:\Softwares\Anaconda\Lib\site-packages\matplotlib\axes\_base.py:303, in _process_plot_var_args.__call__(self, axes, data, *args, **kwargs)
    301     this += args[0],
    302     args = args[1:]
--> 303 yield from self._plot_args(
    304     axes, this, kwargs, ambiguous_fmt_datakey=ambiguous_fmt_datakey)

File D:\Softwares\Anaconda\Lib\site-packages\matplotlib\axes\_base.py:499, in _process_plot_var_args._plot_args(self, axes, tup, kwargs, return_kwargs, ambiguous_fmt_datakey)
    496     axes.yaxis.update_units(y)
    498 if x.shape[0] != y.shape[0]:
--> 499     raise ValueError(f"x and y must have same first dimension, but "
    500                      f"have shapes {x.shape} and {y.shape}")
    501 if x.ndim > 2 or y.ndim > 2:
    502     raise ValueError(f"x and y can be no greater than 2D, but have "
    503                      f"shapes {x.shape} and {y.shape}")

ValueError: x and y must have same first dimension, but have shapes (11299,) and (91, 2)

# Plot the results
plt.figure(figsize=(12, 6))

# Training data
plt.plot(date_range[:len(train_data)], train_data, label='Training Data')

# Forecasted data (previously labeled as 'Test Data')
plt.plot(date_range[len(train_data):len(hourly_demand)], test_data, label='Forecasted Data', color='orange')

# Format x-axis to show dates
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
plt.gca().xaxis.set_major_locator(mdates.MonthLocator())

# Rotate x-axis labels for readability
plt.gcf().autofmt_xdate()

# Zoom into Jan 2024 to June 2024
plt.xlim(pd.Timestamp('2024-01-01'), pd.Timestamp('2024-04-30'))

# Add title and labels
plt.title('SARIMA Forecast for Hourly Demand (Jan 2024 - June 2024)')
plt.xlabel('Date')
plt.ylabel('Hourly Demand Met (in MW)')
plt.legend()
plt.tight_layout()
plt.show()