File size: 6,550 Bytes
0c89273 f19f4d4 0c89273 f19f4d4 0c89273 f19f4d4 0c89273 f19f4d4 0c89273 f19f4d4 0c89273 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 | import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import streamlit as st # Cần thiết để báo lỗi nếu cột không tồn tại
# --- HÀM 1: Biểu đồ suy giảm hiệu suất (Theo Checklist mục 5) ---
def plot_performance_degradation(df, metric_column, metric_name, color='blue'):
"""
Tạo biểu đồ đường (line plot) cho thấy một chỉ số (metric) thay đổi
như thế nào qua 5 ngày dự báo.
Args:
df (pd.DataFrame): DataFrame được tải từ 'final_5_day_results_df.csv'.
metric_column (str): Tên cột chính xác trong CSV (ví dụ: 'RMSE (Absolute Error)').
metric_name (str): Tên hiển thị đẹp cho trục Y (ví dụ: 'RMSE (Temperature °C)').
color (str): Tên màu cho đường line.
Returns:
plotly.graph_objects.Figure: Một đối tượng biểu đồ Plotly.
"""
# --- TÙY CHỈNH QUAN TRỌNG (ĐÃ SỬA) ---
# Cột chứa "Day 1", "Day 2",... là 'Horizon'
DAY_AHEAD_COLUMN = 'Horizon'
# ---------------------------
if DAY_AHEAD_COLUMN not in df.columns:
st.error(f"Lỗi plot: Không tìm thấy cột '{DAY_AHEAD_COLUMN}' trong dữ liệu. "
f"Vui lòng kiểm tra file `src/diagnostic_plots.py`.")
return go.Figure()
if metric_column not in df.columns:
st.error(f"Lỗi plot: Không tìm thấy cột '{metric_column}' trong dữ liệu. "
f"Vui lòng kiểm tra file `src/diagnostic_plots.py`.")
return go.Figure()
# --- SỬA LỖI LOGIC: Chuyển "Day 1" thành số 1 ---
# Tạo một bản copy để tránh cảnh báo
plot_df = df.copy()
# Trích xuất số từ cột 'Horizon' (ví dụ: 'Day 1' -> 1)
# và tạo cột mới 'day_num'
plot_df['day_num'] = plot_df[DAY_AHEAD_COLUMN].str.extract(r'(\d+)').astype(int)
plot_df = plot_df.sort_values(by='day_num')
# ---------------------------------------------
fig = go.Figure()
fig.add_trace(go.Scatter(
x=plot_df['day_num'], # Dùng cột số 'day_num' mới cho trục X
y=plot_df[metric_column],
mode='lines+markers',
name=metric_name,
line=dict(color=color, width=3),
marker=dict(size=8)
))
fig.update_layout(
title=f"<b>{metric_name} vs. Forecast Horizon</b>",
xaxis_title="Day Ahead (Horizon)",
yaxis_title=metric_name,
title_x=0.5, # Căn giữa tiêu đề
template="plotly_white",
xaxis = dict(tickmode = 'linear', tick0 = 1, dtick = 1) # Đảm bảo trục X là 1, 2, 3, 4, 5
)
# Nếu là R2, đặt giới hạn trục y từ 0 đến 1 cho dễ nhìn
if "R2" in metric_name or "R-squared" in metric_name:
fig.update_layout(yaxis_range=[0, 1])
return fig
# --- HÀM 2: Biểu đồ Dự báo vs. Thực tế (Theo Checklist mục 5) ---
def plot_forecast_vs_actual(y_true, y_pred, day_ahead_title):
"""
Tạo biểu đồ phân tán (scatter plot) so sánh giá trị dự báo và giá trị thực tế.
Args:
y_true (array-like): Mảng chứa các giá trị thực tế.
y_pred (array-like): Mảng chứa các giá trị dự báo.
day_ahead_title (str): Tiêu đề phụ (ví dụ: "Day 1" hoặc "Day 5").
Returns:
plotly.graph_objects.Figure: Một đối tượng biểu đồ Plotly.
"""
# Tạo DataFrame tạm thời để vẽ
plot_df = pd.DataFrame({
'Actual': y_true,
'Predicted': y_pred
})
fig = px.scatter(
plot_df,
x='Actual',
y='Predicted',
title=f"<b>Forecast vs. Actual - {day_ahead_title}</b>",
opacity=0.7,
hover_data={'Actual': ':.2f', 'Predicted': ':.2f'}
)
# Thêm đường chéo (y=x) thể hiện dự báo hoàn hảo
min_val = min(plot_df['Actual'].min(), plot_df['Predicted'].min())
max_val = max(plot_df['Actual'].max(), plot_df['Predicted'].max())
fig.add_trace(go.Scatter(
x=[min_val, max_val],
y=[min_val, max_val],
mode='lines',
name='Perfect Prediction',
line=dict(color='red', dash='dash', width=2)
))
fig.update_layout(
title_x=0.5,
xaxis_title="Actual Temperature (°C)",
yaxis_title="Predicted Temperature (°C)",
template="plotly_white"
)
return fig
# --- CÁC HÀM 3 & 4: Biểu đồ "Deep Dive" (Theo Checklist mục 5 - Tùy chọn) ---
def plot_residuals_vs_time(y_true, y_pred, dates, day_ahead_title):
"""
Tạo biểu đồ phân tán của phần dư (residuals) theo thời gian.
Args:
y_true (array-like): Mảng giá trị thực tế.
y_pred (array-like): Mảng giá trị dự báo.
dates (array-like): Mảng chứa ngày tháng tương ứng.
day_ahead_title (str): Tiêu đề phụ (ví dụ: "Day 1").
Returns:
plotly.graph_objects.Figure: Một đối tượng biểu đồ Plotly.
"""
residuals = y_true - y_pred
plot_df = pd.DataFrame({
'Date': dates,
'Residual': residuals
})
fig = px.scatter(
plot_df,
x='Date',
y='Residual',
title=f"<b>Residuals vs. Time - {day_ahead_title}</b>",
opacity=0.7
)
# Thêm đường y=0 (lỗi bằng 0)
fig.add_hline(y=0, line=dict(color='red', dash='dash', width=2))
fig.update_layout(
title_x=0.5,
yaxis_title="Residual (Actual - Predicted)",
template="plotly_white"
)
return fig
def plot_residuals_distribution(y_true, y_pred, day_ahead_title):
"""
Tạo biểu đồ histogram phân phối của phần dư (residuals).
Args:
y_true (array-like): Mảng giá trị thực tế.
y_pred (array-like): Mảng giá trị dự báo.
day_ahead_title (str): Tiêu đề phụ (ví dụ: "Day 1").
Returns:
plotly.graph_objects.Figure: Một đối tượng biểu đồ Plotly.
"""
residuals = y_true - y_pred
fig = px.histogram(
residuals,
nbins=50,
title=f"<b>Residuals Distribution - {day_ahead_title}</b>"
)
fig.update_layout(
title_x=0.5,
xaxis_title="Residual (Error)",
yaxis_title="Count",
template="plotly_white"
)
return fig |