UCS2014 commited on
Commit
9039637
·
verified ·
1 Parent(s): 9354b76

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +96 -47
app.py CHANGED
@@ -45,10 +45,11 @@ CANDS = {
45
  COLORS = {"pred_bo":"#1f77b4","pred_bd":"#d62728","actual_bo":"#f2b702","actual_bd":"#2ca02c","ref":"#5a5a5a"}
46
 
47
  # Plot sizing
48
- CROSS_W, CROSS_H = 350, 350
49
  TRACK_H, TRACK_W = 1000, 500
50
  FONT_SZ = 13
51
  BOLD_FONT = "Arial Black, Arial, sans-serif"
 
52
 
53
  # =========================
54
  # Page / CSS
@@ -319,8 +320,10 @@ def cross_plot_static(actual, pred, xlabel, ylabel, color="#1f77b4"):
319
  ax.set_xticks(ticks); ax.set_yticks(ticks); ax.set_aspect("equal", adjustable="box")
320
  fmt = FuncFormatter(lambda x, _: f"{x:.2f}")
321
  ax.xaxis.set_major_formatter(fmt); ax.yaxis.set_major_formatter(fmt)
322
- ax.set_xlabel(xlabel, fontweight="bold", fontsize=10); ax.set_ylabel(ylabel, fontweight="bold", fontsize=10)
323
- ax.tick_params(labelsize=6); ax.grid(True, linestyle=":", alpha=0.3)
 
 
324
  for s in ax.spines.values(): s.set_linewidth(1.1); s.set_color("#444")
325
  fig.subplots_adjust(left=0.16, bottom=0.16, right=0.98, top=0.98); return fig
326
 
@@ -362,14 +365,21 @@ def track_plot_single(df, pred_col, actual_col=None, title_suffix=""):
362
  legend=dict(x=0.98, y=0.05, xanchor="right", yanchor="bottom",
363
  bgcolor="rgba(255,255,255,0.75)", bordercolor="#ccc", borderwidth=1),
364
  legend_title_text="", title=title_suffix)
365
- fig.update_xaxes(title_text=st.session_state.get("X_UNITS","MW (pcf)"),
366
- title_font=dict(size=20, family=BOLD_FONT), tickfont=dict(size=15, family=BOLD_FONT),
367
- side="top", range=[xmin, xmax], ticks="outside", tickformat=",.2f",
368
- tickmode="auto", tick0=tick0, showline=True, linewidth=1.2, linecolor="#444",
369
- mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True)
370
- fig.update_yaxes(title_text=ylab, title_font=dict(size=20, family=BOLD_FONT), tickfont=dict(size=15, family=BOLD_FONT),
371
- range=y_range, ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
372
- mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True)
 
 
 
 
 
 
 
373
  return fig
374
 
375
  def track_plot_combined(df):
@@ -401,14 +411,21 @@ def track_plot_combined(df):
401
  legend=dict(x=0.98, y=0.05, xanchor="right", yanchor="bottom",
402
  bgcolor="rgba(255,255,255,0.75)", bordercolor="#ccc", borderwidth=1),
403
  legend_title_text="", title="Combined (Breakout / Breakdown)")
404
- fig.update_xaxes(title_text=st.session_state.get("X_UNITS","MW (pcf)"),
405
- title_font=dict(size=20, family=BOLD_FONT), tickfont=dict(size=15, family=BOLD_FONT),
406
- side="top", range=[xmin, xmax], ticks="outside", tickformat=",.2f",
407
- tickmode="auto", tick0=tick0, showline=True, linewidth=1.2, linecolor="#444",
408
- mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True)
409
- fig.update_yaxes(title_text=ylab, title_font=dict(size=20, family=BOLD_FONT), tickfont=dict(size=15, family=BOLD_FONT),
410
- range=y_range, ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
411
- mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True)
 
 
 
 
 
 
 
412
  return fig
413
 
414
  def preview_tracks(df: pd.DataFrame, cols: list[str]):
