Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -25,9 +25,11 @@ MODEL_FALLBACKS = [MODELS_DIR / "model.joblib", MODELS_DIR / "model.pkl"]
|
|
| 25 |
COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
|
| 26 |
|
| 27 |
# ---- Plot sizing controls ----
|
| 28 |
-
CROSS_W =
|
| 29 |
-
CROSS_H =
|
| 30 |
TRACK_H = 1000 # px (plotly height; width auto-fits column)
|
|
|
|
|
|
|
| 31 |
FONT_SZ = 13
|
| 32 |
BOLD_FONT = "Arial Black, Arial, sans-serif" # used for bold axis titles & ticks
|
| 33 |
|
|
@@ -107,6 +109,34 @@ TABLE_CENTER_CSS = [
|
|
| 107 |
dict(selector="td", props=[("text-align", "center")]),
|
| 108 |
]
|
| 109 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
# =========================
|
| 111 |
# Password gate
|
| 112 |
# =========================
|
|
@@ -158,7 +188,7 @@ def rmse(y_true, y_pred) -> float:
|
|
| 158 |
|
| 159 |
def pearson_r(y_true, y_pred) -> float:
|
| 160 |
a = np.asarray(y_true, dtype=float)
|
| 161 |
-
p = np.asarray(y_pred,
|
| 162 |
if a.size < 2: return float("nan")
|
| 163 |
return float(np.corrcoef(a, p)[0, 1])
|
| 164 |
|
|
@@ -207,7 +237,7 @@ def df_centered_rounded(df: pd.DataFrame, hide_index=True):
|
|
| 207 |
# =========================
|
| 208 |
def cross_plot_static(actual, pred):
|
| 209 |
a = pd.Series(actual, dtype=float)
|
| 210 |
-
p = pd.Series(pred,
|
| 211 |
|
| 212 |
fixed_min, fixed_max = 6000, 10000
|
| 213 |
ticks = np.arange(fixed_min, fixed_max + 1, 1000)
|
|
@@ -284,7 +314,9 @@ def track_plot(df, include_actual=True):
|
|
| 284 |
))
|
| 285 |
|
| 286 |
fig.update_layout(
|
| 287 |
-
height=TRACK_H,
|
|
|
|
|
|
|
| 288 |
paper_bgcolor="#fff", plot_bgcolor="#fff",
|
| 289 |
margin=dict(l=64, r=16, t=36, b=48), hovermode="closest",
|
| 290 |
font=dict(size=FONT_SZ, color="#000"),
|
|
@@ -509,10 +541,12 @@ if st.session_state.app_step == "dev":
|
|
| 509 |
sh_train = find_sheet(book, ["Train","Training","training2","train","training"])
|
| 510 |
sh_test = find_sheet(book, ["Test","Testing","testing2","test","testing"])
|
| 511 |
if sh_train is None or sh_test is None:
|
| 512 |
-
st.
|
|
|
|
| 513 |
tr = book[sh_train].copy(); te = book[sh_test].copy()
|
| 514 |
if not (ensure_cols(tr, FEATURES+[TARGET]) and ensure_cols(te, FEATURES+[TARGET])):
|
| 515 |
-
st.
|
|
|
|
| 516 |
tr["UCS_Pred"] = model.predict(tr[FEATURES])
|
| 517 |
te["UCS_Pred"] = model.predict(te[FEATURES])
|
| 518 |
|
|
@@ -530,7 +564,7 @@ if st.session_state.app_step == "dev":
|
|
| 530 |
|
| 531 |
tr_min = tr[FEATURES].min().to_dict(); tr_max = tr[FEATURES].max().to_dict()
|
| 532 |
st.session_state.train_ranges = {f:(float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
| 533 |
-
st.
|
| 534 |
|
| 535 |
def _dev_block(df, m):
|
| 536 |
c1,c2,c3 = st.columns(3)
|
|
@@ -543,7 +577,7 @@ if st.session_state.app_step == "dev":
|
|
| 543 |
with col_track:
|
| 544 |
st.plotly_chart(
|
| 545 |
track_plot(df, include_actual=True),
|
| 546 |
-
use_container_width=
|
| 547 |
config={"displayModeBar": False, "scrollZoom": True}
|
| 548 |
)
|
| 549 |
|
|
@@ -577,7 +611,7 @@ if st.session_state.app_step == "validate":
|
|
| 577 |
book = read_book_bytes(up.getvalue())
|
| 578 |
name = find_sheet(book, ["Validation","Validate","validation2","Val","val"]) or list(book.keys())[0]
|
| 579 |
df = book[name].copy()
|
| 580 |
-
if not ensure_cols(df, FEATURES+[TARGET]): st.
|
| 581 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 582 |
st.session_state.results["Validate"]=df
|
| 583 |
|
|
@@ -613,11 +647,12 @@ if st.session_state.app_step == "validate":
|
|
| 613 |
with col_track:
|
| 614 |
st.plotly_chart(
|
| 615 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|
| 616 |
-
use_container_width=
|
|
|
|
| 617 |
)
|
| 618 |
|
| 619 |
sv = st.session_state.results["sv_val"]
|
| 620 |
-
if sv["oor"] > 0: st.
|
| 621 |
if st.session_state.results["oor_tbl"] is not None:
|
| 622 |
st.write("*Out-of-range rows (vs. Training min–max):*")
|
| 623 |
df_centered_rounded(st.session_state.results["oor_tbl"])
|
|
@@ -643,7 +678,7 @@ if st.session_state.app_step == "predict":
|
|
| 643 |
if go_btn and up is not None:
|
| 644 |
book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
|
| 645 |
df = book[name].copy()
|
| 646 |
-
if not ensure_cols(df, FEATURES): st.
|
| 647 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 648 |
st.session_state.results["PredictOnly"]=df
|
| 649 |
|
|
@@ -674,13 +709,14 @@ if st.session_state.app_step == "predict":
|
|
| 674 |
round(sv["pred_std"],2),
|
| 675 |
f'{sv["oor"]:.1f}%']
|
| 676 |
})
|
| 677 |
-
st.
|
| 678 |
df_centered_rounded(table, hide_index=True)
|
| 679 |
st.caption("**★ OOR** = % of rows whose input features fall outside the training min–max range.")
|
| 680 |
with col_right:
|
| 681 |
st.plotly_chart(
|
| 682 |
track_plot(df, include_actual=False),
|
| 683 |
-
use_container_width=
|
|
|
|
| 684 |
)
|
| 685 |
|
| 686 |
# =========================
|
|
@@ -693,20 +729,20 @@ if st.session_state.show_preview_modal:
|
|
| 693 |
book_to_preview = read_book_bytes(st.session_state.dev_file_bytes)
|
| 694 |
elif st.session_state.app_step in ["validate", "predict"] and up is not None:
|
| 695 |
book_to_preview = read_book_bytes(up.getvalue())
|
| 696 |
-
|
| 697 |
# Use a try-except block to handle cases where 'up' might be None
|
| 698 |
# and the logic tries to access its attributes.
|
| 699 |
try:
|
| 700 |
if st.session_state.app_step == "validate" and up is not None:
|
| 701 |
-
|
| 702 |
elif st.session_state.app_step == "predict" and up is not None:
|
| 703 |
-
|
| 704 |
except NameError:
|
| 705 |
book_to_preview = {}
|
| 706 |
|
| 707 |
with st.expander("Preview data", expanded=True):
|
| 708 |
if not book_to_preview:
|
| 709 |
-
st.
|
| 710 |
else:
|
| 711 |
names = list(book_to_preview.keys())
|
| 712 |
tabs = st.tabs(names)
|
|
@@ -718,12 +754,12 @@ if st.session_state.show_preview_modal:
|
|
| 718 |
st.pyplot(preview_tracks(df, FEATURES), use_container_width=True)
|
| 719 |
with t2:
|
| 720 |
tbl = (df[FEATURES]
|
| 721 |
-
|
| 722 |
-
|
| 723 |
df_centered_rounded(tbl.reset_index(names="Feature"))
|
| 724 |
# Reset the state variable after the modal is displayed
|
| 725 |
st.session_state.show_preview_modal = False
|
| 726 |
-
|
| 727 |
# =========================
|
| 728 |
# Footer
|
| 729 |
# =========================
|
|
|
|
| 25 |
COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
|
| 26 |
|
| 27 |
# ---- Plot sizing controls ----
|
| 28 |
+
CROSS_W = 350 # px (matplotlib figure size; Streamlit will still scale)
|
| 29 |
+
CROSS_H = 350
|
| 30 |
TRACK_H = 1000 # px (plotly height; width auto-fits column)
|
| 31 |
+
# NEW: Add a TRACK_W variable to control the width
|
| 32 |
+
TRACK_W = 800 # px (plotly width)
|
| 33 |
FONT_SZ = 13
|
| 34 |
BOLD_FONT = "Arial Black, Arial, sans-serif" # used for bold axis titles & ticks
|
| 35 |
|
|
|
|
| 109 |
dict(selector="td", props=[("text-align", "center")]),
|
| 110 |
]
|
| 111 |
|
| 112 |
+
# NEW: CSS for the message box
|
| 113 |
+
st.markdown("""
|
| 114 |
+
<style>
|
| 115 |
+
.st-message-box {
|
| 116 |
+
background-color: #f0f2f6;
|
| 117 |
+
color: #333333;
|
| 118 |
+
padding: 10px;
|
| 119 |
+
border-radius: 10px;
|
| 120 |
+
border: 1px solid #e6e9ef;
|
| 121 |
+
}
|
| 122 |
+
.st-message-box.st-success {
|
| 123 |
+
background-color: #d4edda;
|
| 124 |
+
color: #155724;
|
| 125 |
+
border-color: #c3e6cb;
|
| 126 |
+
}
|
| 127 |
+
.st-message-box.st-warning {
|
| 128 |
+
background-color: #fff3cd;
|
| 129 |
+
color: #856404;
|
| 130 |
+
border-color: #ffeeba;
|
| 131 |
+
}
|
| 132 |
+
.st-message-box.st-error {
|
| 133 |
+
background-color: #f8d7da;
|
| 134 |
+
color: #721c24;
|
| 135 |
+
border-color: #f5c6cb;
|
| 136 |
+
}
|
| 137 |
+
</style>
|
| 138 |
+
""", unsafe_allow_html=True)
|
| 139 |
+
|
| 140 |
# =========================
|
| 141 |
# Password gate
|
| 142 |
# =========================
|
|
|
|
| 188 |
|
| 189 |
def pearson_r(y_true, y_pred) -> float:
|
| 190 |
a = np.asarray(y_true, dtype=float)
|
| 191 |
+
p = np.asarray(y_pred, dtype=float)
|
| 192 |
if a.size < 2: return float("nan")
|
| 193 |
return float(np.corrcoef(a, p)[0, 1])
|
| 194 |
|
|
|
|
| 237 |
# =========================
|
| 238 |
def cross_plot_static(actual, pred):
|
| 239 |
a = pd.Series(actual, dtype=float)
|
| 240 |
+
p = pd.Series(pred, dtype=float)
|
| 241 |
|
| 242 |
fixed_min, fixed_max = 6000, 10000
|
| 243 |
ticks = np.arange(fixed_min, fixed_max + 1, 1000)
|
|
|
|
| 314 |
))
|
| 315 |
|
| 316 |
fig.update_layout(
|
| 317 |
+
height=TRACK_H,
|
| 318 |
+
width=TRACK_W, # Set the width here
|
| 319 |
+
autosize=False, # Disable autosizing to respect the width
|
| 320 |
paper_bgcolor="#fff", plot_bgcolor="#fff",
|
| 321 |
margin=dict(l=64, r=16, t=36, b=48), hovermode="closest",
|
| 322 |
font=dict(size=FONT_SZ, color="#000"),
|
|
|
|
| 541 |
sh_train = find_sheet(book, ["Train","Training","training2","train","training"])
|
| 542 |
sh_test = find_sheet(book, ["Test","Testing","testing2","test","testing"])
|
| 543 |
if sh_train is None or sh_test is None:
|
| 544 |
+
st.markdown('<div class="st-message-box st-error">Workbook must include Train/Training/training2 and Test/Testing/testing2 sheets.</div>', unsafe_allow_html=True)
|
| 545 |
+
st.stop()
|
| 546 |
tr = book[sh_train].copy(); te = book[sh_test].copy()
|
| 547 |
if not (ensure_cols(tr, FEATURES+[TARGET]) and ensure_cols(te, FEATURES+[TARGET])):
|
| 548 |
+
st.markdown('<div class="st-message-box st-error">Missing required columns.</div>', unsafe_allow_html=True)
|
| 549 |
+
st.stop()
|
| 550 |
tr["UCS_Pred"] = model.predict(tr[FEATURES])
|
| 551 |
te["UCS_Pred"] = model.predict(te[FEATURES])
|
| 552 |
|
|
|
|
| 564 |
|
| 565 |
tr_min = tr[FEATURES].min().to_dict(); tr_max = tr[FEATURES].max().to_dict()
|
| 566 |
st.session_state.train_ranges = {f:(float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
| 567 |
+
st.markdown('<div class="st-message-box st-success">Case has been built and results are displayed below.</div>', unsafe_allow_html=True)
|
| 568 |
|
| 569 |
def _dev_block(df, m):
|
| 570 |
c1,c2,c3 = st.columns(3)
|
|
|
|
| 577 |
with col_track:
|
| 578 |
st.plotly_chart(
|
| 579 |
track_plot(df, include_actual=True),
|
| 580 |
+
use_container_width=False, # Set to False to honor the width in track_plot()
|
| 581 |
config={"displayModeBar": False, "scrollZoom": True}
|
| 582 |
)
|
| 583 |
|
|
|
|
| 611 |
book = read_book_bytes(up.getvalue())
|
| 612 |
name = find_sheet(book, ["Validation","Validate","validation2","Val","val"]) or list(book.keys())[0]
|
| 613 |
df = book[name].copy()
|
| 614 |
+
if not ensure_cols(df, FEATURES+[TARGET]): st.markdown('<div class="st-message-box st-error">Missing required columns.</div>', unsafe_allow_html=True); st.stop()
|
| 615 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 616 |
st.session_state.results["Validate"]=df
|
| 617 |
|
|
|
|
| 647 |
with col_track:
|
| 648 |
st.plotly_chart(
|
| 649 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|
| 650 |
+
use_container_width=False, # Set to False to honor the width in track_plot()
|
| 651 |
+
config={"displayModeBar": False, "scrollZoom": True}
|
| 652 |
)
|
| 653 |
|
| 654 |
sv = st.session_state.results["sv_val"]
|
| 655 |
+
if sv["oor"] > 0: st.markdown('<div class="st-message-box st-warning">Some inputs fall outside **training min–max** ranges.</div>', unsafe_allow_html=True)
|
| 656 |
if st.session_state.results["oor_tbl"] is not None:
|
| 657 |
st.write("*Out-of-range rows (vs. Training min–max):*")
|
| 658 |
df_centered_rounded(st.session_state.results["oor_tbl"])
|
|
|
|
| 678 |
if go_btn and up is not None:
|
| 679 |
book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
|
| 680 |
df = book[name].copy()
|
| 681 |
+
if not ensure_cols(df, FEATURES): st.markdown('<div class="st-message-box st-error">Missing required columns.</div>', unsafe_allow_html=True); st.stop()
|
| 682 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 683 |
st.session_state.results["PredictOnly"]=df
|
| 684 |
|
|
|
|
| 709 |
round(sv["pred_std"],2),
|
| 710 |
f'{sv["oor"]:.1f}%']
|
| 711 |
})
|
| 712 |
+
st.markdown('<div class="st-message-box st-success">Predictions ready ✓</div>', unsafe_allow_html=True)
|
| 713 |
df_centered_rounded(table, hide_index=True)
|
| 714 |
st.caption("**★ OOR** = % of rows whose input features fall outside the training min–max range.")
|
| 715 |
with col_right:
|
| 716 |
st.plotly_chart(
|
| 717 |
track_plot(df, include_actual=False),
|
| 718 |
+
use_container_width=False, # Set to False to honor the width in track_plot()
|
| 719 |
+
config={"displayModeBar": False, "scrollZoom": True}
|
| 720 |
)
|
| 721 |
|
| 722 |
# =========================
|
|
|
|
| 729 |
book_to_preview = read_book_bytes(st.session_state.dev_file_bytes)
|
| 730 |
elif st.session_state.app_step in ["validate", "predict"] and up is not None:
|
| 731 |
book_to_preview = read_book_bytes(up.getvalue())
|
| 732 |
+
|
| 733 |
# Use a try-except block to handle cases where 'up' might be None
|
| 734 |
# and the logic tries to access its attributes.
|
| 735 |
try:
|
| 736 |
if st.session_state.app_step == "validate" and up is not None:
|
| 737 |
+
book_to_preview = read_book_bytes(up.getvalue())
|
| 738 |
elif st.session_state.app_step == "predict" and up is not None:
|
| 739 |
+
book_to_preview = read_book_bytes(up.getvalue())
|
| 740 |
except NameError:
|
| 741 |
book_to_preview = {}
|
| 742 |
|
| 743 |
with st.expander("Preview data", expanded=True):
|
| 744 |
if not book_to_preview:
|
| 745 |
+
st.markdown('<div class="st-message-box">No data loaded yet.</div>', unsafe_allow_html=True)
|
| 746 |
else:
|
| 747 |
names = list(book_to_preview.keys())
|
| 748 |
tabs = st.tabs(names)
|
|
|
|
| 754 |
st.pyplot(preview_tracks(df, FEATURES), use_container_width=True)
|
| 755 |
with t2:
|
| 756 |
tbl = (df[FEATURES]
|
| 757 |
+
.agg(['min','max','mean','std'])
|
| 758 |
+
.T.rename(columns={"min":"Min","max":"Max","mean":"Mean","std":"Std"}))
|
| 759 |
df_centered_rounded(tbl.reset_index(names="Feature"))
|
| 760 |
# Reset the state variable after the modal is displayed
|
| 761 |
st.session_state.show_preview_modal = False
|
| 762 |
+
|
| 763 |
# =========================
|
| 764 |
# Footer
|
| 765 |
# =========================
|