Files changed (1) hide show
  1. app.py +86 -39
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
- 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,8 +257,7 @@ def load_champion_models():
257
  "Ensure the 5 .pkl files are in the 'models/' directory.")
258
  return []
259
 
260
- @st.cache_data
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
- 'target_temp_next_96h', 'target_temp_next_120h']
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
- for h in range(num_horizons)]
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
- # --- ĐÃ SỬA LỖI ---
542
- # Thay thế 'precip_roll_7d_sum' (không tồn tại) bằng 'precip_roll_7d_mean' (tồn tại)
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
- # 5. Graph: Nhiệt độ Từng Giờ
 
 
 
855
 
856
- st.subheader("Hourly Temperature Breakdown (T+1h to T+24h)")
 
 
857
 
858
- # ... (Biểu đồ giữ nguyên)
859
- hourly_index = pd.date_range(start=forecast_start_ts, periods=len(predictions_24h), freq='H')
860
- df_hourly_forecast = pd.DataFrame({
861
- 'Time': hourly_index,
862
- 'Temperature': predictions_24h
 
 
 
863
  }).set_index('Time')
864
 
865
- fig_hourly = go.Figure()
866
- fig_hourly.add_trace(go.Scatter(
867
- x=df_hourly_forecast.index,
868
- y=df_hourly_forecast['Temperature'],
869
- mode='lines+markers',
870
- name='Hourly Forecast',
871
- line=dict(color='#ff6347', width=2),
872
- marker=dict(size=6)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
873
  ))
 
 
 
 
 
 
 
 
 
 
 
 
874
 
875
- fig_hourly.update_layout(
876
- title="Hourly Temperature Forecast (T+1h to T+24h)",
877
- xaxis_title="Time",
878
- yaxis_title="Temperature (°C)",
879
- template="plotly_white",
880
- legend=dict(x=0.01, y=0.99)
881
  )
882
- st.plotly_chart(fig_hourly, use_container_width=True)
 
 
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")