@@ -463,7 +480,6 @@ def _try_load_or_explain(p: Path, name: str):
463
  try:
464
  return load_model(str(p))
465
  except Exception as e:
466
- # Prefer BO meta versions if available, else BD
467
  want_np = (meta_bo.get("versions",{}) or meta_bd.get("versions",{})).get("numpy", "N/A")
468
  want_skl = (meta_bo.get("versions",{}) or meta_bd.get("versions",{})).get("scikit_learn", "N/A")
469
  st.error(
@@ -473,11 +489,9 @@ def _try_load_or_explain(p: Path, name: str):
473
  )
474
  st.stop()
475
 
476
- # Unwrap payload-style joblibs: {"model": pipeline, "model_info": {...}}
477
  def _unwrap(payload):
478
  if isinstance(payload, dict) and "model" in payload:
479
  return payload["model"], payload.get("model_info", {})
480
- # Fallback: estimator saved bare or custom object
481
  return payload, getattr(payload, "model_info", {})
482
 
483
  payload_bo = _try_load_or_explain(bo_model_path, "BO model")
@@ -486,23 +500,17 @@ payload_bd = _try_load_or_explain(bd_model_path, "BD model")
486
  model_bo, info_bo = _unwrap(payload_bo)
487
  model_bd, info_bd = _unwrap(payload_bd)
488
 
489
- # Resolve features per model (prefer model_info → meta → fallback)
490
  features_bo = list((info_bo.get("features") or meta_bo.get("features") or FEATURES_DEFAULT))
491
  features_bd = list((info_bd.get("features") or meta_bd.get("features") or FEATURES_DEFAULT))
492
-
493
- # Use the UNION so BD's 'Depth (ft)' is required and both models can run from one input
494
- # Order: keep BO order, then append BD-only columns in order
495
  features_union = list(dict.fromkeys(features_bo + [c for c in features_bd if c not in features_bo]))
496
 
497
- # Resolve targets / units
498
  TARGET_BO = str(meta_bo.get("target") or TARGET_BO_DEFAULT)
499
  TARGET_BD = str(meta_bd.get("target") or TARGET_BD_DEFAULT)
500
  X_UNITS = str(meta_bo.get("units") or meta_bd.get("units") or "MW (pcf)")
501
 
502
- # Stash in session
503
  st.session_state["FEATURES_BO"] = features_bo
504
  st.session_state["FEATURES_BD"] = features_bd
505
- st.session_state["FEATURES"] = features_union # strict check uses the union
506
  st.session_state["TARGET_BO"] = TARGET_BO
507
  st.session_state["TARGET_BD"] = TARGET_BD
508
  st.session_state["X_UNITS"] = X_UNITS
@@ -601,7 +609,7 @@ if st.session_state.app_step == "dev":
601
  sticky_header("Case Building", "📄 **Preview** then click **Run Model**.")
602
  else:
603
  sticky_header("Case Building", "**Upload your data** and run the model.")
604
- render_bo_bd_note()
605
 
606
  if run and st.session_state.dev_file_bytes:
607
  book = read_book_bytes(st.session_state.dev_file_bytes)
@@ -643,13 +651,35 @@ if st.session_state.app_step == "dev":
643
  t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
644
  units = st.session_state.get("X_UNITS","MW (pcf)")
645
  with t1:
646
- st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
647
- use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
648
- st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO} ({units})", f"Predicted {TARGET_BO} ({units})", COLORS["pred_bo"]), use_container_width=False)
 
 
 
 
 
 
 
 
 
 
 
649
  with t2:
650
- st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
651
- use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
652
- st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD} ({units})", f"Predicted {TARGET_BD} ({units})", COLORS["pred_bd"]), use_container_width=False)
 
 
 
 
 
 
 
 
 
 
 
653
  with t3:
654
  st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
655
 
@@ -679,7 +709,7 @@ if st.session_state.app_step == "validate":
679
  if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
680
 
681
  sticky_header("Validate the Models", "Upload a dataset with the same **feature** columns and **BO/BD** actuals.")
