Wichtiger Hinweis: Dieses Notebook demonstriert das Feature Engineering zu Lern- und Demonstrationszwecken durch manuelle Programmierung. In der Praxis könnten sämtliche Schritte der Datenaufbereitung auch mit etablierten Bibliotheken durchgeführt werden:
MinMaxScaler, StandardScaler)train_test_split aus scikit-learnDie manuelle Implementierung hier erfolgt bewusst, um die einzelnen Schritte transparent und nachvollziehbar zu machen und ein tieferes Verständnis der Prozesse zu vermitteln.
In diesem Abschnitt laden wir die originalen Verkaufsdaten aus einer CSV-Datei. Die Daten enthalten wöchentliche Verkaufsmengen über mehrere Jahre.
Ziel: Erste Inspektion der Rohdaten, um Muster, Trends und potenzielle Ausreißer zu identifizieren.
Wir verwenden Pandas, um die CSV-Datei zu laden. Die Datei verwendet Semikolon als Trennzeichen.
import pandas as pd
# original Daten
df_org = pd.read_csv("wochen_verkäufe.csv", header=0,delimiter=";")
von = 0
bis = len(df_org)
df_org[von:bis]
| jahr | woche | menge | |
|---|---|---|---|
| 0 | 2021 | 2 | 10 |
| 1 | 2021 | 3 | 47 |
| 2 | 2021 | 4 | 10 |
| 3 | 2021 | 5 | 11 |
| 4 | 2021 | 6 | 24 |
| ... | ... | ... | ... |
| 214 | 2025 | 14 | 100 |
| 215 | 2025 | 15 | 8 |
| 216 | 2025 | 16 | 13 |
| 217 | 2025 | 17 | 9 |
| 218 | 2025 | 18 | 12 |
219 rows × 3 columns
Ein Plot der Originalzeitreihe hilft uns, das Verkaufsverhalten zu verstehen:
%matplotlib inline
import matplotlib.pyplot as plt
# original Daten
von = 0
bis = len(df_org)
plt.plot(df_org.index[von:bis], df_org["menge"][von:bis], label="Menge")
plt.grid(True)
plt.legend()
plt.show()
print(df_org["menge"].describe())
print(sorted(df_org["menge"].unique()))
print(df_org.loc[df_org["menge"] >= 30])
count 219.000000
mean 11.853881
std 12.901057
min 1.000000
25% 7.000000
50% 10.000000
75% 12.500000
max 100.000000
Name: menge, dtype: float64
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 24, 33, 47, 50, 66, 80, 85, 92, 100]
jahr woche menge
1 2021 3 47
18 2021 20 66
25 2021 27 33
48 2021 50 50
49 2021 51 47
53 2022 3 92
145 2023 50 85
162 2024 15 80
214 2025 14 100
Die Datenaufbereitung ist ein kritischer Schritt für das Training neuronaler Netze. Ausreißer können das Modell negativ beeinflussen.
Strategie zur Ausreißerbehandlung:
df.loc[df["menge"] > grenze, "menge"] = round(df["menge"].quantile(0.75))
Laden der Daten erneut, Identifikation von Ausreißern und deren Behandlung. Der folgende Code:
import pandas as pd
df = pd.read_csv("wochen_verkäufe.csv", header=0,delimiter=";")
print(df.describe())
print("\n")
print(df.info())
print("\n")
print(sorted(df["menge"].unique()))
print(df.loc[df["menge"] >= 40])
grenze = df["menge"].mean() * 3
# new_value = df["menge"].mean() + df["menge"].std()
df.loc[df["menge"] > grenze, "menge"] = round(df["menge"].quantile(0.75))
print(df.describe())
print(sorted(df["menge"].unique()))
print(df.loc[df["menge"] >= 40])
jahr woche menge
count 219.000000 219.000000 219.000000
mean 2022.698630 25.328767 11.853881
std 1.292373 15.029990 12.901057
min 2021.000000 1.000000 1.000000
25% 2022.000000 12.000000 7.000000
50% 2023.000000 24.000000 10.000000
75% 2024.000000 38.000000 12.500000
max 2025.000000 53.000000 100.000000
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 219 entries, 0 to 218
Data columns (total 3 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 jahr 219 non-null int64
1 woche 219 non-null int64
2 menge 219 non-null int64
dtypes: int64(3)
memory usage: 5.3 KB
None
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 24, 33, 47, 50, 66, 80, 85, 92, 100]
jahr woche menge
1 2021 3 47
18 2021 20 66
48 2021 50 50
49 2021 51 47
53 2022 3 92
145 2023 50 85
162 2024 15 80
214 2025 14 100
jahr woche menge
count 219.000000 219.000000 219.000000
mean 2022.698630 25.328767 9.703196
std 1.292373 15.029990 4.402724
min 2021.000000 1.000000 1.000000
25% 2022.000000 12.000000 7.000000
50% 2023.000000 24.000000 10.000000
75% 2024.000000 38.000000 12.000000
max 2025.000000 53.000000 33.000000
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 24, 33]
Empty DataFrame
Columns: [jahr, woche, menge]
Index: []
%matplotlib inline
import matplotlib.pyplot as plt
von = 0
bis = len(df)
plt.plot(df.index[von:bis], df["menge"][von:bis], label="Menge")
plt.grid(True)
plt.legend()
plt.show()
Für die Zeitreihenvorhersage mit rekurrenten Netzen kehren wir die Reihenfolge der Daten um:
menge_before zur Vorwocheprint("Reihenfolge aus Datei")
print(df.head())
df = df[::-1] # für LSTM
df.reset_index(drop=True,inplace=True)
df = df.dropna()
print("Reihenfolge gedreht für LSTM")
print(df.head())
Reihenfolge aus Datei jahr woche menge 0 2021 2 10 1 2021 3 12 2 2021 4 10 3 2021 5 11 4 2021 6 24 Reihenfolge gedreht für LSTM jahr woche menge 0 2025 18 12 1 2025 17 9 2 2025 16 13 3 2025 15 8 4 2025 14 12
df["menge_before"] = df["menge"].shift(-1)
df.head(10)
| jahr | woche | menge | menge_before | |
|---|---|---|---|---|
| 0 | 2025 | 18 | 12 | 9.0 |
| 1 | 2025 | 17 | 9 | 13.0 |
| 2 | 2025 | 16 | 13 | 8.0 |
| 3 | 2025 | 15 | 8 | 12.0 |
| 4 | 2025 | 14 | 12 | 13.0 |
| 5 | 2025 | 13 | 13 | 16.0 |
| 6 | 2025 | 12 | 16 | 13.0 |
| 7 | 2025 | 11 | 13 | 9.0 |
| 8 | 2025 | 10 | 9 | 13.0 |
| 9 | 2025 | 9 | 13 | 8.0 |
Das changes-Feature repräsentiert die relative Veränderung zur Vorwoche:
(menge / menge_before) / 10# Faktor für Änderung zur Vorwoche
#df["changes"] = ((df["menge"] / df["menge_before"]) -1 ) #/ 10
df["changes"] = ((df["menge"] / df["menge_before"]) / 10 )
df = df.dropna()
df.head(10)
| jahr | woche | menge | menge_before | changes | |
|---|---|---|---|---|---|
| 0 | 2025 | 18 | 12 | 9.0 | 0.133333 |
| 1 | 2025 | 17 | 9 | 13.0 | 0.069231 |
| 2 | 2025 | 16 | 13 | 8.0 | 0.162500 |
| 3 | 2025 | 15 | 8 | 12.0 | 0.066667 |
| 4 | 2025 | 14 | 12 | 13.0 | 0.092308 |
| 5 | 2025 | 13 | 13 | 16.0 | 0.081250 |
| 6 | 2025 | 12 | 16 | 13.0 | 0.123077 |
| 7 | 2025 | 11 | 13 | 9.0 | 0.144444 |
| 8 | 2025 | 10 | 9 | 13.0 | 0.069231 |
| 9 | 2025 | 9 | 13 | 8.0 | 0.162500 |
#print(df["menge_before"][4] * (df["changes"][4] * 10 +1))
changes = df["changes"]
print(len(changes))
218
Die Sliding Window Technik erstellt Trainingssequenzen aus den Zeitreihendaten:
Zeitliche Aufteilung (wichtig bei Zeitreihen!):
Die umgekehrte Reihenfolge ([::-1]) stellt sicher, dass die neuesten Werte zuerst kommen.
import numpy as np
X = []
y = []
X_train = []
y_train = []
X_test = []
y_test = []
train_offset = 30
block_size = 3
for i in range(0, len(changes) - block_size):
y.append(changes[i])
X.append(np.array(changes[i+1:i+(block_size+1)][::-1]))
for i in range(train_offset, len(changes) - block_size):
y_train.append(changes[i])
X_train.append(np.array(changes[i+1:i+(block_size+1)][::-1]))
for i in range(0, train_offset - block_size):
y_test.append(changes[i])
X_test.append(np.array(changes[i+1:i+(block_size+1)][::-1]))
X = np.array(X).reshape(-1, block_size, 1)
y = np.array(y)
X_train = np.array(X_train).reshape(-1, block_size, 1)
y_train = np.array(y_train)
X_test = np.array(X_test).reshape(-1, block_size, 1)
y_test = np.array(y_test)
print(X[0])
print(y[0])
print(X_train[0])
print(y_train[0])
print(X_test[0])
print(y_test[0])
[[0.06666667] [0.1625 ] [0.06923077]] 0.13333333333333333 [[0.10909091] [0.09166667] [0.08181818]] 0.13333333333333333 [[0.06666667] [0.1625 ] [0.06923077]] 0.13333333333333333
X.shape # type: ignore
#from sklearn.model_selection import train_test_split
#X_train, X_test, y_train, y_test = train_test_split(X, Y, test_size=0.33,shuffle=True)
(215, 3, 1)
GRU ist eine vereinfachte Variante von LSTM, die oft ähnlich gute Ergebnisse bei weniger Parametern liefert.
Hinweis: is_production = False bedeutet, wir trainieren nur auf dem Trainingsset, nicht auf allen Daten.
import keras.backend
keras.backend.clear_session()
from keras.models import Sequential
from keras.layers import GRU
from keras.layers import Dense
from keras.layers import Dropout
from keras.layers import Input
from keras.layers import LeakyReLU
from keras.callbacks import EarlyStopping
early_stopping = EarlyStopping(monitor='loss', patience=10, restore_best_weights=True)
is_production = False
model = Sequential()
model.add(Input(shape=(block_size, 1)))
model.add(GRU(block_size))
model.add(Dense(128, name="hidden001"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dropout(0.1))
model.add(Dense(64, name="hidden01"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dropout(0.1))
model.add(Dense(32, name="hidden1"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dropout(0.1))
model.add(Dense(16, name="hidden2"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dropout(0.1))
model.add(Dense(8, name="hidden3"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dense(4, name="hidden4"))
model.add(LeakyReLU(negative_slope=0.1))
model.add(Dense(1, name="out"))
model.compile(optimizer="adam", loss="mse") #, metrics=['mae','Accuracy'])
model.summary()
if (is_production):
model.fit(X, y, batch_size=8, epochs=55)
else:
model.fit(X_train, y_train, batch_size=8, epochs=100,callbacks=[early_stopping]) # epochs=55 ergab sich aus early-stopping
Model: "sequential"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ gru (GRU) │ (None, 3) │ 54 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden001 (Dense) │ (None, 128) │ 512 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu (LeakyReLU) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout (Dropout) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden01 (Dense) │ (None, 64) │ 8,256 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu_1 (LeakyReLU) │ (None, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_1 (Dropout) │ (None, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden1 (Dense) │ (None, 32) │ 2,080 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu_2 (LeakyReLU) │ (None, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_2 (Dropout) │ (None, 32) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden2 (Dense) │ (None, 16) │ 528 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu_3 (LeakyReLU) │ (None, 16) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dropout_3 (Dropout) │ (None, 16) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden3 (Dense) │ (None, 8) │ 136 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu_4 (LeakyReLU) │ (None, 8) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ hidden4 (Dense) │ (None, 4) │ 36 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ leaky_re_lu_5 (LeakyReLU) │ (None, 4) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ out (Dense) │ (None, 1) │ 5 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 11,607 (45.34 KB)
Trainable params: 11,607 (45.34 KB)
Non-trainable params: 0 (0.00 B)
Epoch 1/100
2025-11-02 18:41:55.493754: I tensorflow/core/grappler/optimizers/custom_graph_optimizer_registry.cc:117] Plugin optimizer for device_type GPU is enabled.
24/24 ━━━━━━━━━━━━━━━━━━━━ 6s 135ms/step - loss: 0.0195 Epoch 2/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 103ms/step - loss: 0.0160 Epoch 3/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 105ms/step - loss: 0.0161 Epoch 4/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 103ms/step - loss: 0.0148 Epoch 5/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 104ms/step - loss: 0.0147 Epoch 6/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 94ms/step - loss: 0.0151 Epoch 7/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 109ms/step - loss: 0.0155 Epoch 8/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 111ms/step - loss: 0.0145 Epoch 9/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 107ms/step - loss: 0.0141 Epoch 10/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 105ms/step - loss: 0.0142 Epoch 11/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 122ms/step - loss: 0.0138 Epoch 12/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 114ms/step - loss: 0.0133 Epoch 13/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 106ms/step - loss: 0.0123 Epoch 14/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 114ms/step - loss: 0.0121 Epoch 15/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 107ms/step - loss: 0.0106 Epoch 16/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 108ms/step - loss: 0.0154 Epoch 17/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 103ms/step - loss: 0.0107 Epoch 18/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 98ms/step - loss: 0.0123 Epoch 19/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 94ms/step - loss: 0.0123 Epoch 20/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 126ms/step - loss: 0.0122 Epoch 21/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 114ms/step - loss: 0.0134 Epoch 22/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 102ms/step - loss: 0.0100 Epoch 23/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 98ms/step - loss: 0.0085 Epoch 24/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 126ms/step - loss: 0.0096 Epoch 25/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 104ms/step - loss: 0.0105 Epoch 26/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 104ms/step - loss: 0.0107 Epoch 27/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 102ms/step - loss: 0.0098 Epoch 28/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 106ms/step - loss: 0.0075 Epoch 29/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 96ms/step - loss: 0.0164 Epoch 30/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 109ms/step - loss: 0.0134 Epoch 31/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 131ms/step - loss: 0.0061 Epoch 32/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 116ms/step - loss: 0.0077 Epoch 33/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 102ms/step - loss: 0.0067 Epoch 34/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 105ms/step - loss: 0.0108 Epoch 35/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 104ms/step - loss: 0.0064 Epoch 36/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 3s 104ms/step - loss: 0.0097 Epoch 37/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 93ms/step - loss: 0.0139 Epoch 38/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 97ms/step - loss: 0.0126 Epoch 39/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 95ms/step - loss: 0.0064 Epoch 40/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 95ms/step - loss: 0.0085 Epoch 41/100 24/24 ━━━━━━━━━━━━━━━━━━━━ 2s 94ms/step - loss: 0.0076
print(model.evaluate(X_test, y_test))
print(model.evaluate(X_train, y_train))
print(model.evaluate(X, y))
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 188ms/step - loss: 0.0023 0.0022551948204636574 6/6 ━━━━━━━━━━━━━━━━━━━━ 0s 20ms/step - loss: 0.0065 0.006532527506351471 7/7 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - loss: 0.0059 0.005915033631026745
Jetzt verwenden wir das trainierte GRU-Modell, um Vorhersagen auf allen Daten zu machen.
model.predict(X) gibt die vorhergesagten changes-Werte zurückpredictions = model.predict(X)
predictions.shape
7/7 ━━━━━━━━━━━━━━━━━━━━ 0s 19ms/step
(215, 1)
print(predictions)
[[0.09813829] [0.08790225] [0.13468355] [0.09812401] [0.09625456] [0.08980297] [0.0886924 ] [0.09766522] [0.08615153] [0.09731602] [0.09098737] [0.09328337] [0.09612396] [0.09070449] [0.09431577] [0.08779415] [0.0839823 ] [0.1501984 ] [0.09753084] [0.08095044] [0.10007307] [0.08282965] [0.08382986] [0.0890993 ] [0.07591484] [0.46153292] [0.09334129] [0.08876219] [0.11034367] [0.091677 ] [0.10526877] [0.09826311] [0.09724215] [0.10623837] [0.08502066] [0.07648113] [0.09334676] [0.1392675 ] [0.09137205] [0.09011222] [0.09026998] [0.13817564] [0.09323836] [0.09789144] [0.11692637] [0.08035758] [0.07578386] [0.07262777] [0.15037346] [0.08316055] [0.11562733] [0.08483283] [0.07637224] [0.18801594] [0.09272344] [0.0878893 ] [0.0979758 ] [0.09604811] [0.12101276] [0.09664869] [0.09123904] [0.09205766] [0.09437017] [0.09241752] [0.08487571] [0.09112975] [0.07946242] [0.09677161] [0.07618962] [0.09806639] [0.07716269] [0.07498149] [0.06987651] [0.8557852 ] [0.15330812] [0.10865845] [0.09778069] [0.10838339] [0.09568754] [0.09285083] [0.09314213] [0.09393417] [0.08752847] [0.11662638] [0.09123904] [0.09319563] [0.09433337] [0.09385673] [0.07646156] [0.10185117] [0.08577968] [0.07648061] [0.14224404] [0.09538481] [0.09143222] [0.10919128] [0.07654379] [0.0939876 ] [0.08594742] [0.1249598 ] [0.08567232] [0.08311823] [0.14170094] [0.09345919] [0.08288924] [0.08881554] [0.07756311] [0.13947727] [0.08797656] [0.08377407] [0.08773283] [0.07502352] [0.07452963] [0.09481268] [0.09405357] [0.07601722] [0.09832951] [0.11285783] [0.15455991] [0.08784886] [0.09042952] [0.14701891] [0.12919366] [0.12418304] [0.12070633] [0.12529948] [0.09726508] [0.10106568] [0.12437459] [0.09100367] [0.08728938] [0.09541513] [0.09848803] [0.09436902] [0.08849856] [0.09803865] [0.09152956] [0.09116653] [0.09616092] [0.15361932] [0.09138961] [0.08754236] [0.07578711] [0.15509121] [0.08967691] [0.08663513] [0.09698086] [0.09545711] [0.09623728] [0.08219852] [0.13689105] [0.09784909] [0.09603292] [0.08698736] [0.07907201] [0.07599338] [0.23256454] [0.12502968] [0.08849856] [0.0972493 ] [0.09149289] [0.09042952] [0.13837439] [0.08818066] [0.0761607 ] [0.07432958] [0.63080615] [0.09441724] [0.09038205] [0.08193275] [0.12672128] [0.08956105] [0.09118912] [0.08790939] [0.13273695] [0.0877209 ] [0.11734685] [0.09005768] [0.14043845] [0.09635933] [0.08526149] [0.07573691] [0.07392338] [0.69957477] [0.13333629] [0.14131862] [0.09829878] [0.09256272] [0.09242696] [0.09764808] [0.09397329] [0.09265485] [0.07608499] [0.11428607] [0.09373078] [0.09409864] [0.08674458] [0.10033745] [0.09740625] [0.0961329 ] [0.08749868] [0.09728593] [0.092214 ] [0.09664247] [0.08053426] [0.09607401] [0.09826829] [0.08538634] [0.16894531] [0.09714226] [0.09269595] [0.08034411] [0.09909739] [0.07654618] [0.09465578]]
predictions = predictions.reshape(-1)
print(predictions.shape)
(215,)
predictions = np.append(predictions, np.zeros(len(df) - predictions.shape[0]))
predictions.shape
(218,)
df["predictions"] = predictions
Das Modell sagt die normierten changes-Werte vorher. Um zurück zu den tatsächlichen Verkaufsmengen zu kommen:
menge_predicted = menge_before × (10 × predictions)Formel erklärt:
predictions ist der vorhergesagte normierte changes-Wertmenge_before berechnet die absolute Mengedf["menge_predicted"] = df["menge_before"] * (10 * df["predictions"])
%matplotlib inline
import matplotlib.pyplot as plt
uebereinander = True
scatter = False
df_plot = df[::-1]
df_plot.reset_index(drop=True,inplace=True)
von = 100
bis = len(df_plot)
if scatter:
plt.scatter(df_plot.index[von:bis], df_plot["menge"][von:bis], label="Menge")
else:
plt.plot(df_plot.index[von:bis], df_plot["menge"][von:bis], label="Menge")
if uebereinander:
if scatter:
plt.scatter(df_plot.index[von:bis], df_plot["menge_predicted"].shift(-1)[von:bis], label="Menge (predicted-scatter)")
else:
plt.plot(df_plot.index[von:bis], df_plot["menge_predicted"].shift(-1)[von:bis], label="Menge (predicted)")
else:
if scatter:
plt.scatter(df_plot.index[von:bis], df_plot["menge_predicted"][von:bis], label="Menge (predicted-scatter)")
else:
plt.plot(df_plot.index[von:bis], df_plot["menge_predicted"][von:bis], label="Menge (predicted)")
plt.grid(True)
plt.legend()
plt.show()
Wichtige Beobachtung zur Modellqualität:
train_offset = 30) wurden nicht im Training verwendetprint(df_plot)
jahr woche menge menge_before changes predictions menge_predicted 0 2021 3 12 10.0 0.120000 0.000000 0.000000 1 2021 4 10 12.0 0.083333 0.000000 0.000000 2 2021 5 11 10.0 0.110000 0.000000 0.000000 3 2021 6 24 11.0 0.218182 0.094656 10.412136 4 2021 7 9 24.0 0.037500 0.076546 18.371084 .. ... ... ... ... ... ... ... 213 2025 14 12 13.0 0.092308 0.096255 12.513092 214 2025 15 8 12.0 0.066667 0.098124 11.774881 215 2025 16 13 8.0 0.162500 0.134684 10.774684 216 2025 17 9 13.0 0.069231 0.087902 11.427292 217 2025 18 12 9.0 0.133333 0.098138 8.832446 [218 rows x 7 columns]
df.head(6)
| jahr | woche | menge | menge_before | changes | predictions | menge_predicted | |
|---|---|---|---|---|---|---|---|
| 0 | 2025 | 18 | 12 | 9.0 | 0.133333 | 0.098138 | 8.832446 |
| 1 | 2025 | 17 | 9 | 13.0 | 0.069231 | 0.087902 | 11.427292 |
| 2 | 2025 | 16 | 13 | 8.0 | 0.162500 | 0.134684 | 10.774684 |
| 3 | 2025 | 15 | 8 | 12.0 | 0.066667 | 0.098124 | 11.774881 |
| 4 | 2025 | 14 | 12 | 13.0 | 0.092308 | 0.096255 | 12.513092 |
| 5 | 2025 | 13 | 13 | 16.0 | 0.081250 | 0.089803 | 14.368474 |
for i in range(0, len(changes) - block_size):
Y.append(changes[i])
X.append(np.array(changes[i+1:i+block_size][::-1]))
print("erster trainierter Block aus X_train: " + str([ i[0] for i in X_train[0]]))
print("erstes y_train: " + str(y_train[0]) + "\n")
print("nicht beim Training gesehene Daten:\n")
print("erster Block aus X: " + str([ i[0] for i in X[0]]))
print("erstes y: " + str(y[0]) + "\n")
print("\033[1moberster Block für die Prediction:\033[92m " + str([ i for i in df["changes"][0:block_size][::-1]]) + "\n")
df["changes"][0:10]
erster trainierter Block aus X_train: [0.10909090909090909, 0.09166666666666666, 0.08181818181818182] erstes y_train: 0.13333333333333333 nicht beim Training gesehene Daten: erster Block aus X: [0.06666666666666667, 0.1625, 0.06923076923076923] erstes y: 0.13333333333333333 oberster Block für die Prediction: [0.1625, 0.06923076923076923, 0.13333333333333333]
0 0.133333 1 0.069231 2 0.162500 3 0.066667 4 0.092308 5 0.081250 6 0.123077 7 0.144444 8 0.069231 9 0.162500 Name: changes, dtype: float64
print("\033[92mLetzte bekannte VK-Menge: " + str(df["menge"][0]))
Letzte bekannte VK-Menge: 12
### df["menge_predicted"] = df["menge_before"] * (1 + df["predictions"])
pred_changes = df["changes"][0:block_size][::-1]
pred_X = np.array(pred_changes).reshape(1,block_size,1)
prediction_X = model.predict(pred_X)
print(prediction_X)
# Letze Menge
menge_last = df["menge"][0]
naechste_woche = df["woche"][0] + 1
#menge_p = menge_last * (1 + prediction_X)
menge_p = menge_last * (10 * prediction_X)
menge_p = round(menge_p.reshape(-1)[0])
print(menge_p)
print()
print("\033[1m Voraussage für Menge nächste Woche, Kalenderwoche(" + str(naechste_woche) + "):\033[1m \033[92m" + str(menge_p))
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 46ms/step [[0.09053151]] 11 Voraussage für Menge nächste Woche, Kalenderwoche(19): 11
#pred_changes.shape
pred_changes = pred_changes[0:block_size-1]
#new_change = ((menge_p / menge_last) -1) #/ 10 # genau wie für Training: df["changes"] = ((df["menge"] / df["menge_before"]) -1 ) / 10
new_change = ((menge_p / menge_last) / 10) #/ 10 # genau wie für Training: df["changes"] = ((df["menge"] / df["menge_before"]) -1 ) / 10
pred_changes = pd.concat([pred_changes, pd.Series([new_change])], ignore_index=True)
pred_X = np.array(pred_changes).reshape(1,block_size,1)
prediction_X = model.predict(pred_X)
print(prediction_X)
menge_last = menge_p
menge_p = menge_last * (10 * prediction_X)
menge_p = round(menge_p.reshape(-1)[0])
print(menge_p)
print()
print("\033[1m Voraussage für Menge übernächste Woche, Kalenderwoche(" + str(naechste_woche +1) + "):\033[1m \033[92m" + str(menge_p))
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 23ms/step [[0.09792395]] 11 Voraussage für Menge übernächste Woche, Kalenderwoche(20): 11
import pandas as pd
#df_bestand = pd.read_csv("pzn_bestand_name.csv", header=0,delimiter=";")
#bestand_akt = float(df_bestand[df_bestand['pzn'] == 8628264]['bestand'])
bestand_akt = 58
bedarf_zwei_wochen = menge_last + menge_p
print("\033[1m Voraussichtlicher Bedarf für die nächsten zwei Wochen:\033[1m \033[92m" + str(bedarf_zwei_wochen))
Voraussichtlicher Bedarf für die nächsten zwei Wochen: 22
print("\033[1m Aktueller Bestand:\033[1m " + "\033[92m" + str(bestand_akt))
print("\033[0m\033[1m Voraussichtlicher Bedarf für die nächsten zwei Wochen:\033[1m " + "\033[92m" +str(bedarf_zwei_wochen))
if bestand_akt >= bedarf_zwei_wochen:
if bestand_akt > (bedarf_zwei_wochen * 2):
print("\033[0m\033[1m\033[4m Bestand ist mehr als doppelt so hoch wie der Bedarf!\033[1m \033[93m Bestellrythmus überprüfen! " )
else:
print("\033[92m\033[1m Kein Handlungsbedarf!\033[1m " )
else:
print("\033[93m\033[1m Bestellung notwendig: \033[1m \033[92m" + str(bedarf_zwei_wochen - bestand_akt))
Aktueller Bestand: 58 Voraussichtlicher Bedarf für die nächsten zwei Wochen: 22 Bestand ist mehr als doppelt so hoch wie der Bedarf! Bestellrythmus überprüfen!
class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKCYAN = '\033[96m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m'