Sari95 commited on
Commit
c63f439
1 Parent(s): 080f627

Upload SARIMAX.ipynb

Browse files
Files changed (1) hide show
  1. SARIMAX.ipynb +821 -0
SARIMAX.ipynb ADDED
@@ -0,0 +1,821 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "24884304-ab6c-462a-bf6b-2a8abf74c9cc",
6
+ "metadata": {},
7
+ "source": [
8
+ "# SARIMAX for the prediction of energy consumption data over 48 hours"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": null,
14
+ "id": "05f2b198-d2f4-484b-8d63-5e590e3223b2",
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "#Install all required packages with 'conda install NAME' or with pip install NAME'\n",
19
+ "# pandas\n",
20
+ "# numpy\n",
21
+ "# matplotlib\n",
22
+ "# scikit-learn\n",
23
+ "# statsmodels\n",
24
+ "# pmdarima\n",
25
+ "# psutil"
26
+ ]
27
+ },
28
+ {
29
+ "cell_type": "code",
30
+ "execution_count": 2,
31
+ "id": "ca3f47bb-e6ef-4a33-a60d-77c500a9c6ab",
32
+ "metadata": {},
33
+ "outputs": [],
34
+ "source": [
35
+ "#Import all required libraries\n",
36
+ "import sys # Provides access to some variables used or maintained by the interpreter\n",
37
+ "import pandas as pd # Library for data manipulation and analysis, ideal for working with structured data like tables\n",
38
+ "import numpy as np # For scientific computing, support for large, multi-dimensional arrays and matrices\n",
39
+ "import matplotlib.pyplot as plt # For creating static, interactive, and animated visualizations\n",
40
+ "import matplotlib.dates as mdates # Provides matplotlib with date-specific plotting tools\n",
41
+ "from sklearn.preprocessing import MinMaxScaler # For scaling and normalizing features, often useful in data preprocessing\n",
42
+ "from sklearn.metrics import mean_squared_error, mean_absolute_error # For calculating common statistical error metrics\n",
43
+ "import pmdarima as pm # For ARIMA modeling in Python, which simplifies the process of building ARIMA models\n",
44
+ "from pmdarima.model_selection import train_test_split # Provides model selection capabilities like train/test splits\n",
45
+ "import statsmodels.api as sm # Provides many statistical models and functions\n",
46
+ "from statsmodels.graphics.tsaplots import plot_acf, plot_pacf # For plotting autocorrelation and partial autocorrelation charts\n",
47
+ "import psutil # For accessing system details and process utilities, helpful for monitoring system performance\n",
48
+ "from pathlib import Path # Used for filesystem path manipulation in an object-oriented way"
49
+ ]
50
+ },
51
+ {
52
+ "cell_type": "code",
53
+ "execution_count": 1,
54
+ "id": "88b3929e-0b04-4036-b43e-5738f74b2bc9",
55
+ "metadata": {},
56
+ "outputs": [
57
+ {
58
+ "name": "stdout",
59
+ "output_type": "stream",
60
+ "text": [
61
+ "/home/sarah/anaconda3/envs/BT2024SARIMAModel/bin/python\n"
62
+ ]
63
+ }
64
+ ],
65
+ "source": [
66
+ "#To display the current environment\n",
67
+ "print(sys.executable)"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "code",
72
+ "execution_count": 3,
73
+ "id": "ad3b8a68-11be-4cf0-9d33-dfcea476d122",
74
+ "metadata": {},
75
+ "outputs": [
76
+ {
77
+ "name": "stdout",
78
+ "output_type": "stream",
79
+ "text": [
80
+ "Erste und letzte Zeilen aus dfClimaAll:\n",
81
+ " dy Globalstrahlung_15Min Sonnenhöhe ExtraterrestrischeStrahlung \\\n",
82
+ "0 1 0 -65.0 0 \n",
83
+ "1 1 0 -65.0 0 \n",
84
+ "2 1 0 -65.4 0 \n",
85
+ "3 1 0 -65.5 0 \n",
86
+ "4 1 0 -65.2 0 \n",
87
+ "\n",
88
+ " StundenwertStrahlung Diffusstrahlung StrahlungGeneigteFläche \\\n",
89
+ "0 0 0 0 \n",
90
+ "1 0 0 0 \n",
91
+ "2 0 0 0 \n",
92
+ "3 0 0 0 \n",
93
+ "4 0 0 0 \n",
94
+ "\n",
95
+ " DiffusstrahlungGeneigteFläche Direktnormalstrahlung Lufttemperatur \\\n",
96
+ "0 0 0 9.2 \n",
97
+ "1 0 0 9.2 \n",
98
+ "2 0 0 9.2 \n",
99
+ "3 0 0 9.2 \n",
100
+ "4 0 0 9.1 \n",
101
+ "\n",
102
+ " Windgeschwindigkeit Schönwetterstrahlung Taupunkttemperatur TheorPVProd \\\n",
103
+ "0 1.8 0 7.9 0 \n",
104
+ "1 1.8 0 7.9 0 \n",
105
+ "2 1.8 0 7.9 0 \n",
106
+ "3 1.8 0 7.9 0 \n",
107
+ "4 1.8 0 7.8 0 \n",
108
+ "\n",
109
+ " TimestampWeather \n",
110
+ "0 2023-01-01 00:00:00 \n",
111
+ "1 2023-01-01 00:15:00 \n",
112
+ "2 2023-01-01 00:30:00 \n",
113
+ "3 2023-01-01 00:45:00 \n",
114
+ "4 2023-01-01 01:00:00 \n",
115
+ " dy Globalstrahlung_15Min Sonnenhöhe ExtraterrestrischeStrahlung \\\n",
116
+ "35035 365 0 -56.5 0 \n",
117
+ "35036 365 0 -58.5 0 \n",
118
+ "35037 365 0 -60.3 0 \n",
119
+ "35038 365 0 -61.9 0 \n",
120
+ "35039 365 0 -63.3 0 \n",
121
+ "\n",
122
+ " StundenwertStrahlung Diffusstrahlung StrahlungGeneigteFläche \\\n",
123
+ "35035 0 0 0 \n",
124
+ "35036 0 0 0 \n",
125
+ "35037 0 0 0 \n",
126
+ "35038 0 0 0 \n",
127
+ "35039 0 0 0 \n",
128
+ "\n",
129
+ " DiffusstrahlungGeneigteFläche Direktnormalstrahlung Lufttemperatur \\\n",
130
+ "35035 0 0 5.5 \n",
131
+ "35036 0 0 5.5 \n",
132
+ "35037 0 0 5.5 \n",
133
+ "35038 0 0 5.5 \n",
134
+ "35039 0 0 4.9 \n",
135
+ "\n",
136
+ " Windgeschwindigkeit Schönwetterstrahlung Taupunkttemperatur \\\n",
137
+ "35035 2.4 0 -18.2 \n",
138
+ "35036 2.4 0 -18.2 \n",
139
+ "35037 2.4 0 -18.2 \n",
140
+ "35038 2.4 0 -18.2 \n",
141
+ "35039 1.8 0 -14.0 \n",
142
+ "\n",
143
+ " TheorPVProd TimestampWeather \n",
144
+ "35035 0 2023-12-31 22:45:00 \n",
145
+ "35036 0 2023-12-31 23:00:00 \n",
146
+ "35037 0 2023-12-31 23:15:00 \n",
147
+ "35038 0 2023-12-31 23:30:00 \n",
148
+ "35039 0 2023-12-31 23:45:00 \n",
149
+ "Anzahl fehlender Werte pro Spalte:\n",
150
+ "dy 0\n",
151
+ "Globalstrahlung_15Min 0\n",
152
+ "Sonnenhöhe 0\n",
153
+ "ExtraterrestrischeStrahlung 0\n",
154
+ "StundenwertStrahlung 0\n",
155
+ "Diffusstrahlung 0\n",
156
+ "StrahlungGeneigteFläche 0\n",
157
+ "DiffusstrahlungGeneigteFläche 0\n",
158
+ "Direktnormalstrahlung 0\n",
159
+ "Lufttemperatur 0\n",
160
+ "Windgeschwindigkeit 0\n",
161
+ "Schönwetterstrahlung 0\n",
162
+ "Taupunkttemperatur 0\n",
163
+ "TheorPVProd 0\n",
164
+ "TimestampWeather 0\n",
165
+ "dtype: int64\n",
166
+ "Anzahl der Zeilen in dfClimaAll: 35040\n"
167
+ ]
168
+ }
169
+ ],
170
+ "source": [
171
+ "# Step 1 - Reading Data\n",
172
+ "\n",
173
+ "## dfClimaAll: Data from 2021-2023\n",
174
+ "## dfEnergyAll: All data from the transformer station from 2021-2023\n",
175
+ "\n",
176
+ "# Load data for 2021\n",
177
+ "dfClima21 = pd.read_excel('/home/sarah/Documents/BT2024/Weather_Data/2021TimeWeather.xlsx')\n",
178
+ "\n",
179
+ "# Load data for 2022\n",
180
+ "dfClima22 = pd.read_excel('/home/sarah/Documents/BT2024/Weather_Data/2022TimeWeather.xlsx')\n",
181
+ "\n",
182
+ "# Load data for 2023\n",
183
+ "dfClima23 = pd.read_excel('/home/sarah/Documents/BT2024/Weather_Data/2023TimeWeather.xlsx')\n",
184
+ "\n",
185
+ "# Merge the DataFrames\n",
186
+ "#Only DF23\n",
187
+ "dfClimaAll = dfClima23.copy()\n",
188
+ "#All DFs:\n",
189
+ "#dfClimaAll = pd.concat([dfClima21, dfClima22, dfClima23])\n",
190
+ "\n",
191
+ "# Check the resulting DataFrame\n",
192
+ "print(\"First and last rows from dfClimaAll:\")\n",
193
+ "print(dfClimaAll.head())\n",
194
+ "print(dfClimaAll.tail())\n",
195
+ "\n",
196
+ "# Check for missing values in each column\n",
197
+ "## .isnull(): Returns DF where values with NaN are replaced by True & values without NaN are replaced by False\n",
198
+ "## sum(): Sums up the number of True values\n",
199
+ "print(\"Number of missing values per column:\")\n",
200
+ "print(dfClimaAll.isnull().sum())\n",
201
+ "\n",
202
+ "# Convert 'Timestamp' into a Datetime object and set it as index, if not already set\n",
203
+ "if 'TimestampWeather' in dfClimaAll.columns:\n",
204
+ " ## pd.to_datetime: Converts values into Datetime objects —> Pandas can then treat these as timestamps\n",
205
+ " dfClimaAll['TimestampWeather'] = pd.to_datetime(dfClimaAll['TimestampWeather'])\n",
206
+ " ## set_index: Sets index for the Timestamp column to facilitate access to data based on time points\n",
207
+ " dfClimaAll = dfClimaAll.set_index('TimestampWeather')\n",
208
+ "\n",
209
+ "# Number of rows in dfClimaAll\n",
210
+ "print(\"Number of rows in dfClimaAll:\", dfClimaAll.shape[0])"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "code",
215
+ "execution_count": 4,
216
+ "id": "0a4710d5-06ed-4ab1-b396-66797786f0fa",
217
+ "metadata": {},
218
+ "outputs": [
219
+ {
220
+ "name": "stdout",
221
+ "output_type": "stream",
222
+ "text": [
223
+ "Erste und letzte Zeilen aus dfEnergyAll:\n",
224
+ " Lastgang\n",
225
+ "Timestamp \n",
226
+ "2023-01-01 00:00:00 657.35\n",
227
+ "2023-01-01 00:15:00 677.68\n",
228
+ "2023-01-01 00:30:00 644.91\n",
229
+ "2023-01-01 00:45:00 656.67\n",
230
+ "2023-01-01 01:00:00 660.77\n",
231
+ " Lastgang\n",
232
+ "Timestamp \n",
233
+ "2023-12-31 22:45:00 833.17\n",
234
+ "2023-12-31 23:00:00 767.89\n",
235
+ "2023-12-31 23:15:00 770.04\n",
236
+ "2023-12-31 23:30:00 730.45\n",
237
+ "2023-12-31 23:45:00 744.21\n",
238
+ "Anzahl der Zeilen in dfEnergyAll: 35036\n"
239
+ ]
240
+ }
241
+ ],
242
+ "source": [
243
+ "# Step 1 - Reading Data\n",
244
+ "\n",
245
+ "## dfClimaAll: Data from 2021-2023\n",
246
+ "## dfEnergyAll: All data from the transformer station from 2021-2023\n",
247
+ "\n",
248
+ "# Path to the directory containing the Excel files\n",
249
+ "directory_path = Path('/home/sarah/Documents/BT2024/2023')\n",
250
+ "\n",
251
+ "# Create a list of all Excel files in the directory\n",
252
+ "file_paths = list(directory_path.glob('*.xlsx'))\n",
253
+ "\n",
254
+ "# List to store the individual DataFrames\n",
255
+ "dfs = []\n",
256
+ "\n",
257
+ "# Loop over all file paths\n",
258
+ "for file_path in file_paths:\n",
259
+ " # Read the Excel file\n",
260
+ " df = pd.read_excel(file_path)\n",
261
+ " \n",
262
+ " # Convert the 'Timestamp' column into a datetime\n",
263
+ " df['Timestamp'] = pd.to_datetime(df['Timestamp'])\n",
264
+ " \n",
265
+ " # Convert the 'Lastgang' column into a numeric data type, treating errors as NaN\n",
266
+ " df['Lastgang'] = pd.to_numeric(df['Lastgang'], errors='coerce')\n",
267
+ " \n",
268
+ " # Sort the DataFrame by the 'Timestamp'\n",
269
+ " df = df.sort_values(by='Timestamp')\n",
270
+ " \n",
271
+ " # Perform linear interpolation for the 'Lastgang' on the individual DataFrame\n",
272
+ " df['Lastgang'] = df['Lastgang'].interpolate(method='linear')\n",
273
+ " \n",
274
+ " # Add the DataFrame to the list\n",
275
+ " dfs.append(df)\n",
276
+ "\n",
277
+ "# Merge all DataFrames in the list\n",
278
+ "dfEnergyAll = pd.concat(dfs).set_index('Timestamp')\n",
279
+ "\n",
280
+ "# Sum the Lastgang values for identical timestamps\n",
281
+ "dfEnergyAll = dfEnergyAll.groupby('Timestamp').sum()\n",
282
+ "\n",
283
+ "# Check the resulting DataFrame\n",
284
+ "print(\"First and last rows from dfEnergyAll:\")\n",
285
+ "print(dfEnergyAll.head())\n",
286
+ "print(dfEnergyAll.tail())\n",
287
+ "\n",
288
+ "# Display the number of rows in dfEnergyAll\n",
289
+ "print(\"Number of rows in dfEnergyAll:\", dfEnergyAll.shape[0])\n"
290
+ ]
291
+ },
292
+ {
293
+ "cell_type": "code",
294
+ "execution_count": 5,
295
+ "id": "dda00bbf-4a50-45f8-8c98-c29eff6bf793",
296
+ "metadata": {},
297
+ "outputs": [
298
+ {
299
+ "name": "stdout",
300
+ "output_type": "stream",
301
+ "text": [
302
+ "Fehlende Zeitstempel in dfEnergyAll:\n",
303
+ "DatetimeIndex(['2023-03-26 02:00:00', '2023-03-26 02:15:00',\n",
304
+ " '2023-03-26 02:30:00', '2023-03-26 02:45:00'],\n",
305
+ " dtype='datetime64[ns]', name='Timestamp', freq=None)\n",
306
+ "Überprüfen nach dem Ergänzen der fehlenden Zeitstempel:\n",
307
+ " Lastgang\n",
308
+ "2023-03-26 02:00:00 520.34\n",
309
+ "2023-03-26 02:15:00 520.34\n",
310
+ "2023-03-26 02:30:00 520.34\n",
311
+ "2023-03-26 02:45:00 520.34\n",
312
+ "Anzahl der Zeilen in dfClima21: 35040\n",
313
+ "Anzahl der Zeilen in dfEnergyAll: 35040\n"
314
+ ]
315
+ },
316
+ {
317
+ "name": "stderr",
318
+ "output_type": "stream",
319
+ "text": [
320
+ "/tmp/ipykernel_1561988/424088960.py:29: FutureWarning: DataFrame.fillna with 'method' is deprecated and will raise in a future version. Use obj.ffill() or obj.bfill() instead.\n",
321
+ " dfEnergyAll = dfEnergyAll.sort_index().fillna(method='ffill')\n"
322
+ ]
323
+ }
324
+ ],
325
+ "source": [
326
+ "# Step 2 - Preparing the Data\n",
327
+ "\n",
328
+ "# Create a complete timestamp index for the years 2023 in 15-minute intervals\n",
329
+ "all_timestamps = pd.date_range(start='2023-01-01 00:00:00', end='2023-12-31 23:45:00', freq='15T')\n",
330
+ "\n",
331
+ "# Convert this into a DataFrame\n",
332
+ "df_all_timestamps = pd.DataFrame(all_timestamps, columns=['Timestamp'])\n",
333
+ "df_all_timestamps = df_all_timestamps.set_index('Timestamp')\n",
334
+ "\n",
335
+ "# Compare the complete timestamp index with dfEnergyAll\n",
336
+ "missing_timestamps = df_all_timestamps.index.difference(dfEnergyAll.index)\n",
337
+ "\n",
338
+ "print(\"Missing timestamps in dfEnergyAll:\")\n",
339
+ "print(missing_timestamps)\n",
340
+ "\n",
341
+ "\n",
342
+ "# Missing timestamps\n",
343
+ "missing_timestamps = pd.DatetimeIndex(['2023-03-26 02:00:00', '2023-03-26 02:15:00',\n",
344
+ " '2023-03-26 02:30:00', '2023-03-26 02:45:00'])\n",
345
+ "\n",
346
+ "# Create a DataFrame with the missing timestamps\n",
347
+ "df_missing = pd.DataFrame(index=missing_timestamps)\n",
348
+ "\n",
349
+ "# Merge this DataFrame with the original DataFrame\n",
350
+ "dfEnergyAll = dfEnergyAll.combine_first(df_missing)\n",
351
+ "\n",
352
+ "# Fill the missing values. Use 'ffill' for forward fill.\n",
353
+ "dfEnergyAll = dfEnergyAll.sort_index().fillna(method='ffill')\n",
354
+ "\n",
355
+ "print(\"Check after adding the missing timestamps:\")\n",
356
+ "print(dfEnergyAll.loc[missing_timestamps])\n",
357
+ "\n",
358
+ "# Number of rows in dfClima21\n",
359
+ "# Ensure dfClima21 is already defined before executing this line.\n",
360
+ "print(\"Number of rows in dfClima21:\", dfClimaAll.shape[0])\n",
361
+ "\n",
362
+ "# Number of rows in dfEnergyAll\n",
363
+ "print(\"Number of rows in dfEnergyAll:\", dfEnergyAll.shape[0])"
364
+ ]
365
+ },
366
+ {
367
+ "cell_type": "code",
368
+ "execution_count": 6,
369
+ "id": "d7e1513e-2337-45c7-92b9-82db88714c50",
370
+ "metadata": {},
371
+ "outputs": [
372
+ {
373
+ "name": "stdout",
374
+ "output_type": "stream",
375
+ "text": [
376
+ "Anzahl fehlender Werte pro Spalte:\n",
377
+ "Lastgang 0\n",
378
+ "StundenwertStrahlung 0\n",
379
+ "Globalstrahlung_15Min 0\n",
380
+ "StrahlungGeneigteFläche 0\n",
381
+ "TheorPVProd 0\n",
382
+ "Direktnormalstrahlung 0\n",
383
+ "Schönwetterstrahlung 0\n",
384
+ "Lufttemperatur 0\n",
385
+ "dtype: int64\n",
386
+ "Erste Zeilen aus dfEnergyAll-Bearbeitet:\n",
387
+ " Lastgang StundenwertStrahlung Globalstrahlung_15Min \\\n",
388
+ "2023-01-01 00:00:00 657.35 0 0 \n",
389
+ "2023-01-01 00:15:00 677.68 0 0 \n",
390
+ "2023-01-01 00:30:00 644.91 0 0 \n",
391
+ "2023-01-01 00:45:00 656.67 0 0 \n",
392
+ "2023-01-01 01:00:00 660.77 0 0 \n",
393
+ "\n",
394
+ " StrahlungGeneigteFläche TheorPVProd \\\n",
395
+ "2023-01-01 00:00:00 0 0 \n",
396
+ "2023-01-01 00:15:00 0 0 \n",
397
+ "2023-01-01 00:30:00 0 0 \n",
398
+ "2023-01-01 00:45:00 0 0 \n",
399
+ "2023-01-01 01:00:00 0 0 \n",
400
+ "\n",
401
+ " Direktnormalstrahlung Schönwetterstrahlung \\\n",
402
+ "2023-01-01 00:00:00 0 0 \n",
403
+ "2023-01-01 00:15:00 0 0 \n",
404
+ "2023-01-01 00:30:00 0 0 \n",
405
+ "2023-01-01 00:45:00 0 0 \n",
406
+ "2023-01-01 01:00:00 0 0 \n",
407
+ "\n",
408
+ " Lufttemperatur \n",
409
+ "2023-01-01 00:00:00 9.2 \n",
410
+ "2023-01-01 00:15:00 9.2 \n",
411
+ "2023-01-01 00:30:00 9.2 \n",
412
+ "2023-01-01 00:45:00 9.2 \n",
413
+ "2023-01-01 01:00:00 9.1 \n",
414
+ " Lastgang StundenwertStrahlung Globalstrahlung_15Min \\\n",
415
+ "2023-12-31 22:45:00 833.17 0 0 \n",
416
+ "2023-12-31 23:00:00 767.89 0 0 \n",
417
+ "2023-12-31 23:15:00 770.04 0 0 \n",
418
+ "2023-12-31 23:30:00 730.45 0 0 \n",
419
+ "2023-12-31 23:45:00 744.21 0 0 \n",
420
+ "\n",
421
+ " StrahlungGeneigteFläche TheorPVProd \\\n",
422
+ "2023-12-31 22:45:00 0 0 \n",
423
+ "2023-12-31 23:00:00 0 0 \n",
424
+ "2023-12-31 23:15:00 0 0 \n",
425
+ "2023-12-31 23:30:00 0 0 \n",
426
+ "2023-12-31 23:45:00 0 0 \n",
427
+ "\n",
428
+ " Direktnormalstrahlung Schönwetterstrahlung \\\n",
429
+ "2023-12-31 22:45:00 0 0 \n",
430
+ "2023-12-31 23:00:00 0 0 \n",
431
+ "2023-12-31 23:15:00 0 0 \n",
432
+ "2023-12-31 23:30:00 0 0 \n",
433
+ "2023-12-31 23:45:00 0 0 \n",
434
+ "\n",
435
+ " Lufttemperatur \n",
436
+ "2023-12-31 22:45:00 5.5 \n",
437
+ "2023-12-31 23:00:00 5.5 \n",
438
+ "2023-12-31 23:15:00 5.5 \n",
439
+ "2023-12-31 23:30:00 5.5 \n",
440
+ "2023-12-31 23:45:00 4.9 \n"
441
+ ]
442
+ }
443
+ ],
444
+ "source": [
445
+ "# Step 2 - Preparing the Data\n",
446
+ "\n",
447
+ "# To check if the indexes in both DataFrames are set equally\n",
448
+ "dfClimaAll.index.equals(dfEnergyAll.index)\n",
449
+ "\n",
450
+ "# Adding the columns from dfClima21 to dfEnergyAll\n",
451
+ "dfEnergyAll = dfEnergyAll.join(dfClimaAll[['StundenwertStrahlung', 'Globalstrahlung_15Min', 'StrahlungGeneigteFläche', 'TheorPVProd', 'Direktnormalstrahlung', 'Schönwetterstrahlung', 'Lufttemperatur']])\n",
452
+ "\n",
453
+ "# Checking for missing values in each column\n",
454
+ "print(\"Number of missing values per column:\")\n",
455
+ "print(dfEnergyAll.isnull().sum())\n",
456
+ "\n",
457
+ "# Printing the first and last rows from the dataframe\n",
458
+ "print(\"First and last rows from dfEnergyAll-Edited:\")\n",
459
+ "print(dfEnergyAll.head())\n",
460
+ "print(dfEnergyAll.tail())"
461
+ ]
462
+ },
463
+ {
464
+ "cell_type": "code",
465
+ "execution_count": 7,
466
+ "id": "e33a35b4-0b2d-41d0-9be7-5ed46491538b",
467
+ "metadata": {},
468
+ "outputs": [
469
+ {
470
+ "name": "stdout",
471
+ "output_type": "stream",
472
+ "text": [
473
+ " Lastgang StundenwertStrahlung Globalstrahlung_15Min \\\n",
474
+ "2023-01-01 00:00:00 657.35 0 0 \n",
475
+ "2023-01-01 00:15:00 677.68 0 0 \n",
476
+ "2023-01-01 00:30:00 644.91 0 0 \n",
477
+ "2023-01-01 00:45:00 656.67 0 0 \n",
478
+ "2023-01-01 01:00:00 660.77 0 0 \n",
479
+ "\n",
480
+ " StrahlungGeneigteFläche TheorPVProd \\\n",
481
+ "2023-01-01 00:00:00 0 0 \n",
482
+ "2023-01-01 00:15:00 0 0 \n",
483
+ "2023-01-01 00:30:00 0 0 \n",
484
+ "2023-01-01 00:45:00 0 0 \n",
485
+ "2023-01-01 01:00:00 0 0 \n",
486
+ "\n",
487
+ " Direktnormalstrahlung Schönwetterstrahlung \\\n",
488
+ "2023-01-01 00:00:00 0 0 \n",
489
+ "2023-01-01 00:15:00 0 0 \n",
490
+ "2023-01-01 00:30:00 0 0 \n",
491
+ "2023-01-01 00:45:00 0 0 \n",
492
+ "2023-01-01 01:00:00 0 0 \n",
493
+ "\n",
494
+ " Lufttemperatur Lastgang_Moving_Average \\\n",
495
+ "2023-01-01 00:00:00 9.2 707.704792 \n",
496
+ "2023-01-01 00:15:00 9.2 707.704792 \n",
497
+ "2023-01-01 00:30:00 9.2 707.704792 \n",
498
+ "2023-01-01 00:45:00 9.2 707.704792 \n",
499
+ "2023-01-01 01:00:00 9.1 707.704792 \n",
500
+ "\n",
501
+ " Lastgang_First_Difference \n",
502
+ "2023-01-01 00:00:00 20.33 \n",
503
+ "2023-01-01 00:15:00 20.33 \n",
504
+ "2023-01-01 00:30:00 -32.77 \n",
505
+ "2023-01-01 00:45:00 11.76 \n",
506
+ "2023-01-01 01:00:00 4.10 \n"
507
+ ]
508
+ },
509
+ {
510
+ "name": "stderr",
511
+ "output_type": "stream",
512
+ "text": [
513
+ "/tmp/ipykernel_1561988/3400565057.py:13: FutureWarning: DataFrame.fillna with 'method' is deprecated and will raise in a future version. Use obj.ffill() or obj.bfill() instead.\n",
514
+ " dfEnergyAll = dfEnergyAll.fillna(method='bfill')\n"
515
+ ]
516
+ }
517
+ ],
518
+ "source": [
519
+ "# Step 2 - Preparing the Data\n",
520
+ "\n",
521
+ "# Calculating the moving average for 'Lastgang'\n",
522
+ "window_size = 96 # This corresponds to 24 hours at 15-minute intervals\n",
523
+ "dfEnergyAll['Lastgang_Moving_Average'] = dfEnergyAll['Lastgang'].rolling(window=window_size).mean()\n",
524
+ "\n",
525
+ "# Calculating the first differences for 'Lastgang'\n",
526
+ "dfEnergyAll['Lastgang_First_Difference'] = dfEnergyAll['Lastgang'].diff()\n",
527
+ "\n",
528
+ "# Since the first `window_size - 1` values of the moving average will be NaN and the first value of the first difference will be NaN, fill these values.\n",
529
+ "dfEnergyAll = dfEnergyAll.fillna(method='bfill')\n",
530
+ "\n",
531
+ "# To check the first few rows to ensure the new features look as expected.\n",
532
+ "print(dfEnergyAll.head())"
533
+ ]
534
+ },
535
+ {
536
+ "cell_type": "code",
537
+ "execution_count": 8,
538
+ "id": "326e34ca-5066-464d-9e84-b1da0b22f755",
539
+ "metadata": {},
540
+ "outputs": [
541
+ {
542
+ "data": {
543
+ "image/png": "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9SYiIiIq7lGPoRg/ERERUUlGJ6XWrl2rFsTcv38fx44dw7Jly4wONN58881SR2nx8fHByZMncePGDY3Xbt26BVdXV73z379/H2FhYbh06RJ2795d6pDDbdq0gZWVFc6fP68zKTVp0iSMHz9efp6RkQEvLy+9yyUiIqJH26MeQzF+IiIiopKMTkqpOussbtCgQWjevDmioqIwatQog5fl7OwMZ2fnUssFBQUhPT0df/75J9q1awcAOHToENLT09GhQwed86mCqfPnz2PPnj1wcnIq9b1Onz6N+/fvw93dXWcZhUIBhUJR6rKIiIiIVB71GIrxExEREZUkCSGEKRZ08eJF+Pv7IysryxSL0xAaGorr16/ju+++A1A0nLG3t7facMZNmzZFREQEBgwYgPz8fAwcOBBHjx7Fpk2b1H4NrFOnDqytrXHx4kWsXLkSvXr1grOzM/7++2+8++67sLW1xeHDh2FpaWlQ3TIyMqBUKpGenl7qr4hERERUdZjjHP+oxlCMn4iIiKovQ8/zFqZ4s+zsbMyfPx+enp6mWJxWK1euRMuWLRESEoKQkBD4+/tjxYoVamXOnTuH9PR0AEBiYiI2bNiAxMREPP7443B3d5cfBw4cAFA0DPOuXbvQo0cPNGnSBGPHjkVISAh27txpcEKKiIiI6GExhiIiIqJHmdEtpRwdHdX6QxBCIDMzE7a2tli5ciWeeeYZk1eysuMvfURERNWTKc/xjKHUMX4iIiKqvgw9zxvdp9ScOXPUnltYWKBu3boIDAzElStXjK4oERER0aOAMRQRERGRujL3KZWeno6VK1diyZIlOH78uFHDGVcX/KWPiIioeirPc/yjHkMxfiIiIqq+yr1Pqd27d2PYsGFwd3fH/PnzERoaivj4+IddHBEREdEjgTEUERERURGjbt9LTExEZGQkli5diqysLISFheH+/fuIjo6Gn59fedWRiIiIqEpjDEVERESkyeCWUr169YKfnx/+/vtvzJ8/H9evX8f8+fPLs25EREREVR5jKCIiIiLtDG4ptWPHDowdOxavv/46GjVqVJ51IiIiIqo2GEMRERERaWdwS6l9+/YhMzMTAQEBCAwMxDfffINbt26VZ92IiIiIqjzGUERERETaGZyUCgoKwvfff4+kpCS8+uqrWL16NerVq4fCwkLExMQgMzOzPOtJREREVCUxhiIiIiLSThJCiIed+dy5c1iyZAlWrFiBtLQ0BAcHY8OGDaasX5XAIY2JiIiqp/I6xzOGYvxERERUnRl6nje4pZQ2TZo0wcyZM5GYmIhVq1aVZVFEREREjwzGUERERERlbClFRfhLHxERUfXEc3z54bYlIiKqvszSUoqIiIiIiIiIiOhhMClFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2TEpRUREREREREREZsekFBERERERERERmR2TUkREREREREREZHZMShERERERERERkdkxKUVERERERERERGbHpBQREREREREREZkdk1JERERERERERGR2TEoREREREREREZHZMSlFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2VWZpFRqairCw8OhVCqhVCoRHh6OtLQ0vfOMGDECkiSpPdq3b69WJjc3F2+99RacnZ1hb2+PZ555BomJieW4JkRERETmwxiKiIiIKqsqk5R6/vnncfz4cWzbtg3btm3D8ePHER4eXup8PXv2RFJSkvzYsmWL2uvjxo3DunXrsHr1auzfvx93795Fnz59UFBQUF6rQkRERGQ2jKGIiIiosqpR0RUwxJkzZ7Bt2zYcPHgQgYGBAIDvv/8eQUFBOHfuHJo0aaJzXoVCATc3N62vpaenY8mSJVixYgW6d+8OAPjpp5/g5eWFnTt3okePHqZfGSIiIiIzYQxFRERElVmVaCkVFxcHpVIpB1MA0L59eyiVShw4cEDvvHv37oWLiwsaN26M0aNH4+bNm/JrR44cwf379xESEiJP8/DwQIsWLfQuNzc3FxkZGWoPIiIiosqmMsVQjJ+IiIiopCqRlEpOToaLi4vGdBcXFyQnJ+ucLzQ0FCtXrsTu3bvx9ddf4/Dhw3j66aeRm5srL9fa2hqOjo5q87m6uupdbkREhNwvg1KphJeX10OuGREREVH5qUwxFOMnIiIiKqlCk1JTpkzR6ESz5CM+Ph4AIEmSxvxCCK3TVYYMGYLevXujRYsW6Nu3L7Zu3Yp//vkHmzdv1luv0pY7adIkpKeny4+EhAQD15iIiIio7KpiDMX4iYiIiEqq0D6l3nzzTQwdOlRvGR8fH5w8eRI3btzQeO3WrVtwdXU1+P3c3d3h7e2N8+fPAwDc3NyQl5eH1NRUtV/6bt68iQ4dOuhcjkKhgEKhMPh9iYiIiEypKsZQjJ+IiIiopApNSjk7O8PZ2bnUckFBQUhPT8eff/6Jdu3aAQAOHTqE9PR0vcmjklJSUpCQkAB3d3cAQNu2bWFlZYWYmBiEhYUBAJKSknDq1CnMnDnzIdaIiIiIqPwxhiIiIqLqoEr0KdWsWTP07NkTo0ePxsGDB3Hw4EGMHj0affr0URs1pmnTpli3bh0A4O7du3jvvfcQFxeHy5cvY+/evejbty+cnZ0xYMAAAIBSqcSoUaPw7rvvYteuXTh27BiGDRuGli1byiPJEBEREVVVjKGIiIioMqvQllLGWLlyJcaOHSuP8vLMM8/gm2++UStz7tw5pKenAwAsLS3x119/Yfny5UhLS4O7uzu6du2KqKgo1KpVS55n9uzZqFGjBsLCwpCdnY1u3bohMjISlpaW5ls5IiIionLCGIqIiIgqK0kIISq6ElVdRkYGlEol0tPT4eDgUNHVISIiIhPhOb78cNsSERFVX4ae56vE7XtERERERERERFS9MClFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2TEpRUREREREREREZsekFBERERERERERmR2TUkREREREREREZHZMShERERERERERkdkxKUVERERERERERGbHpBQREREREREREZkdk1JERERERERERGR2TEoREREREREREZHZMSlFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2TEpRUREREREREREZsekFBERERERERERmR2TUkREREREREREZHZMShERERERERERkdkxKUVERERERERERGbHpBQREREREREREZkdk1JERERERERERGR2TEoREREREREREZHZMSlFRERERERERERmx6QUERERERERERGZXZVJSqWmpiI8PBxKpRJKpRLh4eFIS0vTO48kSVofX375pVymS5cuGq8PHTq0nNeGiIiIyDwYQxEREVFlVaOiK2Co559/HomJidi2bRsA4JVXXkF4eDg2btyoc56kpCS151u3bsWoUaMwcOBAtemjR4/GtGnT5Oe2trYmrDkRERFRxWEMRURERJVVlUhKnTlzBtu2bcPBgwcRGBgIAPj+++8RFBSEc+fOoUmTJlrnc3NzU3v+22+/oWvXrmjQoIHadDs7O42yRERERFUdYygiIiKqzKrE7XtxcXFQKpVyMAUA7du3h1KpxIEDBwxaxo0bN7B582aMGjVK47WVK1fC2dkZzZs3x3vvvYfMzEy9y8rNzUVGRobag4iIiKiyqUwxFOMnIiIiKqlKtJRKTk6Gi4uLxnQXFxckJycbtIxly5ahVq1aePbZZ9Wmv/DCC/D19YWbmxtOnTqFSZMm4cSJE4iJidG5rIiICEydOtW4lSAiIiIys8oUQzF+IiIiopIqtKXUlClTdHakqXrEx8cDKOpwsyQhhNbp2ixduhQvvPACbGxs1KaPHj0a3bt3R4sWLTB06FD8+uuv2LlzJ44ePapzWZMmTUJ6err8SEhIMGKtiYiIiMqmKsZQjJ+IiIiopAptKfXmm2+WOkqLj48PTp48iRs3bmi8duvWLbi6upb6Pvv27cO5c+cQFRVVatk2bdrAysoK58+fR5s2bbSWUSgUUCgUpS6LiIiIqDxUxRiK8RMRERGVVKFJKWdnZzg7O5daLigoCOnp6fjzzz/Rrl07AMChQ4eQnp6ODh06lDr/kiVL0LZtW7Rq1arUsqdPn8b9+/fh7u5e+goQERERVQDGUERERFQdVImOzps1a4aePXti9OjROHjwIA4ePIjRo0ejT58+aqPGNG3aFOvWrVObNyMjA7/88gtefvlljeVevHgR06ZNQ3x8PC5fvowtW7Zg8ODBaN26NTp27Fju60VERERUnhhDERERUWVWJZJSQNHoLi1btkRISAhCQkLg7++PFStWqJU5d+4c0tPT1aatXr0aQgg899xzGsu0trbGrl270KNHDzRp0gRjx45FSEgIdu7cCUtLy3JdHyIiIiJzYAxFRERElZUkhBAVXYmqLiMjA0qlEunp6XBwcKjo6hAREZGJ8BxffrhtiYiIqi9Dz/NVpqUUERERERERERFVH0xKERERERERERGR2TEpRUREREREREREZsekFBERERERERERmR2TUkREREREREREZHZMShERERERERERkdkxKUVERERERERERGbHpBQREREREREREZkdk1JERERERERERGR2TEoREREREREREZHZMSlFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2TEpRUREREREREREZsekFBERERERERERmR2TUkREREREREREZHZMShERERERERERkdkxKUVERERERERERGbHpBQREREREREREZkdk1JERERERERERGR2TEoREREREREREZHZMSlFRERERERERERmx6QUERERERERERGZHZNSRERERERERERkdkxKERERERERERGR2TEpRUREREREREREZldlklKff/45OnToADs7O9SuXdugeYQQmDJlCjw8PGBra4suXbrg9OnTamVyc3Px1ltvwdnZGfb29njmmWeQmJhYDmtAREREZH6MoYiIiKiyqjJJqby8PAwePBivv/66wfPMnDkTs2bNwjfffIPDhw/Dzc0NwcHByMzMlMuMGzcO69atw+rVq7F//37cvXsXffr0QUFBQXmsBhEREZFZMYYiIiKiykoSQoiKroQxIiMjMW7cOKSlpektJ4SAh4cHxo0bhwkTJgAo+kXP1dUVM2bMwKuvvor09HTUrVsXK1aswJAhQwAA169fh5eXF7Zs2YIePXoYVKeMjAwolUqkp6fDwcGhTOtHRERElUd1OsdXthiqOm1bIiIiUmfoeb7KtJQy1qVLl5CcnIyQkBB5mkKhQOfOnXHgwAEAwJEjR3D//n21Mh4eHmjRooVchoiIiOhRwhiKiIiIzKVGRVegvCQnJwMAXF1d1aa7urriypUrchlra2s4OjpqlFHNr01ubi5yc3Pl5+np6QCKMoFERERUfajO7VWsYXmZlFcMxfiJiIjo0WFoDFWhSakpU6Zg6tSpesscPnwYAQEBD/0ekiSpPRdCaEwrqbQyERERWuvt5eX1cJUkIiKiSi0zMxNKpbKiqyGrijEU4yciIqJHT2kxVIUmpd58800MHTpUbxkfH5+HWrabmxuAol/y3N3d5ek3b96Uf/lzc3NDXl4eUlNT1X7pu3nzJjp06KBz2ZMmTcL48ePl54WFhbhz5w6cnJxKDdaMlZGRAS8vLyQkJLC/hXLGbW0+3Nbmw21tPtzW5mPObS2EQGZmJjw8PMr1fYxVFWMoxk/VE7e1+XBbmw+3tflwW5uPube1oTFUhSalnJ2d4ezsXC7L9vX1hZubG2JiYtC6dWsARaPPxMbGYsaMGQCAtm3bwsrKCjExMQgLCwMAJCUl4dSpU5g5c6bOZSsUCigUCrVphg6x/LAcHBy4k5oJt7X5cFubD7e1+XBb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",
544
+ "text/plain": [
545
+ "<Figure size 1200x400 with 2 Axes>"
546
+ ]
547
+ },
548
+ "metadata": {},
549
+ "output_type": "display_data"
550
+ }
551
+ ],
552
+ "source": [
553
+ "# Step 2 - Preparing the Data\n",
554
+ "\n",
555
+ "# Create a figure with two subplots side by side\n",
556
+ "fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4)) # 1 row, 2 columns, and an optional size specification\n",
557
+ "\n",
558
+ "# ACF plot in the first subplot\n",
559
+ "plot_acf(dfEnergyAll['Lastgang'].dropna(), lags=50, ax=ax1) # Autocorrelation plot for 'Lastgang' with 50 lags\n",
560
+ "ax1.set_title('Autocorrelation Function (ACF) of Lastgang') # Set title for the first subplot\n",
561
+ "ax1.set_xlabel('Lags') # Set x-label as 'Lags'\n",
562
+ "ax1.set_ylabel('Autocorrelation') # Set y-label as 'Autocorrelation'\n",
563
+ "\n",
564
+ "# PACF plot in the second subplot\n",
565
+ "plot_pacf(dfEnergyAll['Lastgang'].dropna(), lags=50, ax=ax2) # Partial autocorrelation plot for 'Lastgang' with 50 lags\n",
566
+ "ax2.set_title('Partial Autocorrelation Function (PACF) of Lastgang') # Set title for the second subplot\n",
567
+ "ax2.set_xlabel('Lags') # Set x-label as 'Lags'\n",
568
+ "ax2.set_ylabel('Autocorrelation') # Set y-label as 'Autocorrelation'\n",
569
+ "\n",
570
+ "# Adjust spacing between the plots\n",
571
+ "plt.tight_layout()\n",
572
+ "\n",
573
+ "# Display the plots\n",
574
+ "plt.show()"
575
+ ]
576
+ },
577
+ {
578
+ "cell_type": "code",
579
+ "execution_count": 9,
580
+ "id": "a8e697f0-54a3-4854-82dd-2334582538a5",
581
+ "metadata": {},
582
+ "outputs": [],
583
+ "source": [
584
+ "# Step 3 - Data Scaling\n",
585
+ "\n",
586
+ "# Initialize the scalers\n",
587
+ "lastgang_scaler = MinMaxScaler(feature_range=(0, 1))\n",
588
+ "features_scaler = MinMaxScaler(feature_range=(0, 1))\n",
589
+ "\n",
590
+ "# Apply the scaler to the corresponding columns\n",
591
+ "dfEnergyAll['Lastgang'] = lastgang_scaler.fit_transform(dfEnergyAll['Lastgang'].values.reshape(-1, 1))\n",
592
+ "dfEnergyAll[['StundenwertStrahlung', 'Lufttemperatur', 'Globalstrahlung_15Min', 'StrahlungGeneigteFläche', 'Lastgang_Moving_Average', 'Lastgang_First_Difference']] = features_scaler.fit_transform(dfEnergyAll[['StundenwertStrahlung', 'Lufttemperatur', 'Globalstrahlung_15Min', 'StrahlungGeneigteFläche', 'Lastgang_Moving_Average', 'Lastgang_First_Difference']])"
593
+ ]
594
+ },
595
+ {
596
+ "cell_type": "code",
597
+ "execution_count": null,
598
+ "id": "4a436b9a-88ca-4642-9a8f-5e4f61d69c7e",
599
+ "metadata": {},
600
+ "outputs": [],
601
+ "source": [
602
+ "# Step 3 - Check the scaled values\n",
603
+ "\n",
604
+ "print(dfEnergyAll.describe())\n",
605
+ "\n",
606
+ "print(\"Skalierparameter für Lastgang:\", lastgang_scaler.scale_, lastgang_scaler.min_)"
607
+ ]
608
+ },
609
+ {
610
+ "cell_type": "code",
611
+ "execution_count": 10,
612
+ "id": "7c33eb1a-a633-499d-96cf-a5a5a6aa33b5",
613
+ "metadata": {},
614
+ "outputs": [
615
+ {
616
+ "name": "stdout",
617
+ "output_type": "stream",
618
+ "text": [
619
+ "CPU before model training:\n",
620
+ "CPU Utilization: 98.6%\n",
621
+ "RAM before model training:\n",
622
+ "Total memory: 146.88 GB\n",
623
+ "Available memory: 109.45 GB\n",
624
+ "Used memory: 35.63 GB\n",
625
+ "Memory usage: 25.5%\n"
626
+ ]
627
+ }
628
+ ],
629
+ "source": [
630
+ "# Step 3 - Show GPU Utilization and Memory Usage before Model Training\n",
631
+ "\n",
632
+ "#def print_gpu_utilization():\n",
633
+ " # Retrieve and print GPU utilization and memory stats\n",
634
+ " # GPUs = GPUtil.getGPUs()\n",
635
+ " # for GPU in GPUs:\n",
636
+ " # print(f\"GPU: {GPU.name}, GPU RAM Free: {GPU.memoryFree}MB, Used: {GPU.memoryUsed}MB, Utilization: {GPU.load*100}%\")\n",
637
+ "\n",
638
+ "#print(\"GPU before model training:\", datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\"))\n",
639
+ "#print_gpu_utilization()\n",
640
+ "\n",
641
+ "# Function to print CPU utilization\n",
642
+ "def print_cpu_utilization():\n",
643
+ " # Retrieve and print the percentage of CPU utilization\n",
644
+ " print(f\"CPU Utilization: {psutil.cpu_percent(interval=1)}%\")\n",
645
+ "\n",
646
+ "print(\"CPU before model training:\")\n",
647
+ "print_cpu_utilization()\n",
648
+ "\n",
649
+ "def print_memory_usage():\n",
650
+ " # Retrieve and print memory statistics\n",
651
+ " memory = psutil.virtual_memory()\n",
652
+ " print(f\"Total memory: {memory.total / (1024**3):.2f} GB\")\n",
653
+ " print(f\"Available memory: {memory.available / (1024**3):.2f} GB\")\n",
654
+ " print(f\"Used memory: {memory.used / (1024**3):.2f} GB\")\n",
655
+ " print(f\"Memory usage: {memory.percent}%\")\n",
656
+ "\n",
657
+ "print(\"RAM before model training:\")\n",
658
+ "print_memory_usage()\n"
659
+ ]
660
+ },
661
+ {
662
+ "cell_type": "code",
663
+ "execution_count": null,
664
+ "id": "c181460f-7248-4672-b8a0-516e6e09fed7",
665
+ "metadata": {},
666
+ "outputs": [
667
+ {
668
+ "name": "stdout",
669
+ "output_type": "stream",
670
+ "text": [
671
+ "RUNNING THE L-BFGS-B CODE\n",
672
+ "\n",
673
+ " * * *\n",
674
+ "\n",
675
+ "Machine precision = 2.220D-16\n",
676
+ " N = 12 M = 10\n",
677
+ "\n",
678
+ "At X0 0 variables are exactly at the bounds\n",
679
+ "\n",
680
+ "At iterate 0 f= -2.08057D+00 |proj g|= 1.19631D+03\n"
681
+ ]
682
+ },
683
+ {
684
+ "name": "stderr",
685
+ "output_type": "stream",
686
+ "text": [
687
+ " This problem is unconstrained.\n"
688
+ ]
689
+ }
690
+ ],
691
+ "source": [
692
+ "# Step 4 - Creating the SARIMAX model and generating predictions\n",
693
+ "\n",
694
+ "# Ensure the index is set as DatetimeIndex and has the correct frequency\n",
695
+ "dfEnergyAll.index = pd.to_datetime(dfEnergyAll.index) # Convert the index to datetime type\n",
696
+ "dfEnergyAll = dfEnergyAll.asfreq('15T') # Set the frequency of the DataFrame to every 15 minutes\n",
697
+ "\n",
698
+ "# Split the data into training and testing sets\n",
699
+ "# Since you want to forecast 48 hours ahead (192 fifteen-minute intervals), take this into account when splitting\n",
700
+ "train, test = train_test_split(dfEnergyAll, train_size=len(dfEnergyAll)-192)\n",
701
+ "\n",
702
+ "# Separate the target variable (Lastgang) and the exogenous variables\n",
703
+ "y_train = train['Lastgang'] # Target variable for training\n",
704
+ "X_train = train[['StundenwertStrahlung', 'Globalstrahlung_15Min', 'StrahlungGeneigteFläche', 'TheorPVProd', 'Direktnormalstrahlung', 'Schönwetterstrahlung', 'Lufttemperatur', 'Lastgang_Moving_Average', 'Lastgang_First_Difference']] # Exogenous variables for training\n",
705
+ "# Only 1 external variable: , 'Lufttemperatur'\n",
706
+ "\n",
707
+ "y_test = test['Lastgang'] # Target variable for testing, used later for evaluation\n",
708
+ "X_test = test[['StundenwertStrahlung', 'Globalstrahlung_15Min', 'StrahlungGeneigteFläche', 'TheorPVProd', 'Direktnormalstrahlung', 'Schönwetterstrahlung', 'Lufttemperatur', 'Lastgang_Moving_Average', 'Lastgang_First_Difference']] # Exogenous variables for testing, used for predictions\n",
709
+ "# Only 1 external variable: , 'Lufttemperatur'\n",
710
+ "\n",
711
+ "# Define and fit the SARIMAX model\n",
712
+ "# SARIMAX model with ARIMA(1,0,0) and seasonal order (1,0,0,96)\n",
713
+ "model = sm.tsa.SARIMAX(y_train, exog=X_train, order=(1,0,0), seasonal_order=(1,0,0,96), enforce_stationarity=False, enforce_invertibility=False)\n",
714
+ "results = model.fit()\n",
715
+ "\n",
716
+ "# Display the model summary\n",
717
+ "print(results.summary())\n",
718
+ "\n",
719
+ "# Use the model for predictions\n",
720
+ "predictions = results.get_forecast(steps=192, exog=X_test).predicted_mean # Forecasting using the model\n"
721
+ ]
722
+ },
723
+ {
724
+ "cell_type": "code",
725
+ "execution_count": null,
726
+ "id": "ecc374bc-c312-4400-915d-96707c87ae49",
727
+ "metadata": {},
728
+ "outputs": [],
729
+ "source": [
730
+ "# Step 5 - Rescaling, error metrics and visualization\n",
731
+ "\n",
732
+ "# Rescale predictions and review\n",
733
+ "predictions_original_scale = lastgang_scaler.inverse_transform(predictions.to_numpy().reshape(-1, 1)) # Rescale the predictions to the original scale\n",
734
+ "print(\"After rescaling (first 5 values):\", predictions_original_scale[:5]) # Print the first 5 rescaled prediction values\n",
735
+ "\n",
736
+ "# Rescale actual values and review\n",
737
+ "y_test_original_scale = lastgang_scaler.inverse_transform(y_test.to_numpy().reshape(-1, 1)) # Rescale the actual values to the original scale\n",
738
+ "print(\"Actual values after rescaling (first 5 values):\", y_test_original_scale[:5]) # Print the first 5 rescaled actual values"
739
+ ]
740
+ },
741
+ {
742
+ "cell_type": "code",
743
+ "execution_count": null,
744
+ "id": "d272a1e2-4f7e-4840-81ed-0a1f5a462d46",
745
+ "metadata": {},
746
+ "outputs": [],
747
+ "source": [
748
+ "# Step 5 - Rescaling, error metrics and visualization\n",
749
+ "\n",
750
+ "# Create a series for SARIMAX forecast results with timestamps\n",
751
+ "sarimax_forecast_series = pd.Series(predictions, index=y_test.index) # Initially store predictions in a series with the test set's timestamps\n",
752
+ "\n",
753
+ "# Create a series for SARIMAX forecast results with timestamps using rescaled predictions\n",
754
+ "sarimax_forecast_series = pd.Series(predictions_original_scale.ravel(), index=y_test.index) # Use rescaled predictions for the series\n",
755
+ "\n",
756
+ "# Calculate error metrics for SARIMAX model predictions\n",
757
+ "mse = mean_squared_error(y_test_original_scale, sarimax_forecast_series) # Calculate mean squared error\n",
758
+ "mae = mean_absolute_error(y_test_original_scale, sarimax_forecast_series) # Calculate mean absolute error\n",
759
+ "rmse = np.sqrt(mse) # Calculate root mean square error\n",
760
+ "mape = np.mean(np.abs((y_test_original_scale.ravel() - sarimax_forecast_series) / y_test_original_scale.ravel())) * 100 # Calculate mean absolute percentage error\n",
761
+ "\n",
762
+ "# Output error metrics for SARIMAX model predictions\n",
763
+ "print(f\"Mean Squared Error (MSE) for the last 2 days: {mse}\")\n",
764
+ "print(f\"Mean Absolute Error (MAE) for the last 2 days: {mae}\")\n",
765
+ "print(f\"Root Mean Square Error (RMSE) for the last 2 days: {rmse}\")\n",
766
+ "print(f\"Mean Absolute Percentage Error (MAPE) for the last 2 days: {mape}%\")\n",
767
+ "\n",
768
+ "# Visualization\n",
769
+ "plt.figure(figsize=(12, 6))\n",
770
+ "plt.plot(y_test.index, y_test_original_scale, label='Actual Values', color='#3E7A6F') # Plot actual values\n",
771
+ "plt.plot(sarimax_forecast_series.index, sarimax_forecast_series, label='Predicted Values', color='#7DFFE7') # Plot predicted values\n",
772
+ "plt.legend()\n",
773
+ "plt.title('Comparison of Actual and Predicted Values: SARIMAX over 48 Hours')\n",
774
+ "plt.gca().xaxis.set_major_formatter(mdates.DateFormatter('%m-%d %H:%M')) # Format the x-axis labels to show month-day and time\n",
775
+ "plt.gca().xaxis.set_major_locator(mdates.HourLocator(interval=3)) # Set major tick intervals on the x-axis\n",
776
+ "plt.gcf().autofmt_xdate() # Auto-format date labels for better readability\n",
777
+ "plt.show()"
778
+ ]
779
+ },
780
+ {
781
+ "cell_type": "code",
782
+ "execution_count": null,
783
+ "id": "40da38a3-6473-4dc2-9690-dcfa5ae27415",
784
+ "metadata": {},
785
+ "outputs": [],
786
+ "source": [
787
+ "# Step 7 - Show GPU Utilization and Memory Usage after Model Training\n",
788
+ "\n",
789
+ "#print(\"GPU after model training:\", datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")) # Print GPU utilization after model training\n",
790
+ "#print_gpu_utilization() # Call function to print GPU utilization\n",
791
+ "\n",
792
+ "print(\"CPU after model training:\") # Print CPU utilization after model training\n",
793
+ "print_cpu_utilization() # Call function to print CPU utilization\n",
794
+ "\n",
795
+ "print(\"RAM after model training:\") # Print RAM usage after model training\n",
796
+ "print_memory_usage() # Call function to print RAM usage"
797
+ ]
798
+ }
799
+ ],
800
+ "metadata": {
801
+ "kernelspec": {
802
+ "display_name": "Python 3 (ipykernel)",
803
+ "language": "python",
804
+ "name": "python3"
805
+ },
806
+ "language_info": {
807
+ "codemirror_mode": {
808
+ "name": "ipython",
809
+ "version": 3
810
+ },
811
+ "file_extension": ".py",
812
+ "mimetype": "text/x-python",
813
+ "name": "python",
814
+ "nbconvert_exporter": "python",
815
+ "pygments_lexer": "ipython3",
816
+ "version": "3.11.8"
817
+ }
818
+ },
819
+ "nbformat": 4,
820
+ "nbformat_minor": 5
821
+ }