682
- render_bo_bd_note()
683
 
684
  if go_btn and up is not None:
685
  book = read_book_bytes(up.getvalue())
@@ -717,15 +747,35 @@ if st.session_state.app_step == "validate":
717
  t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
718
  units = st.session_state.get("X_UNITS","MW (pcf)")
719
  with t1:
720
- st.plotly_chart(track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
721
- use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
722
- st.pyplot(cross_plot_static(df[TARGET_BO], df[PRED_BO], f"Actual {TARGET_BO} ({units})", f"Predicted {TARGET_BO} ({units})", COLORS["pred_bo"]),
723
- use_container_width=False)
 
 
 
 
 
 
 
 
 
 
724
  with t2:
725
- st.plotly_chart(track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
726
- use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
727
- st.pyplot(cross_plot_static(df[TARGET_BD], df[PRED_BD], f"Actual {TARGET_BD} ({units})", f"Predicted {TARGET_BD} ({units})", COLORS["pred_bd"]),
728
- use_container_width=False)
 
 
 
 
 
 
 
 
 
 
729
  with t3:
730
  st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
731
 
@@ -752,7 +802,7 @@ if st.session_state.app_step == "predict":
752
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
753
 
754
  sticky_header("Prediction", "Upload a dataset with **feature columns only** (no BO/BD actuals).")
755
- render_bo_bd_note()
756
 
757
  if go_btn and up is not None:
758
  book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
@@ -814,7 +864,6 @@ if st.session_state.show_preview_modal:
814
  for t, name in zip(tabs, names):
815
  with t:
816
  df = book_to_preview[name]
817
- # show a strict check summary, but do not stop
818
  missing = [c for c in st.session_state["FEATURES"] if c not in df.columns]
819
  st.write("**Missing vs required features:**", missing if missing else "None ✅")
820
  t1, t2 = st.tabs(["Tracks", "Summary"])
 
45
  COLORS = {"pred_bo":"#1f77b4","pred_bd":"#d62728","actual_bo":"#f2b702","actual_bd":"#2ca02c","ref":"#5a5a5a"}
46
 
47
  # Plot sizing
48
+ CROSS_W, CROSS_H = 350, 350 # X-plot size
49
  TRACK_H, TRACK_W = 1000, 500
50
  FONT_SZ = 13
51
  BOLD_FONT = "Arial Black, Arial, sans-serif"
52
+ PLAIN_FONT = "Arial, sans-serif"
53
 
54
  # =========================
55
  # Page / CSS
 
320
  ax.set_xticks(ticks); ax.set_yticks(ticks); ax.set_aspect("equal", adjustable="box")
321
  fmt = FuncFormatter(lambda x, _: f"{x:.2f}")
322
  ax.xaxis.set_major_formatter(fmt); ax.yaxis.set_major_formatter(fmt)
323
+ ax.set_xlabel(xlabel, fontweight="bold", fontsize=10, color="black")
324
+ ax.set_ylabel(ylabel, fontweight="bold", fontsize=10, color="black")
325
+ ax.tick_params(labelsize=6, colors="black")
326
+ ax.grid(True, linestyle=":", alpha=0.3)
327
  for s in ax.spines.values(): s.set_linewidth(1.1); s.set_color("#444")
328
  fig.subplots_adjust(left=0.16, bottom=0.16, right=0.98, top=0.98); return fig
329
 
 
365
  legend=dict(x=0.98, y=0.05, xanchor="right", yanchor="bottom",
366
  bgcolor="rgba(255,255,255,0.75)", bordercolor="#ccc", borderwidth=1),
367
  legend_title_text="", title=title_suffix)
368
+ fig.update_xaxes(
369
+ title_text=st.session_state.get("X_UNITS","MW (pcf)"),
370
+ title_font=dict(size=20, family=BOLD_FONT, color="#000"),
371
+ tickfont=dict(size=15, family=PLAIN_FONT, color="#000"),
372
+ side="top", range=[xmin, xmax], ticks="outside", tickformat=",.2f",
373
+ tickmode="auto", tick0=tick0, showline=True, linewidth=1.2, linecolor="#444",
374
+ mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
375
+ )
376
+ fig.update_yaxes(
377
+ title_text=ylab,
378
+ title_font=dict(size=20, family=BOLD_FONT, color="#000"),
379
+ tickfont=dict(size=15, family=PLAIN_FONT, color="#000"),
380
+ range=y_range, ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
381
+ mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
382
+ )
383
  return fig
384
 
385
  def track_plot_combined(df):
 
411
  legend=dict(x=0.98, y=0.05, xanchor="right", yanchor="bottom",
412
  bgcolor="rgba(255,255,255,0.75)", bordercolor="#ccc", borderwidth=1),
413
  legend_title_text="", title="Combined (Breakout / Breakdown)")
