Aliazimi00 commited on
Commit
405d0e9
·
verified ·
1 Parent(s): 0860e92

Delete core/plot.py

Browse files
Files changed (1) hide show
  1. core/plot.py +0 -377
core/plot.py DELETED
@@ -1,377 +0,0 @@
1
- import matplotlib.pyplot as plt
2
- import seaborn as sns
3
- import pandas as pd
4
- import numpy as np
5
- import networkx as nx
6
-
7
-
8
- def plot_forecast(result):
9
- """Modified to show only last day backtest comparison"""
10
- forecast = result["forecast"]
11
- actual = result["actual"]
12
- next_pred = result.get("latest_prediction", [])
13
-
14
- # Convert to numpy arrays and flatten if needed
15
- forecast = np.array(forecast).flatten()
16
- actual = np.array(actual).flatten()
17
-
18
- # Ensure both arrays have the same length
19
- min_len = min(len(forecast), len(actual))
20
- forecast = forecast[:min_len]
21
- actual = actual[:min_len]
22
-
23
- fig, ax = plt.subplots(figsize=(10, 6))
24
-
25
- if len(actual) == 0 or len(forecast) == 0:
26
- ax.text(0.5, 0.5, "No data available for plotting", ha='center', va='center')
27
- ax.set_title("Forecast vs Actual")
28
- return fig
29
-
30
- # Create time indices
31
- time_indices = np.arange(len(actual))
32
-
33
- # Plot most of the data normally
34
- if len(actual) > 1:
35
- ax.plot(time_indices[:-1], actual[:-1], label="Historical", color="blue", linewidth=2, alpha=0.7)
36
- ax.plot(time_indices[:-1], forecast[:-1], label="Historical Forecast", color="orange", linestyle="--", linewidth=1, alpha=0.7)
37
-
38
- # Highlight only the last day comparison (backtest)
39
- if len(actual) > 1 and len(forecast) > 1:
40
- last_idx = len(actual) - 1
41
-
42
- # Create line segments for last day
43
- last_actual_segment = [float(actual[last_idx-1]), float(actual[last_idx])]
44
- last_forecast_segment = [float(forecast[last_idx-1]), float(forecast[last_idx])]
45
- last_time_segment = [time_indices[last_idx-1], time_indices[last_idx]]
46
-
47
- ax.plot(last_time_segment, last_actual_segment,
48
- color="blue", linewidth=3, label="Last Day Actual")
49
- ax.plot(last_time_segment, last_forecast_segment,
50
- color="red", linewidth=3, linestyle="--", label="Last Day Predicted")
51
-
52
- # Add markers for the last day comparison
53
- ax.scatter([last_idx], [float(actual[last_idx])], color="blue", s=100, zorder=5)
54
- ax.scatter([last_idx], [float(forecast[last_idx])], color="red", s=100, zorder=5)
55
-
56
- # Add value annotations for last day
57
- actual_val = float(actual[last_idx])
58
- forecast_val = float(forecast[last_idx])
59
- ax.annotate(f'Actual: {actual_val:.2f}',
60
- xy=(last_idx, actual_val),
61
- xytext=(10, 10), textcoords='offset points',
62
- bbox=dict(boxstyle='round,pad=0.3', facecolor='blue', alpha=0.7, edgecolor='none'),
63
- color='white', fontsize=9, fontweight='bold')
64
- ax.annotate(f'Predicted: {forecast_val:.2f}',
65
- xy=(last_idx, forecast_val),
66
- xytext=(10, -20), textcoords='offset points',
67
- bbox=dict(boxstyle='round,pad=0.3', facecolor='red', alpha=0.7, edgecolor='none'),
68
- color='white', fontsize=9, fontweight='bold')
69
- elif len(actual) == 1:
70
- # Handle single point case
71
- ax.scatter([0], [float(actual[0])], color="blue", s=100, label="Actual", zorder=5)
72
- ax.scatter([0], [float(forecast[0])], color="red", s=100, label="Predicted", zorder=5)
73
-
74
- ax.legend()
75
- ax.set_title("Forecast vs Actual (Last Day Backtest Highlighted)")
76
- ax.set_xlabel("Time Index")
77
- ax.set_ylabel("Value")
78
- ax.grid(True, alpha=0.3)
79
- plt.tight_layout()
80
- return fig
81
-
82
-
83
- def plot_future_forecast(df, result, future_df):
84
- """Enhanced to show trend lines for predictions"""
85
- fig, ax = plt.subplots(figsize=(12, 7))
86
-
87
- # Validate and convert data
88
- if df.empty or 'Date' not in df.columns or 'value' not in df.columns:
89
- ax.text(0.5, 0.5, "No valid historical data available", ha='center', va='center')
90
- ax.set_title("Stock Prediction: Historical Data → Future Forecast")
91
- return fig
92
-
93
- # Plot historical data
94
- dates = pd.to_datetime(df['Date'])
95
- values = np.array(df['value']).flatten()
96
- ax.plot(dates, values, label="Historical Data", color="blue", linewidth=2)
97
-
98
- if "latest_prediction" in result and len(result["latest_prediction"]) > 0:
99
- # Convert predictions to flat array
100
- predictions = np.array(result["latest_prediction"]).flatten()
101
-
102
- # Create future dates
103
- last_date = dates.iloc[-1] if len(dates) > 0 else pd.Timestamp.now()
104
- horizon = len(predictions)
105
-
106
- try:
107
- future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq='B')
108
- except:
109
- # Fallback to daily frequency if business day fails
110
- future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq='D')
111
-
112
- if len(values) > 0 and len(predictions) > 0:
113
- # Create connection from last historical point to first prediction
114
- connection_dates = [last_date, future_dates[0]]
115
- connection_values = [float(values[-1]), float(predictions[0])]
116
- ax.plot(connection_dates, connection_values, color="orange", linestyle=":", linewidth=1, alpha=0.7)
117
-
118
- # Plot forecast with fine connecting lines
119
- predictions_float = [float(p) for p in predictions]
120
- ax.plot(future_dates, predictions_float,
121
- label="Forecast", color="orange", linewidth=2.5,
122
- linestyle="-", marker='o', markersize=6, markerfacecolor='orange',
123
- markeredgecolor='darkorange', markeredgewidth=1)
124
-
125
- # Add value annotations for predictions
126
- for i, val in enumerate(predictions_float):
127
- ax.annotate(f"{val:.2f}",
128
- xy=(future_dates[i], val),
129
- xytext=(0, 15), textcoords='offset points',
130
- ha='center', va='bottom',
131
- bbox=dict(boxstyle='round,pad=0.2', facecolor='orange', alpha=0.8, edgecolor='darkorange'),
132
- color='white', fontsize=8, fontweight='bold')
133
-
134
- # Plot actual future values if available
135
- if not future_df.empty and "future_actuals" in result and 'Date' in future_df.columns and 'value' in future_df.columns:
136
- actual_future_dates = pd.to_datetime(future_df['Date'])
137
- actual_future_values = np.array(future_df['value']).flatten()
138
- actual_future_values_float = [float(v) for v in actual_future_values]
139
-
140
- # Connect to actual future values
141
- ax.plot(actual_future_dates, actual_future_values_float,
142
- label="Actual Future", color="green", linewidth=2.5,
143
- linestyle="-", marker='s', markersize=6, markerfacecolor='green',
144
- markeredgecolor='darkgreen', markeredgewidth=1)
145
-
146
- # Add value annotations for actuals
147
- for i, val in enumerate(actual_future_values_float):
148
- date_val = actual_future_dates.iloc[i] if hasattr(actual_future_dates, 'iloc') else actual_future_dates[i]
149
- ax.annotate(f"{val:.2f}",
150
- xy=(date_val, val),
151
- xytext=(0, -20), textcoords='offset points',
152
- ha='center', va='top',
153
- bbox=dict(boxstyle='round,pad=0.2', facecolor='green', alpha=0.8, edgecolor='darkgreen'),
154
- color='white', fontsize=8, fontweight='bold')
155
-
156
- ax.legend(loc='upper left')
157
- ax.set_title("Stock Prediction: Historical Data → Future Forecast", fontsize=14, fontweight='bold')
158
- ax.set_xlabel("Date", fontsize=12)
159
- ax.set_ylabel("Stock Price", fontsize=12)
160
- ax.grid(True, alpha=0.3)
161
-
162
- # Improve date formatting on x-axis
163
- fig.autofmt_xdate(rotation=45)
164
- plt.tight_layout()
165
- return fig
166
-
167
-
168
- def plot_metrics_precision(result):
169
- fig, ax = plt.subplots(figsize=(8, 5))
170
- metrics = {k: v for k, v in result['metrics'].items() if k in ['R² (%)', 'Explained Variance (%)', 'MDA (%)'] and v is not None}
171
- if not metrics:
172
- ax.text(0.5, 0.5, "No valid precision metrics available", ha='center', va='center')
173
- ax.set_title("Precision Metrics (Model Accuracy)")
174
- return fig
175
-
176
- sns.barplot(x=list(metrics.keys()), y=list(metrics.values()), ax=ax, palette="Blues_d")
177
- for i, v in enumerate(metrics.values()):
178
- ax.text(i, v + 0.01 * max(metrics.values(), default=100), f"{v:.2f}%", ha='center', va='bottom', fontsize=10)
179
-
180
- max_val = max(metrics.values(), default=100)
181
- min_val = min(metrics.values(), default=0)
182
- ax.set_ylim(min(min_val - 5, -10), max_val + 10)
183
- ax.set_ylabel("Value (%)")
184
- ax.set_title("Precision Metrics (Model Accuracy)")
185
- ax.grid(True, axis='y')
186
- plt.tight_layout()
187
- return fig
188
-
189
-
190
- def plot_metrics_risk(result):
191
- fig, ax = plt.subplots(figsize=(8, 5))
192
- metrics = {k: v for k, v in result['metrics'].items() if k in ['RMSE', 'MAE', 'MAPE (%)', 'MASE'] and v is not None}
193
- if not metrics:
194
- ax.text(0.5, 0.5, "No valid risk metrics available", ha='center', va='center')
195
- ax.set_title("Risk Metrics (Error Magnitude)")
196
- return fig
197
-
198
- sns.barplot(x=list(metrics.keys()), y=list(metrics.values()), ax=ax, palette="Reds_d")
199
- for i, v in enumerate(metrics.values()):
200
- ax.text(i, v + 0.01 * max(metrics.values(), default=1), f"{v:.2f}", ha='center', va='bottom', fontsize=10)
201
-
202
- max_val = max(metrics.values(), default=1)
203
- ax.set_ylim(0, max_val + 0.2 * max_val)
204
- ax.set_ylabel("Value")
205
- ax.set_title("Risk Metrics (Error Magnitude)")
206
- ax.grid(True, axis='y')
207
- plt.tight_layout()
208
- return fig
209
-
210
-
211
- def plot_loss_curve(result):
212
- train_losses = result.get('train_loss', [])
213
- val_losses = result.get('val_loss', [])
214
- fig, ax = plt.subplots(figsize=(8, 5))
215
- ax.plot(train_losses, label='Train Loss', color='blue', linewidth=2)
216
- if val_losses:
217
- ax.plot(val_losses, label='Validation Loss', color='orange', linewidth=2)
218
- ax.set_title('Training and Validation Loss Curve')
219
- ax.set_xlabel('Epoch')
220
- ax.set_ylabel('Loss (MSE)')
221
- ax.legend()
222
- ax.grid(True)
223
- plt.tight_layout()
224
- return fig
225
-
226
-
227
- def plot_model_architecture(result):
228
- fig, ax = plt.subplots(figsize=(10, 6))
229
- ax.axis('off')
230
- G = nx.DiGraph()
231
-
232
- if "architecture" not in result:
233
- ax.text(0.5, 0.5, "No architecture details available", ha='center', va='center', fontsize=12)
234
- ax.set_title("Model Architecture")
235
- plt.tight_layout()
236
- return fig
237
-
238
- arch = result["architecture"]
239
- model_name = arch["model_name"]
240
- num_layers = arch["num_layers"]
241
- hidden_units = arch["hidden_units"]
242
- dropout = arch["dropout"]
243
- batch_size = arch["batch_size"]
244
- input_size = arch["input_size"]
245
- output_size = arch["output_size"]
246
-
247
- # Handle model-specific hidden units for visualization
248
- if model_name == "MLPModel":
249
- hidden_nodes = min(hidden_units[0], 5) # Use first hidden layer size
250
- units_label = f"{hidden_units[0]},{hidden_units[1]}" # Show both sizes
251
- elif model_name == "CNNModel":
252
- hidden_nodes = 5 # Simplified for visualization
253
- units_label = f"{hidden_units} filters" # num_filters
254
- elif model_name == "TransformerModel":
255
- hidden_nodes = min(hidden_units, 5) # d_model
256
- units_label = f"{hidden_units}"
257
- else:
258
- hidden_nodes = min(hidden_units, 5) # hidden_size
259
- units_label = f"{hidden_units}"
260
-
261
- # Simplified block diagram for complex models
262
- if model_name in ["CNNModel", "HybridModel", "CNN_GRU"]:
263
- G = nx.DiGraph()
264
- pos = {}
265
- nodes = []
266
- y_pos = 0.5
267
- layer_width = 1.0 / 4 # Fixed for 3–4 blocks
268
-
269
- # Define components for each complex model
270
- if model_name == "CNNModel":
271
- components = [
272
- ("Input", f"{input_size} units"),
273
- ("Conv1D", f"{hidden_units} filters"),
274
- ("MaxPool", ""),
275
- ("Output", f"{output_size} units")
276
- ]
277
- elif model_name == "HybridModel":
278
- components = [
279
- ("Input", f"{input_size} units"),
280
- ("Conv1D", "32 filters"),
281
- (f"BiLSTM ({num_layers} layers)", f"{hidden_units*2} units"),
282
- ("Output", f"{output_size} units")
283
- ]
284
- elif model_name == "CNN_GRU":
285
- components = [
286
- ("Input", f"{input_size} units"),
287
- ("Conv1D", "32 filters"),
288
- (f"GRU ({num_layers} layers)", f"{hidden_units} units"),
289
- ("Output", f"{output_size} units")
290
- ]
291
-
292
- # Add nodes and positions
293
- for i, (comp, label) in enumerate(components):
294
- G.add_node(comp, layer=comp)
295
- pos[comp] = (i * layer_width, y_pos)
296
- nodes.append([comp])
297
- if i > 0:
298
- G.add_edge(components[i-1][0], comp)
299
-
300
- # Draw the graph
301
- nx.draw(G, pos, ax=ax, with_labels=False, node_color='lightblue', edge_color='gray',
302
- node_size=2000, node_shape='s', arrowsize=10)
303
-
304
- # Add labels for each block
305
- for node, (x, y) in pos.items():
306
- label = [comp[1] for comp in components if comp[0] == node][0]
307
- ax.text(x, y + 0.05, f"{node}\n{label}", ha='center', va='bottom', fontsize=8,
308
- bbox=dict(facecolor='white', alpha=0.8, edgecolor='black'))
309
-
310
- else:
311
- # Node-based graph for RNN, LSTM, GRU, BiLSTM, MLP, Transformer
312
- max_nodes_display = 5
313
- input_nodes = min(input_size, max_nodes_display)
314
- output_nodes = min(output_size, max_nodes_display)
315
-
316
- nodes = []
317
- pos = {}
318
- layer_width = 1.0 / (num_layers + 2)
319
- y_pos = 0.5
320
-
321
- # Input layer
322
- for i in range(input_nodes):
323
- node = f"input_{i}"
324
- G.add_node(node, layer="input")
325
- pos[node] = (0, y_pos + (i - input_nodes / 2) * 0.1)
326
- nodes.append([f"input_{i}" for i in range(input_nodes)])
327
-
328
- # Hidden layers
329
- for layer in range(num_layers):
330
- layer_nodes = []
331
- for i in range(hidden_nodes):
332
- node = f"hidden_{layer}_{i}"
333
- G.add_node(node, layer=f"hidden_{layer+1}")
334
- pos[node] = ((layer + 1) * layer_width, y_pos + (i - hidden_nodes / 2) * 0.1)
335
- layer_nodes.append(node)
336
- nodes.append(layer_nodes)
337
-
338
- # Output layer
339
- output_layer_nodes = []
340
- for i in range(output_nodes):
341
- node = f"output_{i}"
342
- G.add_node(node, layer="output")
343
- pos[node] = ((num_layers + 1) * layer_width, y_pos + (i - output_nodes / 2) * 0.1)
344
- output_layer_nodes.append(node)
345
- nodes.append(output_layer_nodes)
346
-
347
- # Add edges between layers
348
- for layer in range(len(nodes) - 1):
349
- for src in nodes[layer]:
350
- for dst in nodes[layer + 1]:
351
- G.add_edge(src, dst)
352
-
353
- # Draw the graph
354
- nx.draw(G, pos, ax=ax, with_labels=False, node_color='lightblue', edge_color='gray',
355
- node_size=500, arrowsize=10)
356
-
357
- # Add layer labels
358
- for node in G.nodes(data=True):
359
- layer = node[1]['layer']
360
- x, y = pos[node[0]]
361
- if layer.startswith("hidden"):
362
- label = f"Layer {layer.split('_')[1]}: {units_label} units"
363
- elif layer == "input":
364
- label = f"Input: {input_size} units"
365
- elif layer == "output":
366
- label = f"Output: {output_size} units"
367
- ax.text(x, y + 0.05, label, ha='center', va='bottom', fontsize=8)
368
-
369
- # Add model details as title and annotation
370
- title = f"{model_name} Architecture"
371
- details = f"Dropout: {dropout:.2f}\nBatch Size: {batch_size}"
372
- ax.set_title(title, fontsize=12, pad=20)
373
- ax.text(0.5, 0.05, details, ha='center', va='bottom', fontsize=10, transform=ax.transAxes,
374
- bbox=dict(facecolor='white', alpha=0.8, edgecolor='black'))
375
-
376
- plt.tight_layout()
377
- return fig