Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,14 +4,17 @@ import streamlit as st
|
|
| 4 |
import pandas as pd
|
| 5 |
import numpy as np
|
| 6 |
import joblib
|
|
|
|
|
|
|
| 7 |
import matplotlib
|
| 8 |
matplotlib.use("Agg")
|
| 9 |
import matplotlib.pyplot as plt
|
| 10 |
-
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
|
| 11 |
|
| 12 |
-
#
|
| 13 |
import plotly.graph_objects as go
|
| 14 |
|
|
|
|
|
|
|
| 15 |
# =========================
|
| 16 |
# Defaults
|
| 17 |
# =========================
|
|
@@ -95,46 +98,9 @@ def find_sheet(book, names):
|
|
| 95 |
if nm.lower() in low2orig: return low2orig[nm.lower()]
|
| 96 |
return None
|
| 97 |
|
| 98 |
-
# ----------
|
| 99 |
-
def
|
| 100 |
-
|
| 101 |
-
ax.scatter(actual, pred, s=14, alpha=0.85, color=COLORS["pred"])
|
| 102 |
-
lo = float(np.nanmin([actual.min(), pred.min()]))
|
| 103 |
-
hi = float(np.nanmax([actual.max(), pred.max()]))
|
| 104 |
-
pad = 0.03 * (hi - lo if hi > lo else 1.0)
|
| 105 |
-
ax.plot([lo - pad, hi + pad], [lo - pad, hi + pad], '--', lw=1.2, color=COLORS["ref"])
|
| 106 |
-
ax.set_xlim(lo - pad, hi + pad); ax.set_ylim(lo - pad, hi + pad)
|
| 107 |
-
ax.set_aspect('equal', 'box')
|
| 108 |
-
ax.set_xlabel("Actual UCS"); ax.set_ylabel("Predicted UCS"); ax.set_title(title)
|
| 109 |
-
ax.grid(True, ls=":", alpha=0.4)
|
| 110 |
-
return fig
|
| 111 |
-
|
| 112 |
-
def depth_or_index_track(df, title=None, include_actual=True):
|
| 113 |
-
depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
|
| 114 |
-
fig_w = 3.1
|
| 115 |
-
fig_h = 7.6 if depth_col is not None else 7.2
|
| 116 |
-
fig, ax = plt.subplots(figsize=(fig_w, fig_h), dpi=100)
|
| 117 |
-
if depth_col is not None:
|
| 118 |
-
ax.plot(df["UCS_Pred"], df[depth_col], '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
|
| 119 |
-
if include_actual and TARGET in df.columns:
|
| 120 |
-
ax.plot(df[TARGET], df[depth_col], ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
|
| 121 |
-
ax.set_ylabel(depth_col); ax.set_xlabel("UCS")
|
| 122 |
-
ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
|
| 123 |
-
else:
|
| 124 |
-
idx = np.arange(1, len(df) + 1)
|
| 125 |
-
ax.plot(df["UCS_Pred"], idx, '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
|
| 126 |
-
if include_actual and TARGET in df.columns:
|
| 127 |
-
ax.plot(df[TARGET], idx, ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
|
| 128 |
-
ax.set_ylabel("Point Index"); ax.set_xlabel("UCS")
|
| 129 |
-
ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
|
| 130 |
-
ax.grid(True, linestyle=":", alpha=0.4)
|
| 131 |
-
if title: ax.set_title(title, pad=8)
|
| 132 |
-
ax.legend(loc="best")
|
| 133 |
-
return fig
|
| 134 |
-
|
| 135 |
-
# ---------- NEW: Plotly equivalents (interactive, same specs) ----------
|
| 136 |
-
def cross_plot_interactive(actual, pred, title, size=(3.9, 3.9)):
|
| 137 |
-
"""Interactive cross-plot with the same look: blue points, dashed 1:1, equal axes, grid, title."""
|
| 138 |
a = pd.Series(actual).astype(float)
|
| 139 |
p = pd.Series(pred).astype(float)
|
| 140 |
lo = float(np.nanmin([a.min(), p.min()]))
|
|
@@ -144,96 +110,80 @@ def cross_plot_interactive(actual, pred, title, size=(3.9, 3.9)):
|
|
| 144 |
|
| 145 |
fig = go.Figure()
|
| 146 |
|
| 147 |
-
#
|
| 148 |
fig.add_trace(go.Scatter(
|
| 149 |
-
x=a, y=p,
|
| 150 |
-
mode="markers",
|
| 151 |
marker=dict(size=6, color=COLORS["pred"]),
|
| 152 |
hovertemplate="Actual: %{x:.2f}<br>Pred: %{y:.2f}<extra></extra>",
|
| 153 |
-
name="Points"
|
| 154 |
-
showlegend=False
|
| 155 |
))
|
| 156 |
|
| 157 |
-
# 1:1
|
| 158 |
fig.add_trace(go.Scatter(
|
| 159 |
-
x=[x0, x1], y=[x0, x1],
|
| 160 |
-
mode="lines",
|
| 161 |
line=dict(color=COLORS["ref"], width=1.2, dash="dash"),
|
| 162 |
-
hoverinfo="skip",
|
| 163 |
-
name="1:1",
|
| 164 |
-
showlegend=False
|
| 165 |
))
|
| 166 |
|
| 167 |
fig.update_layout(
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
margin=dict(l=50, r=10, t=36, b=36),
|
| 172 |
-
hovermode="closest",
|
| 173 |
-
font=dict(size=13)
|
| 174 |
)
|
| 175 |
fig.update_xaxes(
|
| 176 |
title_text="<b>Actual UCS</b>",
|
| 177 |
-
range=[x0, x1],
|
| 178 |
-
|
| 179 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 180 |
-
automargin=True
|
| 181 |
)
|
| 182 |
fig.update_yaxes(
|
| 183 |
title_text="<b>Predicted UCS</b>",
|
| 184 |
-
range=[x0, x1],
|
| 185 |
-
|
| 186 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 187 |
-
scaleanchor="x", scaleratio=1,
|
| 188 |
automargin=True
|
| 189 |
)
|
| 190 |
-
|
| 191 |
-
# match your size ~ inches * 100 dpi
|
| 192 |
-
w = int(size[0] * 100)
|
| 193 |
-
h = int(size[1] * 100)
|
| 194 |
fig.update_layout(width=w, height=h)
|
| 195 |
return fig
|
| 196 |
|
| 197 |
def depth_or_index_track_interactive(df, title=None, include_actual=True):
|
| 198 |
-
"""Interactive
|
| 199 |
depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
|
| 200 |
if depth_col is not None:
|
| 201 |
-
y = df[depth_col]
|
| 202 |
-
y_label = depth_col
|
| 203 |
else:
|
| 204 |
-
y = np.arange(1, len(df) + 1)
|
| 205 |
-
y_label = "Point Index"
|
| 206 |
|
| 207 |
fig = go.Figure()
|
| 208 |
|
| 209 |
# Predicted (solid blue)
|
| 210 |
fig.add_trace(go.Scatter(
|
| 211 |
-
x=df["UCS_Pred"], y=y,
|
| 212 |
-
mode="lines",
|
| 213 |
line=dict(color=COLORS["pred"], width=1.8),
|
| 214 |
name="UCS_Pred",
|
| 215 |
hovertemplate="UCS_Pred: %{x:.2f}<br>"+y_label+": %{y}<extra></extra>"
|
| 216 |
))
|
| 217 |
-
|
| 218 |
# Actual (dotted yellow)
|
| 219 |
if include_actual and TARGET in df.columns:
|
| 220 |
fig.add_trace(go.Scatter(
|
| 221 |
-
x=df[TARGET], y=y,
|
| 222 |
-
mode="lines",
|
| 223 |
line=dict(color=COLORS["actual"], width=2.0, dash="dot"),
|
| 224 |
name="UCS (actual)",
|
| 225 |
hovertemplate="UCS (actual): %{x:.2f}<br>"+y_label+": %{y}<extra></extra>"
|
| 226 |
))
|
| 227 |
|
| 228 |
fig.update_layout(
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
width=int(3.1 * 100),
|
| 238 |
height=int((7.6 if depth_col is not None else 7.2) * 100),
|
| 239 |
)
|
|
@@ -241,53 +191,17 @@ def depth_or_index_track_interactive(df, title=None, include_actual=True):
|
|
| 241 |
title_text="<b>UCS</b>", side="top",
|
| 242 |
ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
|
| 243 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 244 |
-
automargin=True
|
| 245 |
)
|
| 246 |
fig.update_yaxes(
|
| 247 |
-
title_text=f"<b>{y_label}</b>",
|
| 248 |
-
autorange="reversed",
|
| 249 |
ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
|
| 250 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 251 |
automargin=True
|
| 252 |
)
|
| 253 |
return fig
|
| 254 |
|
| 255 |
-
|
| 256 |
-
try: import openpyxl # noqa
|
| 257 |
-
except Exception: raise RuntimeError("Export requires openpyxl. Please add it to requirements or install it.")
|
| 258 |
-
buf = io.BytesIO()
|
| 259 |
-
with pd.ExcelWriter(buf, engine="openpyxl") as xw:
|
| 260 |
-
for name, frame in sheets_dict.items():
|
| 261 |
-
frame.to_excel(xw, sheet_name=name[:31], index=False)
|
| 262 |
-
if summary_df is not None: summary_df.to_excel(xw, sheet_name="Summary", index=False)
|
| 263 |
-
return buf.getvalue()
|
| 264 |
-
|
| 265 |
-
def toast(msg):
|
| 266 |
-
try: st.toast(msg)
|
| 267 |
-
except Exception: st.info(msg)
|
| 268 |
-
|
| 269 |
-
def infer_features_from_model(m):
|
| 270 |
-
try:
|
| 271 |
-
if hasattr(m, "feature_names_in_") and len(getattr(m, "feature_names_in_")):
|
| 272 |
-
return [str(x) for x in m.feature_names_in_]
|
| 273 |
-
except Exception: pass
|
| 274 |
-
try:
|
| 275 |
-
if hasattr(m, "steps") and len(m.steps):
|
| 276 |
-
last = m.steps[-1][1]
|
| 277 |
-
if hasattr(last, "feature_names_in_") and len(last.feature_names_in_):
|
| 278 |
-
return [str(x) for x in last.feature_names_in_]
|
| 279 |
-
except Exception: pass
|
| 280 |
-
return None
|
| 281 |
-
|
| 282 |
-
def inline_logo(path="logo.png") -> str:
|
| 283 |
-
try:
|
| 284 |
-
p = Path(path)
|
| 285 |
-
if not p.exists(): return ""
|
| 286 |
-
return f"data:image/png;base64,{base64.b64encode(p.read_bytes()).decode('ascii')}"
|
| 287 |
-
except Exception:
|
| 288 |
-
return ""
|
| 289 |
-
|
| 290 |
-
# ---------- Preview modal helpers (unchanged; still Matplotlib) ----------
|
| 291 |
def make_index_tracks(df: pd.DataFrame, cols: list[str]):
|
| 292 |
cols = [c for c in cols if c in df.columns]
|
| 293 |
n = len(cols)
|
|
@@ -390,7 +304,12 @@ if meta_path.exists():
|
|
| 390 |
FEATURES = meta.get("features", FEATURES); TARGET = meta.get("target", TARGET)
|
| 391 |
except Exception: pass
|
| 392 |
else:
|
| 393 |
-
infer =
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
if infer: FEATURES = infer
|
| 395 |
|
| 396 |
# =========================
|
|
@@ -485,7 +404,6 @@ if st.session_state.app_step == "dev":
|
|
| 485 |
st.session_state.dev_file_signature = sig
|
| 486 |
st.session_state.dev_file_name = train_test_file.name
|
| 487 |
st.session_state.dev_file_bytes = file_bytes
|
| 488 |
-
# Inspect first sheet for rows/cols
|
| 489 |
_book_tmp = read_book_bytes(file_bytes)
|
| 490 |
if _book_tmp:
|
| 491 |
first_df = next(iter(_book_tmp.values()))
|
|
@@ -530,7 +448,7 @@ if st.session_state.app_step == "dev":
|
|
| 530 |
else:
|
| 531 |
st.write("**Upload your data to build a case, then run the model to review development performance.**")
|
| 532 |
|
| 533 |
-
# If user clicked preview, open modal
|
| 534 |
if st.session_state.dev_preview_request and st.session_state.dev_file_bytes:
|
| 535 |
_book = read_book_bytes(st.session_state.dev_file_bytes)
|
| 536 |
st.session_state.dev_previewed = True
|
|
@@ -571,10 +489,9 @@ if st.session_state.app_step == "dev":
|
|
| 571 |
st.session_state.train_ranges = {f:(float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
| 572 |
|
| 573 |
st.session_state.dev_ready = True
|
| 574 |
-
status.update(label="Done ✓", state="complete");
|
| 575 |
-
st.rerun()
|
| 576 |
|
| 577 |
-
# Results (
|
| 578 |
if ("Train" in st.session_state.results) or ("Test" in st.session_state.results):
|
| 579 |
tab1, tab2 = st.tabs(["Training", "Testing"])
|
| 580 |
if "Train" in st.session_state.results:
|
|
@@ -585,7 +502,7 @@ if st.session_state.app_step == "dev":
|
|
| 585 |
left, right = st.columns([0.9, 0.55])
|
| 586 |
with left:
|
| 587 |
st.plotly_chart(
|
| 588 |
-
cross_plot_interactive(df[TARGET], df["UCS_Pred"],
|
| 589 |
use_container_width=True, config={"displayModeBar": False}
|
| 590 |
)
|
| 591 |
with right:
|
|
@@ -601,7 +518,7 @@ if st.session_state.app_step == "dev":
|
|
| 601 |
left, right = st.columns([0.9, 0.55])
|
| 602 |
with left:
|
| 603 |
st.plotly_chart(
|
| 604 |
-
cross_plot_interactive(df[TARGET], df["UCS_Pred"],
|
| 605 |
use_container_width=True, config={"displayModeBar": False}
|
| 606 |
)
|
| 607 |
with right:
|
|
@@ -620,11 +537,17 @@ if st.session_state.app_step == "dev":
|
|
| 620 |
rows.append({"Split":"Test", **{k:round(v,6) for k,v in st.session_state.results["metrics_test"].items()}})
|
| 621 |
summary_df = pd.DataFrame(rows) if rows else None
|
| 622 |
try:
|
| 623 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 624 |
st.download_button("Export Development Results to Excel",
|
| 625 |
-
data=
|
| 626 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
| 627 |
-
except
|
| 628 |
st.warning(str(e))
|
| 629 |
|
| 630 |
# =========================
|
|
@@ -703,10 +626,11 @@ if st.session_state.app_step == "predict":
|
|
| 703 |
with left:
|
| 704 |
if TARGET in st.session_state.results["Validate"].columns:
|
| 705 |
st.plotly_chart(
|
| 706 |
-
cross_plot_interactive(
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
|
|
|
| 710 |
use_container_width=True, config={"displayModeBar": False}
|
| 711 |
)
|
| 712 |
else:
|
|
@@ -714,7 +638,8 @@ if st.session_state.app_step == "predict":
|
|
| 714 |
with right:
|
| 715 |
st.plotly_chart(
|
| 716 |
depth_or_index_track_interactive(
|
| 717 |
-
st.session_state.results["Validate"],
|
|
|
|
| 718 |
include_actual=(TARGET in st.session_state.results["Validate"].columns)
|
| 719 |
),
|
| 720 |
use_container_width=True, config={"displayModeBar": False}
|
|
@@ -732,11 +657,17 @@ if st.session_state.app_step == "predict":
|
|
| 732 |
if m: rows.append({"Split": name, **{k: round(v,6) for k,v in m.items()}})
|
| 733 |
summary_df = pd.DataFrame(rows) if rows else None
|
| 734 |
try:
|
| 735 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 736 |
st.download_button("Export Validation Results to Excel",
|
| 737 |
-
data=
|
| 738 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
| 739 |
-
except
|
| 740 |
st.warning(str(e))
|
| 741 |
|
| 742 |
# =========================
|
|
|
|
| 4 |
import pandas as pd
|
| 5 |
import numpy as np
|
| 6 |
import joblib
|
| 7 |
+
|
| 8 |
+
# Matplotlib remains for the preview modal (tracks + stats)
|
| 9 |
import matplotlib
|
| 10 |
matplotlib.use("Agg")
|
| 11 |
import matplotlib.pyplot as plt
|
|
|
|
| 12 |
|
| 13 |
+
# Plotly for the main interactive charts
|
| 14 |
import plotly.graph_objects as go
|
| 15 |
|
| 16 |
+
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
|
| 17 |
+
|
| 18 |
# =========================
|
| 19 |
# Defaults
|
| 20 |
# =========================
|
|
|
|
| 98 |
if nm.lower() in low2orig: return low2orig[nm.lower()]
|
| 99 |
return None
|
| 100 |
|
| 101 |
+
# ---------- Plotly (interactive) charts ----------
|
| 102 |
+
def cross_plot_interactive(actual, pred, size=(3.9, 3.9)):
|
| 103 |
+
"""Interactive cross-plot: blue points, dashed 1:1, equal axes, no title, numeric ticks."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
a = pd.Series(actual).astype(float)
|
| 105 |
p = pd.Series(pred).astype(float)
|
| 106 |
lo = float(np.nanmin([a.min(), p.min()]))
|
|
|
|
| 110 |
|
| 111 |
fig = go.Figure()
|
| 112 |
|
| 113 |
+
# Points (blue)
|
| 114 |
fig.add_trace(go.Scatter(
|
| 115 |
+
x=a, y=p, mode="markers",
|
|
|
|
| 116 |
marker=dict(size=6, color=COLORS["pred"]),
|
| 117 |
hovertemplate="Actual: %{x:.2f}<br>Pred: %{y:.2f}<extra></extra>",
|
| 118 |
+
showlegend=False, name="Points"
|
|
|
|
| 119 |
))
|
| 120 |
|
| 121 |
+
# 1:1 line (dashed grey)
|
| 122 |
fig.add_trace(go.Scatter(
|
| 123 |
+
x=[x0, x1], y=[x0, x1], mode="lines",
|
|
|
|
| 124 |
line=dict(color=COLORS["ref"], width=1.2, dash="dash"),
|
| 125 |
+
hoverinfo="skip", showlegend=False, name="1:1"
|
|
|
|
|
|
|
| 126 |
))
|
| 127 |
|
| 128 |
fig.update_layout(
|
| 129 |
+
paper_bgcolor="#ffffff", plot_bgcolor="#ffffff",
|
| 130 |
+
margin=dict(l=50, r=10, t=10, b=36),
|
| 131 |
+
hovermode="closest", font=dict(size=13)
|
|
|
|
|
|
|
|
|
|
| 132 |
)
|
| 133 |
fig.update_xaxes(
|
| 134 |
title_text="<b>Actual UCS</b>",
|
| 135 |
+
range=[x0, x1], ticks="outside",
|
| 136 |
+
showline=True, linewidth=1.2, linecolor="#444",
|
| 137 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 138 |
+
tickformat=",.0f", automargin=True
|
| 139 |
)
|
| 140 |
fig.update_yaxes(
|
| 141 |
title_text="<b>Predicted UCS</b>",
|
| 142 |
+
range=[x0, x1], ticks="outside",
|
| 143 |
+
showline=True, linewidth=1.2, linecolor="#444",
|
| 144 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 145 |
+
tickformat=",.0f", scaleanchor="x", scaleratio=1,
|
| 146 |
automargin=True
|
| 147 |
)
|
| 148 |
+
w = int(size[0] * 100); h = int(size[1] * 100)
|
|
|
|
|
|
|
|
|
|
| 149 |
fig.update_layout(width=w, height=h)
|
| 150 |
return fig
|
| 151 |
|
| 152 |
def depth_or_index_track_interactive(df, title=None, include_actual=True):
|
| 153 |
+
"""Interactive UCS track: blue solid pred, yellow dotted actual, legend inside; x on top; y inverted."""
|
| 154 |
depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
|
| 155 |
if depth_col is not None:
|
| 156 |
+
y = df[depth_col]; y_label = depth_col
|
|
|
|
| 157 |
else:
|
| 158 |
+
y = np.arange(1, len(df) + 1); y_label = "Point Index"
|
|
|
|
| 159 |
|
| 160 |
fig = go.Figure()
|
| 161 |
|
| 162 |
# Predicted (solid blue)
|
| 163 |
fig.add_trace(go.Scatter(
|
| 164 |
+
x=df["UCS_Pred"], y=y, mode="lines",
|
|
|
|
| 165 |
line=dict(color=COLORS["pred"], width=1.8),
|
| 166 |
name="UCS_Pred",
|
| 167 |
hovertemplate="UCS_Pred: %{x:.2f}<br>"+y_label+": %{y}<extra></extra>"
|
| 168 |
))
|
|
|
|
| 169 |
# Actual (dotted yellow)
|
| 170 |
if include_actual and TARGET in df.columns:
|
| 171 |
fig.add_trace(go.Scatter(
|
| 172 |
+
x=df[TARGET], y=y, mode="lines",
|
|
|
|
| 173 |
line=dict(color=COLORS["actual"], width=2.0, dash="dot"),
|
| 174 |
name="UCS (actual)",
|
| 175 |
hovertemplate="UCS (actual): %{x:.2f}<br>"+y_label+": %{y}<extra></extra>"
|
| 176 |
))
|
| 177 |
|
| 178 |
fig.update_layout(
|
| 179 |
+
paper_bgcolor="#ffffff", plot_bgcolor="#ffffff",
|
| 180 |
+
margin=dict(l=60, r=10, t=10, b=36),
|
| 181 |
+
hovermode="closest", font=dict(size=13),
|
| 182 |
+
legend=dict(
|
| 183 |
+
x=0.98, y=0.05, xanchor="right", yanchor="bottom",
|
| 184 |
+
bgcolor="rgba(255,255,255,0.75)", bordercolor="#cccccc", borderwidth=1
|
| 185 |
+
),
|
| 186 |
+
legend_title_text="",
|
| 187 |
width=int(3.1 * 100),
|
| 188 |
height=int((7.6 if depth_col is not None else 7.2) * 100),
|
| 189 |
)
|
|
|
|
| 191 |
title_text="<b>UCS</b>", side="top",
|
| 192 |
ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
|
| 193 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 194 |
+
tickformat=",.0f", automargin=True
|
| 195 |
)
|
| 196 |
fig.update_yaxes(
|
| 197 |
+
title_text=f"<b>{y_label}</b>", autorange="reversed",
|
|
|
|
| 198 |
ticks="outside", showline=True, linewidth=1.2, linecolor="#444",
|
| 199 |
showgrid=True, gridcolor="rgba(0,0,0,0.12)",
|
| 200 |
automargin=True
|
| 201 |
)
|
| 202 |
return fig
|
| 203 |
|
| 204 |
+
# ---------- Preview modal helpers (matplotlib) ----------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
def make_index_tracks(df: pd.DataFrame, cols: list[str]):
|
| 206 |
cols = [c for c in cols if c in df.columns]
|
| 207 |
n = len(cols)
|
|
|
|
| 304 |
FEATURES = meta.get("features", FEATURES); TARGET = meta.get("target", TARGET)
|
| 305 |
except Exception: pass
|
| 306 |
else:
|
| 307 |
+
infer = None
|
| 308 |
+
try:
|
| 309 |
+
if hasattr(model, "feature_names_in_") and len(getattr(model, "feature_names_in_")):
|
| 310 |
+
infer = [str(x) for x in model.feature_names_in_]
|
| 311 |
+
except Exception:
|
| 312 |
+
pass
|
| 313 |
if infer: FEATURES = infer
|
| 314 |
|
| 315 |
# =========================
|
|
|
|
| 404 |
st.session_state.dev_file_signature = sig
|
| 405 |
st.session_state.dev_file_name = train_test_file.name
|
| 406 |
st.session_state.dev_file_bytes = file_bytes
|
|
|
|
| 407 |
_book_tmp = read_book_bytes(file_bytes)
|
| 408 |
if _book_tmp:
|
| 409 |
first_df = next(iter(_book_tmp.values()))
|
|
|
|
| 448 |
else:
|
| 449 |
st.write("**Upload your data to build a case, then run the model to review development performance.**")
|
| 450 |
|
| 451 |
+
# If user clicked preview, open modal after helper (so helper stays on top)
|
| 452 |
if st.session_state.dev_preview_request and st.session_state.dev_file_bytes:
|
| 453 |
_book = read_book_bytes(st.session_state.dev_file_bytes)
|
| 454 |
st.session_state.dev_previewed = True
|
|
|
|
| 489 |
st.session_state.train_ranges = {f:(float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
| 490 |
|
| 491 |
st.session_state.dev_ready = True
|
| 492 |
+
status.update(label="Done ✓", state="complete"); st.rerun()
|
|
|
|
| 493 |
|
| 494 |
+
# Results (if available)
|
| 495 |
if ("Train" in st.session_state.results) or ("Test" in st.session_state.results):
|
| 496 |
tab1, tab2 = st.tabs(["Training", "Testing"])
|
| 497 |
if "Train" in st.session_state.results:
|
|
|
|
| 502 |
left, right = st.columns([0.9, 0.55])
|
| 503 |
with left:
|
| 504 |
st.plotly_chart(
|
| 505 |
+
cross_plot_interactive(df[TARGET], df["UCS_Pred"], size=(3.9,3.9)),
|
| 506 |
use_container_width=True, config={"displayModeBar": False}
|
| 507 |
)
|
| 508 |
with right:
|
|
|
|
| 518 |
left, right = st.columns([0.9, 0.55])
|
| 519 |
with left:
|
| 520 |
st.plotly_chart(
|
| 521 |
+
cross_plot_interactive(df[TARGET], df["UCS_Pred"], size=(3.9,3.9)),
|
| 522 |
use_container_width=True, config={"displayModeBar": False}
|
| 523 |
)
|
| 524 |
with right:
|
|
|
|
| 537 |
rows.append({"Split":"Test", **{k:round(v,6) for k,v in st.session_state.results["metrics_test"].items()}})
|
| 538 |
summary_df = pd.DataFrame(rows) if rows else None
|
| 539 |
try:
|
| 540 |
+
buf = io.BytesIO()
|
| 541 |
+
import openpyxl # noqa
|
| 542 |
+
with pd.ExcelWriter(buf, engine="openpyxl") as xw:
|
| 543 |
+
for name, frame in sheets.items():
|
| 544 |
+
frame.to_excel(xw, sheet_name=name[:31], index=False)
|
| 545 |
+
if summary_df is not None:
|
| 546 |
+
summary_df.to_excel(xw, sheet_name="Summary", index=False)
|
| 547 |
st.download_button("Export Development Results to Excel",
|
| 548 |
+
data=buf.getvalue(), file_name="UCS_Dev_Results.xlsx",
|
| 549 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
| 550 |
+
except Exception as e:
|
| 551 |
st.warning(str(e))
|
| 552 |
|
| 553 |
# =========================
|
|
|
|
| 626 |
with left:
|
| 627 |
if TARGET in st.session_state.results["Validate"].columns:
|
| 628 |
st.plotly_chart(
|
| 629 |
+
cross_plot_interactive(
|
| 630 |
+
st.session_state.results["Validate"][TARGET],
|
| 631 |
+
st.session_state.results["Validate"]["UCS_Pred"],
|
| 632 |
+
size=(3.9,3.9)
|
| 633 |
+
),
|
| 634 |
use_container_width=True, config={"displayModeBar": False}
|
| 635 |
)
|
| 636 |
else:
|
|
|
|
| 638 |
with right:
|
| 639 |
st.plotly_chart(
|
| 640 |
depth_or_index_track_interactive(
|
| 641 |
+
st.session_state.results["Validate"],
|
| 642 |
+
title=None,
|
| 643 |
include_actual=(TARGET in st.session_state.results["Validate"].columns)
|
| 644 |
),
|
| 645 |
use_container_width=True, config={"displayModeBar": False}
|
|
|
|
| 657 |
if m: rows.append({"Split": name, **{k: round(v,6) for k,v in m.items()}})
|
| 658 |
summary_df = pd.DataFrame(rows) if rows else None
|
| 659 |
try:
|
| 660 |
+
buf = io.BytesIO()
|
| 661 |
+
import openpyxl # noqa
|
| 662 |
+
with pd.ExcelWriter(buf, engine="openpyxl") as xw:
|
| 663 |
+
for nm, fr in sheets.items():
|
| 664 |
+
fr.to_excel(xw, sheet_name=nm[:31], index=False)
|
| 665 |
+
if summary_df is not None:
|
| 666 |
+
summary_df.to_excel(xw, sheet_name="Summary", index=False)
|
| 667 |
st.download_button("Export Validation Results to Excel",
|
| 668 |
+
data=buf.getvalue(), file_name="UCS_Validation_Results.xlsx",
|
| 669 |
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
| 670 |
+
except Exception as e:
|
| 671 |
st.warning(str(e))
|
| 672 |
|
| 673 |
# =========================
|