414
+ fig.update_xaxes(
415
+ title_text=st.session_state.get("X_UNITS","MW (pcf)"),
416
+ title_font=dict(size=20, family=BOLD_FONT, color="#000"),
417
+ tickfont=dict(size=15, family=PLAIN_FONT, color="#000"),
418
+ side="top", range=[xmin, xmax], ticks="outside", tickformat=",.2f",
419
+ tickmode="auto", tick0=tick0, showline=True, linewidth=1.2, linecolor="#444",
420
+ mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
421
+ )
422
+ fig.update_yaxes(
423
+ title_text=ylab,
424
+ title_font=dict(size=20, family=BOLD_FONT, color="#000"),
425
+ tickfont=dict(size=15, family=PLAIN_FONT, color="#000"),
426
+ range=y_range, ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
427
+ mirror=True, showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
428
+ )
429
  return fig
430
 
431
  def preview_tracks(df: pd.DataFrame, cols: list[str]):
 
480
  try:
481
  return load_model(str(p))
482
  except Exception as e:
 
483
  want_np = (meta_bo.get("versions",{}) or meta_bd.get("versions",{})).get("numpy", "N/A")
484
  want_skl = (meta_bo.get("versions",{}) or meta_bd.get("versions",{})).get("scikit_learn", "N/A")
485
  st.error(
 
489
  )
490
  st.stop()
491
 
 
492
  def _unwrap(payload):
493
  if isinstance(payload, dict) and "model" in payload:
494
  return payload["model"], payload.get("model_info", {})
 
495
  return payload, getattr(payload, "model_info", {})
496
 
497
  payload_bo = _try_load_or_explain(bo_model_path, "BO model")
 
500
  model_bo, info_bo = _unwrap(payload_bo)
501
  model_bd, info_bd = _unwrap(payload_bd)
502
 
 
503
  features_bo = list((info_bo.get("features") or meta_bo.get("features") or FEATURES_DEFAULT))
504
  features_bd = list((info_bd.get("features") or meta_bd.get("features") or FEATURES_DEFAULT))
 
 
 
505
  features_union = list(dict.fromkeys(features_bo + [c for c in features_bd if c not in features_bo]))
506
 
 
507
  TARGET_BO = str(meta_bo.get("target") or TARGET_BO_DEFAULT)
508
  TARGET_BD = str(meta_bd.get("target") or TARGET_BD_DEFAULT)
509
  X_UNITS = str(meta_bo.get("units") or meta_bd.get("units") or "MW (pcf)")
510
 
 
511
  st.session_state["FEATURES_BO"] = features_bo
512
  st.session_state["FEATURES_BD"] = features_bd
513
+ st.session_state["FEATURES"] = features_union
514
  st.session_state["TARGET_BO"] = TARGET_BO
515
  st.session_state["TARGET_BD"] = TARGET_BD
516
  st.session_state["X_UNITS"] = X_UNITS
 
609
  sticky_header("Case Building", "📄 **Preview** then click **Run Model**.")
610
  else:
611
  sticky_header("Case Building", "**Upload your data** and run the model.")
612
+
613
 
614
  if run and st.session_state.dev_file_bytes:
