Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -124,15 +124,15 @@ def load_css():
|
|
| 124 |
/* ===== 6. VĂN BẢN THÔNG THƯỜNG (PARAGRAPH & MARKDOWN) ===== */
|
| 125 |
/* Quy tắc này áp dụng cho văn bản st.markdown và các đoạn văn bản khác */
|
| 126 |
.stMarkdown, p, li {
|
| 127 |
-
|
| 128 |
-
|
| 129 |
}
|
| 130 |
|
| 131 |
/* SAFE DataFrame Styling */
|
| 132 |
[data-testid="stDataFrame"] {
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
}
|
| 137 |
|
| 138 |
/* ===== EXPANDERS (vẫn giữ như cũ) ===== */
|
|
@@ -257,8 +257,7 @@ def load_champion_models():
|
|
| 257 |
"Ensure the 5 .pkl files are in the 'models/' directory.")
|
| 258 |
return []
|
| 259 |
|
| 260 |
-
@st.
|
| 261 |
-
def load_performance_data(file_path="data/final_5_day_results_df.csv"):
|
| 262 |
"""Loads pre-calculated performance data for Tab 3."""
|
| 263 |
try:
|
| 264 |
df = pd.read_csv(file_path)
|
|
@@ -310,7 +309,7 @@ else:
|
|
| 310 |
|
| 311 |
# --- CRITICAL CUSTOMIZATION (Hourly Targets) ---
|
| 312 |
HOURLY_TARGET_COLS = ['target_temp_next_24h', 'target_temp_next_48h', 'target_temp_next_72h',
|
| 313 |
-
|
| 314 |
|
| 315 |
# Load models và data mới
|
| 316 |
hourly_data_df = load_hourly_data(file_path="data/final_hourly_feature_dataset.csv") # Dùng tên file features chính xác
|
|
@@ -343,7 +342,7 @@ def predict_next_24_hours(input_features: pd.DataFrame, models: dict) -> List[fl
|
|
| 343 |
# Dùng np đã được import
|
| 344 |
np.random.seed(42)
|
| 345 |
return [last_temp + 1.5 * np.sin(2 * np.pi * (h + 10) / 24) + np.random.normal(0, 0.5)
|
| 346 |
-
|
| 347 |
|
| 348 |
# Chạy mô hình Direct Hourly
|
| 349 |
for h in range(1, num_horizons + 1):
|
|
@@ -538,8 +537,8 @@ with tab2:
|
|
| 538 |
st.metric(label="Temp Yesterday (temp_lag_1)", value=f"{input_features['temp_lag_1'].iloc[0]:.1f}°C")
|
| 539 |
st.metric(label="7-Day Avg Temp (temp_roll_7d_mean)", value=f"{input_features['temp_roll_7d_mean'].iloc[0]:.1f}°C")
|
| 540 |
|
| 541 |
-
# ---
|
| 542 |
-
#
|
| 543 |
st.metric(label="7-Day Total Rainfall (precip_roll_7d_sum)", value=f"{input_features['precip_roll_7d_sum'].iloc[0]:.1f} mm")
|
| 544 |
|
| 545 |
st.metric(label="14-Day Temp Volatility (temp_roll_14d_std)", value=f"{input_features['temp_roll_14d_std'].iloc[0]:.2f}°C")
|
|
@@ -758,7 +757,8 @@ with tab4:
|
|
| 758 |
value=max_ts.date(), # Mặc định chọn ngày cuối cùng
|
| 759 |
min_value=min_ts.date(),
|
| 760 |
max_value=max_ts.date(),
|
| 761 |
-
format="YYYY-MM-DD"
|
|
|
|
| 762 |
)
|
| 763 |
|
| 764 |
# 2. Hour Selection (Chỉ show các giờ có sẵn trong ngày đã chọn)
|
|
@@ -792,7 +792,6 @@ with tab4:
|
|
| 792 |
predictions_24h = predict_next_24_hours(input_features_hourly, hourly_models_24h)
|
| 793 |
|
| 794 |
# --- TÍNH TOÁN METRIC T+24h ---
|
| 795 |
-
# T+24h là index 23 (nếu có đủ 24 giá trị)
|
| 796 |
t_plus_24h_metric_value = predictions_24h[23] if len(predictions_24h) >= 24 else (predictions_24h[-1] if predictions_24h else float('nan'))
|
| 797 |
|
| 798 |
# 2. Hiển thị Dự đoán T+24h (Tức là giờ đó ngày mai)
|
|
@@ -801,7 +800,6 @@ with tab4:
|
|
| 801 |
forecast_start_ts = latest_time_for_day + pd.Timedelta(hours=1)
|
| 802 |
|
| 803 |
# Tính các giá trị cho T+2h và T+3h
|
| 804 |
-
# T+2h là index 1; T+3h là index 2
|
| 805 |
t_plus_2h_value = predictions_24h[1] if len(predictions_24h) >= 2 else float('nan')
|
| 806 |
t_plus_3h_value = predictions_24h[2] if len(predictions_24h) >= 3 else float('nan')
|
| 807 |
|
|
@@ -820,7 +818,6 @@ with tab4:
|
|
| 820 |
# --- 1. Metric T+2h ---
|
| 821 |
with col_t2:
|
| 822 |
st.metric(
|
| 823 |
-
# SỬA: Dùng forecast_t2_ts để hiển thị giờ thực tế (+2h)
|
| 824 |
label=f"Forecast @ {forecast_t2_ts.strftime('%H:%M')} (T+2H)",
|
| 825 |
value=f"{t_plus_2h_value:.1f}°C"
|
| 826 |
)
|
|
@@ -828,7 +825,6 @@ with tab4:
|
|
| 828 |
# --- 2. Metric T+3h ---
|
| 829 |
with col_t3:
|
| 830 |
st.metric(
|
| 831 |
-
# SỬA: Dùng forecast_t3_ts để hiển thị giờ thực tế (+3h)
|
| 832 |
label=f"Forecast @ {forecast_t3_ts.strftime('%H:%M')} (T+3H)",
|
| 833 |
value=f"{t_plus_3h_value:.1f}°C"
|
| 834 |
)
|
|
@@ -836,7 +832,6 @@ with tab4:
|
|
| 836 |
# --- 3. Metric T+24h (Giữ lại để đối chiếu) ---
|
| 837 |
with col_t24:
|
| 838 |
st.metric(
|
| 839 |
-
# SỬA: Dùng forecast_t24_ts để hiển thị giờ thực tế (+24h)
|
| 840 |
label=f"Forecast @ {forecast_t24_ts.strftime('%H:%M')} (T+24H)",
|
| 841 |
value=f"{t_plus_24h_metric_value:.1f}°C"
|
| 842 |
)
|
|
@@ -851,35 +846,87 @@ with tab4:
|
|
| 851 |
delta="Peak Heat")
|
| 852 |
|
| 853 |
|
| 854 |
-
#
|
|
|
|
|
|
|
|
|
|
| 855 |
|
| 856 |
-
|
|
|
|
|
|
|
| 857 |
|
| 858 |
-
#
|
| 859 |
-
|
| 860 |
-
|
| 861 |
-
|
| 862 |
-
|
|
|
|
|
|
|
|
|
|
| 863 |
}).set_index('Time')
|
| 864 |
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
|
| 868 |
-
y=
|
| 869 |
-
mode='lines+markers',
|
| 870 |
-
|
| 871 |
-
|
| 872 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 873 |
))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 874 |
|
| 875 |
-
|
| 876 |
-
|
| 877 |
-
xaxis_title="Time",
|
| 878 |
-
|
| 879 |
-
template="plotly_white",
|
| 880 |
-
legend=dict(x=0.01, y=0.99)
|
| 881 |
)
|
| 882 |
-
st.plotly_chart(
|
|
|
|
|
|
|
| 883 |
|
| 884 |
# --- NEW GRAPH 1: RMSE Degradation Plot (Reliability) ---
|
| 885 |
st.subheader("Model Reliability: Error Degradation")
|
|
|
|
| 124 |
/* ===== 6. VĂN BẢN THÔNG THƯỜNG (PARAGRAPH & MARKDOWN) ===== */
|
| 125 |
/* Quy tắc này áp dụng cho văn bản st.markdown và các đoạn văn bản khác */
|
| 126 |
.stMarkdown, p, li {
|
| 127 |
+
color: #333333 !important; /* Xám đen, tương phản tốt trên nền sáng */
|
| 128 |
+
font-size: 1.05rem; /* Có thể thêm tùy chọn để chữ lớn hơn một chút */
|
| 129 |
}
|
| 130 |
|
| 131 |
/* SAFE DataFrame Styling */
|
| 132 |
[data-testid="stDataFrame"] {
|
| 133 |
+
border: 1px solid #CCCCCC !important;
|
| 134 |
+
border-radius: 8px !important;
|
| 135 |
+
background-color: #FFFFFF !important;
|
| 136 |
}
|
| 137 |
|
| 138 |
/* ===== EXPANDERS (vẫn giữ như cũ) ===== */
|
|
|
|
| 257 |
"Ensure the 5 .pkl files are in the 'models/' directory.")
|
| 258 |
return []
|
| 259 |
|
| 260 |
+
@st.cache_datadef load_performance_data(file_path="data/final_5_day_results_df.csv"):
|
|
|
|
| 261 |
"""Loads pre-calculated performance data for Tab 3."""
|
| 262 |
try:
|
| 263 |
df = pd.read_csv(file_path)
|
|
|
|
| 309 |
|
| 310 |
# --- CRITICAL CUSTOMIZATION (Hourly Targets) ---
|
| 311 |
HOURLY_TARGET_COLS = ['target_temp_next_24h', 'target_temp_next_48h', 'target_temp_next_72h',
|
| 312 |
+
'target_temp_next_96h', 'target_temp_next_120h']
|
| 313 |
|
| 314 |
# Load models và data mới
|
| 315 |
hourly_data_df = load_hourly_data(file_path="data/final_hourly_feature_dataset.csv") # Dùng tên file features chính xác
|
|
|
|
| 342 |
# Dùng np đã được import
|
| 343 |
np.random.seed(42)
|
| 344 |
return [last_temp + 1.5 * np.sin(2 * np.pi * (h + 10) / 24) + np.random.normal(0, 0.5)
|
| 345 |
+
for h in range(num_horizons)]
|
| 346 |
|
| 347 |
# Chạy mô hình Direct Hourly
|
| 348 |
for h in range(1, num_horizons + 1):
|
|
|
|
| 537 |
st.metric(label="Temp Yesterday (temp_lag_1)", value=f"{input_features['temp_lag_1'].iloc[0]:.1f}°C")
|
| 538 |
st.metric(label="7-Day Avg Temp (temp_roll_7d_mean)", value=f"{input_features['temp_roll_7d_mean'].iloc[0]:.1f}°C")
|
| 539 |
|
| 540 |
+
# --- GIỮ NGUYÊN LỖI THEO YÊU CẦU ---
|
| 541 |
+
# Code này sẽ gây lỗi KeyError nếu 'precip_roll_7d_sum' không tồn tại
|
| 542 |
st.metric(label="7-Day Total Rainfall (precip_roll_7d_sum)", value=f"{input_features['precip_roll_7d_sum'].iloc[0]:.1f} mm")
|
| 543 |
|
| 544 |
st.metric(label="14-Day Temp Volatility (temp_roll_14d_std)", value=f"{input_features['temp_roll_14d_std'].iloc[0]:.2f}°C")
|
|
|
|
| 757 |
value=max_ts.date(), # Mặc định chọn ngày cuối cùng
|
| 758 |
min_value=min_ts.date(),
|
| 759 |
max_value=max_ts.date(),
|
| 760 |
+
format="YYYY-MM-DD",
|
| 761 |
+
key="hourly_date_input" # Thêm key duy nhất
|
| 762 |
)
|
| 763 |
|
| 764 |
# 2. Hour Selection (Chỉ show các giờ có sẵn trong ngày đã chọn)
|
|
|
|
| 792 |
predictions_24h = predict_next_24_hours(input_features_hourly, hourly_models_24h)
|
| 793 |
|
| 794 |
# --- TÍNH TOÁN METRIC T+24h ---
|
|
|
|
| 795 |
t_plus_24h_metric_value = predictions_24h[23] if len(predictions_24h) >= 24 else (predictions_24h[-1] if predictions_24h else float('nan'))
|
| 796 |
|
| 797 |
# 2. Hiển thị Dự đoán T+24h (Tức là giờ đó ngày mai)
|
|
|
|
| 800 |
forecast_start_ts = latest_time_for_day + pd.Timedelta(hours=1)
|
| 801 |
|
| 802 |
# Tính các giá trị cho T+2h và T+3h
|
|
|
|
| 803 |
t_plus_2h_value = predictions_24h[1] if len(predictions_24h) >= 2 else float('nan')
|
| 804 |
t_plus_3h_value = predictions_24h[2] if len(predictions_24h) >= 3 else float('nan')
|
| 805 |
|
|
|
|
| 818 |
# --- 1. Metric T+2h ---
|
| 819 |
with col_t2:
|
| 820 |
st.metric(
|
|
|
|
| 821 |
label=f"Forecast @ {forecast_t2_ts.strftime('%H:%M')} (T+2H)",
|
| 822 |
value=f"{t_plus_2h_value:.1f}°C"
|
| 823 |
)
|
|
|
|
| 825 |
# --- 2. Metric T+3h ---
|
| 826 |
with col_t3:
|
| 827 |
st.metric(
|
|
|
|
| 828 |
label=f"Forecast @ {forecast_t3_ts.strftime('%H:%M')} (T+3H)",
|
| 829 |
value=f"{t_plus_3h_value:.1f}°C"
|
| 830 |
)
|
|
|
|
| 832 |
# --- 3. Metric T+24h (Giữ lại để đối chiếu) ---
|
| 833 |
with col_t24:
|
| 834 |
st.metric(
|
|
|
|
| 835 |
label=f"Forecast @ {forecast_t24_ts.strftime('%H:%M')} (T+24H)",
|
| 836 |
value=f"{t_plus_24h_metric_value:.1f}°C"
|
| 837 |
)
|
|
|
|
| 846 |
delta="Peak Heat")
|
| 847 |
|
| 848 |
|
| 849 |
+
# --- BẮT ĐẦU THAY THẾ BIỂU ĐỒ TAB 4 ---
|
| 850 |
+
|
| 851 |
+
# 5.1 Graph: Bối cảnh Lịch sử & Dự báo
|
| 852 |
+
st.subheader("Historical Context & Forecast (Hourly)")
|
| 853 |
|
| 854 |
+
# Lấy 24 giờ lịch sử
|
| 855 |
+
history_start_ts = latest_time_for_day - pd.Timedelta(hours=23) # Lùi 23 giờ để có 24 điểm
|
| 856 |
+
history_end_ts = latest_time_for_day
|
| 857 |
|
| 858 |
+
# Lấy 'temp' (actual) từ dataframe GỐC theo giờ
|
| 859 |
+
history_df_hourly = hourly_data_df.loc[history_start_ts:history_end_ts]['temp']
|
| 860 |
+
|
| 861 |
+
# Tạo dataframe cho 24h dự báo
|
| 862 |
+
forecast_hourly_index = pd.date_range(start=forecast_start_ts, periods=len(predictions_24h), freq='H')
|
| 863 |
+
forecast_df_hourly = pd.DataFrame({
|
| 864 |
+
'Time': forecast_hourly_index,
|
| 865 |
+
'Forecast': predictions_24h
|
| 866 |
}).set_index('Time')
|
| 867 |
|
| 868 |
+
# Vẽ biểu đồ
|
| 869 |
+
fig_hist_hourly = go.Figure()
|
| 870 |
+
fig_hist_hourly.add_trace(go.Scatter(
|
| 871 |
+
x=history_df_hourly.index, y=history_df_hourly,
|
| 872 |
+
mode='lines+markers', name='Past 24 Hours (Actual)',
|
| 873 |
+
line=dict(color='blue')
|
| 874 |
+
))
|
| 875 |
+
fig_hist_hourly.add_trace(go.Scatter(
|
| 876 |
+
x=forecast_df_hourly.index, y=forecast_df_hourly['Forecast'],
|
| 877 |
+
mode='lines+markers', name='Next 24 Hours (Forecast)',
|
| 878 |
+
line=dict(color='red', dash='dot')
|
| 879 |
+
))
|
| 880 |
+
fig_hist_hourly.update_layout(
|
| 881 |
+
title="Hourly Forecast vs. Historical Context",
|
| 882 |
+
xaxis_title="Time", yaxis_title="Temperature (°C)",
|
| 883 |
+
template="plotly_white", legend=dict(x=0.01, y=0.99)
|
| 884 |
+
)
|
| 885 |
+
st.plotly_chart(fig_hist_hourly, use_container_width=True)
|
| 886 |
+
|
| 887 |
+
# 5.2 Graph: So sánh Dự báo vs Thực tế
|
| 888 |
+
st.subheader("24-Hour Forecast vs. Actual Comparison")
|
| 889 |
+
|
| 890 |
+
# Lấy 'temp' (actual) cho 24 giờ TỚI
|
| 891 |
+
try:
|
| 892 |
+
future_actuals_df = hourly_data_df.loc[forecast_hourly_index]['temp']
|
| 893 |
+
actual_values_24h = future_actuals_df.values
|
| 894 |
+
except KeyError:
|
| 895 |
+
# Xảy ra nếu forecast_hourly_index vượt ra ngoài dữ liệu
|
| 896 |
+
actual_values_24h = [float('nan')] * len(predictions_24h)
|
| 897 |
+
|
| 898 |
+
# Kiểm tra xem có bất kỳ giá trị NaN nào không
|
| 899 |
+
is_partial_hourly_forecast = any(pd.isna(v) for v in actual_values_24h) or (len(actual_values_24h) < len(predictions_24h))
|
| 900 |
+
|
| 901 |
+
fig_comp_hourly = go.Figure()
|
| 902 |
+
|
| 903 |
+
# 1. Luôn thêm đường Dự báo
|
| 904 |
+
fig_comp_hourly.add_trace(go.Scatter(
|
| 905 |
+
x=forecast_hourly_index, y=predictions_24h,
|
| 906 |
+
mode='lines+markers', name='24-Hour Forecast',
|
| 907 |
+
line=dict(color='red', dash='dot')
|
| 908 |
))
|
| 909 |
+
|
| 910 |
+
# 2. Chỉ thêm đường Thực tế (màu xanh) nếu có đủ dữ liệu
|
| 911 |
+
if not is_partial_hourly_forecast:
|
| 912 |
+
fig_comp_hourly.add_trace(go.Scatter(
|
| 913 |
+
x=forecast_hourly_index, y=actual_values_24h,
|
| 914 |
+
mode='lines+markers', name='24-Hour Actual',
|
| 915 |
+
line=dict(color='blue')
|
| 916 |
+
))
|
| 917 |
+
fig_comp_hourly.update_layout(title="24-Hour Forecast vs. Actual Values")
|
| 918 |
+
else:
|
| 919 |
+
# Nếu không, chỉ hiển thị dự báo
|
| 920 |
+
fig_comp_hourly.update_layout(title="24-Hour Forecast (Actual data not yet available)")
|
| 921 |
|
| 922 |
+
# Luôn hiển thị biểu đồ
|
| 923 |
+
fig_comp_hourly.update_layout(
|
| 924 |
+
xaxis_title="Time", yaxis_title="Temperature (°C)",
|
| 925 |
+
template="plotly_white", legend=dict(x=0.01, y=0.99)
|
|
|
|
|
|
|
| 926 |
)
|
| 927 |
+
st.plotly_chart(fig_comp_hourly, use_container_width=True)
|
| 928 |
+
|
| 929 |
+
# --- KẾT THÚC THAY THẾ BIỂU ĐỒ TAB 4 ---
|
| 930 |
|
| 931 |
# --- NEW GRAPH 1: RMSE Degradation Plot (Reliability) ---
|
| 932 |
st.subheader("Model Reliability: Error Degradation")
|