615
  book = read_book_bytes(st.session_state.dev_file_bytes)
 
651
  t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
652
  units = st.session_state.get("X_UNITS","MW (pcf)")
653
  with t1:
654
+ left, right = st.columns([3,1], gap="large")
655
+ with left:
656
+ st.plotly_chart(
657
+ track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
658
+ use_container_width=False, config={"displayModeBar": False, "scrollZoom": True}
659
+ )
660
+ with right:
661
+ st.pyplot(
662
+ cross_plot_static(df[TARGET_BO], df[PRED_BO],
663
+ f"Actual {TARGET_BO} ({units})",
664
+ f"Predicted {TARGET_BO} ({units})",
665
+ COLORS["pred_bo"]),
666
+ use_container_width=False
667
+ )
668
  with t2:
669
+ left, right = st.columns([3,1], gap="large")
670
+ with left:
671
+ st.plotly_chart(
672
+ track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
673
+ use_container_width=False, config={"displayModeBar": False, "scrollZoom": True}
674
+ )
675
+ with right:
676
+ st.pyplot(
677
+ cross_plot_static(df[TARGET_BD], df[PRED_BD],
678
+ f"Actual {TARGET_BD} ({units})",
679
+ f"Predicted {TARGET_BD} ({units})",
680
+ COLORS["pred_bd"]),
681
+ use_container_width=False
682
+ )
683
  with t3:
684
  st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
685
 
 
709
  if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
710
 
711
  sticky_header("Validate the Models", "Upload a dataset with the same **feature** columns and **BO/BD** actuals.")
712
+
713
 
714
  if go_btn and up is not None:
715
  book = read_book_bytes(up.getvalue())
 
747
  t1, t2, t3 = st.tabs(["Breakout", "Breakdown", "Combined"])
748
  units = st.session_state.get("X_UNITS","MW (pcf)")
749
  with t1:
750
+ left, right = st.columns([3,1], gap="large")
751
+ with left:
752
+ st.plotly_chart(
753
+ track_plot_single(df, PRED_BO, actual_col=TARGET_BO, title_suffix="Breakout"),
754
+ use_container_width=False, config={"displayModeBar": False, "scrollZoom": True}
755
+ )
756
+ with right:
757
+ st.pyplot(
758
+ cross_plot_static(df[TARGET_BO], df[PRED_BO],
759
+ f"Actual {TARGET_BO} ({units})",
760
+ f"Predicted {TARGET_BO} ({units})",
761
+ COLORS["pred_bo"]),
762
+ use_container_width=False
763
+ )
764
  with t2:
765
+ left, right = st.columns([3,1], gap="large")
766
+ with left:
767
+ st.plotly_chart(
768
+ track_plot_single(df, PRED_BD, actual_col=TARGET_BD, title_suffix="Breakdown"),
769
+ use_container_width=False, config={"displayModeBar": False, "scrollZoom": True}
770
+ )
771
+ with right:
772
+ st.pyplot(
773
+ cross_plot_static(df[TARGET_BD], df[PRED_BD],
774
+ f"Actual {TARGET_BD} ({units})",
775
+ f"Predicted {TARGET_BD} ({units})",
776
+ COLORS["pred_bd"]),
777
+ use_container_width=False
778
+ )
779
  with t3:
780
  st.plotly_chart(track_plot_combined(df), use_container_width=False, config={"displayModeBar": False, "scrollZoom": True})
781
 
 
802
  if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
803
 
804
  sticky_header("Prediction", "Upload a dataset with **feature columns only** (no BO/BD actuals).")
805
+
806
 
807
  if go_btn and up is not None:
808
  book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
 
864
  for t, name in zip(tabs, names):
865
  with t:
866
  df = book_to_preview[name]
 
867
  missing = [c for c in st.session_state["FEATURES"] if c not in df.columns]
868
  st.write("**Missing vs required features:**", missing if missing else "None ✅")
869
  t1, t2 = st.tabs(["Tracks", "Summary"])