Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -6,7 +6,7 @@ import pandas as pd
|
|
| 6 |
import numpy as np
|
| 7 |
import joblib
|
| 8 |
|
| 9 |
-
#
|
| 10 |
import matplotlib
|
| 11 |
matplotlib.use("Agg")
|
| 12 |
import matplotlib.pyplot as plt
|
|
@@ -26,14 +26,13 @@ MODEL_FALLBACKS = [MODELS_DIR / "model.joblib", MODELS_DIR / "model.pkl"]
|
|
| 26 |
COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
|
| 27 |
|
| 28 |
# ---- Plot sizing controls ----
|
| 29 |
-
CROSS_W = None #
|
| 30 |
CROSS_H = 420
|
| 31 |
-
TRACK_W =
|
| 32 |
TRACK_H = 820
|
| 33 |
FONT_SZ = 15
|
| 34 |
-
PLOT_COLS = [42, 2, 30]
|
| 35 |
-
CROSS_NUDGE = 0.0
|
| 36 |
-
|
| 37 |
|
| 38 |
# =========================
|
| 39 |
# Page / CSS
|
|
@@ -41,40 +40,32 @@ CROSS_NUDGE = 0.0
|
|
| 41 |
st.set_page_config(page_title="ST_GeoMech_UCS", page_icon="logo.png", layout="wide")
|
| 42 |
st.markdown("""
|
| 43 |
<style>
|
| 44 |
-
|
| 45 |
-
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
</style>
|
| 52 |
""", unsafe_allow_html=True)
|
| 53 |
-
|
| 54 |
-
# Hide file-uploader helper text + center dataframes (headers & cells)
|
| 55 |
st.markdown("""
|
| 56 |
<style>
|
| 57 |
-
/* Keep the uploader and its Browse button, but hide the drag/drop helper lines */
|
| 58 |
-
|
| 59 |
/* Older builds (helper wrapped in a Markdown container) */
|
| 60 |
-
section[data-testid="stFileUploader"] div[data-testid="stMarkdownContainer"] {
|
| 61 |
-
display: none !important;
|
| 62 |
-
}
|
| 63 |
-
|
| 64 |
/* 1.31–1.34 style (first child inside the dropzone is the helper row) */
|
| 65 |
-
section[data-testid="stFileUploader"] [data-testid="stFileUploaderDropzone"] > div:first-child {
|
| 66 |
-
display: none !important;
|
| 67 |
-
}
|
| 68 |
-
|
| 69 |
/* 1.35+ explicit helper container */
|
| 70 |
-
section[data-testid="stFileUploader"] [data-testid="stFileUploaderInstructions"] {
|
| 71 |
-
|
| 72 |
-
}
|
| 73 |
-
|
| 74 |
-
/*
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
| 78 |
}
|
| 79 |
</style>
|
| 80 |
""", unsafe_allow_html=True)
|
|
@@ -85,7 +76,8 @@ section[data-testid="stFileUploader"] small {
|
|
| 85 |
def inline_logo(path="logo.png") -> str:
|
| 86 |
try:
|
| 87 |
p = Path(path)
|
| 88 |
-
if not p.exists():
|
|
|
|
| 89 |
return f"data:image/png;base64,{base64.b64encode(p.read_bytes()).decode('ascii')}"
|
| 90 |
except Exception:
|
| 91 |
return ""
|
|
@@ -103,7 +95,7 @@ def add_password_gate() -> None:
|
|
| 103 |
if st.session_state.get("auth_ok", False):
|
| 104 |
return
|
| 105 |
|
| 106 |
-
st.sidebar.image("logo.png", use_column_width=
|
| 107 |
st.sidebar.markdown("### ST_GeoMech_UCS\nSmart Thinking • Secure Access")
|
| 108 |
pwd = st.sidebar.text_input("Access key", type="password", placeholder="••••••••")
|
| 109 |
if st.sidebar.button("Unlock", type="primary"):
|
|
@@ -136,7 +128,8 @@ def rmse(y_true, y_pred) -> float:
|
|
| 136 |
def pearson_r(y_true, y_pred) -> float:
|
| 137 |
a = np.asarray(y_true, dtype=float)
|
| 138 |
p = np.asarray(y_pred, dtype=float)
|
| 139 |
-
if a.size < 2:
|
|
|
|
| 140 |
return float(np.corrcoef(a, p)[0, 1])
|
| 141 |
|
| 142 |
@st.cache_resource(show_spinner=False)
|
|
@@ -149,7 +142,8 @@ def parse_excel(data_bytes: bytes):
|
|
| 149 |
xl = pd.ExcelFile(bio)
|
| 150 |
return {sh: xl.parse(sh) for sh in xl.sheet_names}
|
| 151 |
|
| 152 |
-
def read_book_bytes(b: bytes):
|
|
|
|
| 153 |
|
| 154 |
def ensure_cols(df, cols):
|
| 155 |
miss = [c for c in cols if c not in df.columns]
|
|
@@ -161,15 +155,13 @@ def ensure_cols(df, cols):
|
|
| 161 |
def find_sheet(book, names):
|
| 162 |
low2orig = {k.lower(): k for k in book.keys()}
|
| 163 |
for nm in names:
|
| 164 |
-
if nm.lower() in low2orig:
|
|
|
|
| 165 |
return None
|
| 166 |
|
| 167 |
def _nice_tick0(xmin: float, step: int = 100) -> float:
|
| 168 |
return step * math.floor(xmin / step) if np.isfinite(xmin) else xmin
|
| 169 |
|
| 170 |
-
# =========================
|
| 171 |
-
# Cross-plot (Matplotlib, static)
|
| 172 |
-
# =========================
|
| 173 |
# =========================
|
| 174 |
# Cross plot (Matplotlib, fixed limits & ticks)
|
| 175 |
# =========================
|
|
@@ -180,11 +172,10 @@ def cross_plot_static(actual, pred):
|
|
| 180 |
fixed_min, fixed_max = 6000, 10000
|
| 181 |
ticks = np.arange(fixed_min, fixed_max + 1, 1000)
|
| 182 |
|
| 183 |
-
# use constrained_layout + generous margins so big labels never overlap
|
| 184 |
fig, ax = plt.subplots(
|
| 185 |
-
figsize=((CROSS_W or 500)/100, (CROSS_H or
|
| 186 |
dpi=100,
|
| 187 |
-
constrained_layout=
|
| 188 |
)
|
| 189 |
|
| 190 |
# points
|
|
@@ -220,20 +211,90 @@ def cross_plot_static(actual, pred):
|
|
| 220 |
|
| 221 |
# extra room for the large labels
|
| 222 |
fig.subplots_adjust(left=0.23, bottom=0.20, right=0.98, top=0.98)
|
| 223 |
-
|
| 224 |
return fig
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 237 |
|
| 238 |
@dialog("Preview data")
|
| 239 |
def preview_modal(book: dict[str, pd.DataFrame]):
|
|
@@ -304,7 +365,7 @@ st.session_state.setdefault("dev_preview",False)
|
|
| 304 |
# =========================
|
| 305 |
# Branding in Sidebar
|
| 306 |
# =========================
|
| 307 |
-
st.sidebar.image("logo.png", use_column_width=
|
| 308 |
st.sidebar.markdown(
|
| 309 |
"<div style='font-weight:800;font-size:1.2rem;'>ST_GeoMech_UCS</div>"
|
| 310 |
"<div style='color:#667085;'>Real-Time UCS Tracking While Drilling</div>",
|
|
@@ -396,10 +457,13 @@ if st.session_state.app_step == "dev":
|
|
| 396 |
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 397 |
|
| 398 |
left, spacer, right = st.columns(PLOT_COLS)
|
| 399 |
-
|
|
|
|
| 400 |
pad, plotcol = left.columns([CROSS_NUDGE, 1])
|
| 401 |
-
|
| 402 |
-
|
|
|
|
|
|
|
| 403 |
with right:
|
| 404 |
st.plotly_chart(
|
| 405 |
track_plot(df, include_actual=True),
|
|
@@ -451,7 +515,9 @@ if st.session_state.app_step == "validate":
|
|
| 451 |
for c in FEATURES:
|
| 452 |
if pd.api.types.is_numeric_dtype(tbl[c]):
|
| 453 |
tbl[c] = tbl[c].round(2)
|
| 454 |
-
tbl["Violations"] = pd.DataFrame({f:(df[f]<ranges[f][0])|(df[f]>ranges[f][1]) for f in FEATURES}).loc[any_viol].apply(
|
|
|
|
|
|
|
| 455 |
st.session_state.results["m_val"]={
|
| 456 |
"R": pearson_r(df[TARGET], df["UCS_Pred"]),
|
| 457 |
"RMSE": rmse(df[TARGET], df["UCS_Pred"]),
|
|
@@ -466,14 +532,16 @@ if st.session_state.app_step == "validate":
|
|
| 466 |
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 467 |
|
| 468 |
left, spacer, right = st.columns(PLOT_COLS)
|
| 469 |
-
|
| 470 |
pad, plotcol = left.columns([CROSS_NUDGE, 1])
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
|
|
|
|
|
|
| 477 |
with right:
|
| 478 |
st.plotly_chart(
|
| 479 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|
|
|
|
| 6 |
import numpy as np
|
| 7 |
import joblib
|
| 8 |
|
| 9 |
+
# Matplotlib for PREVIEW modal and CROSS-PLOT (static)
|
| 10 |
import matplotlib
|
| 11 |
matplotlib.use("Agg")
|
| 12 |
import matplotlib.pyplot as plt
|
|
|
|
| 26 |
COLORS = {"pred": "#1f77b4", "actual": "#f2b702", "ref": "#5a5a5a"}
|
| 27 |
|
| 28 |
# ---- Plot sizing controls ----
|
| 29 |
+
CROSS_W = None # Matplotlib cross-plot width (in px) — None -> fallback used in fn
|
| 30 |
CROSS_H = 420
|
| 31 |
+
TRACK_W = 380 # Plotly track width in px (kept modest to avoid overlap)
|
| 32 |
TRACK_H = 820
|
| 33 |
FONT_SZ = 15
|
| 34 |
+
PLOT_COLS = [42, 2, 30] # left • spacer • right
|
| 35 |
+
CROSS_NUDGE = 0.0 # set >0 to shove cross-plot inward with a small left pad column
|
|
|
|
| 36 |
|
| 37 |
# =========================
|
| 38 |
# Page / CSS
|
|
|
|
| 40 |
st.set_page_config(page_title="ST_GeoMech_UCS", page_icon="logo.png", layout="wide")
|
| 41 |
st.markdown("""
|
| 42 |
<style>
|
| 43 |
+
/* Reusable logo style */
|
| 44 |
+
.brand-logo { width: 16px; height: auto; object-fit: contain; }
|
| 45 |
|
| 46 |
+
/* Sidebar header layout */
|
| 47 |
+
.sidebar-header { display:flex; align-items:center; gap:12px; }
|
| 48 |
+
.sidebar-header .text h1 { font-size: 1.05rem; margin:0; line-height:1.1; }
|
| 49 |
+
.sidebar-header .text .tag { font-size: .85rem; color:#6b7280; margin:2px 0 0; }
|
| 50 |
</style>
|
| 51 |
""", unsafe_allow_html=True)
|
| 52 |
+
# Hide file-uploader helper text; keep only Browse button
|
|
|
|
| 53 |
st.markdown("""
|
| 54 |
<style>
|
|
|
|
|
|
|
| 55 |
/* Older builds (helper wrapped in a Markdown container) */
|
| 56 |
+
section[data-testid="stFileUploader"] div[data-testid="stMarkdownContainer"] { display:none !important; }
|
|
|
|
|
|
|
|
|
|
| 57 |
/* 1.31–1.34 style (first child inside the dropzone is the helper row) */
|
| 58 |
+
section[data-testid="stFileUploader"] [data-testid="stFileUploaderDropzone"] > div:first-child { display:none !important; }
|
|
|
|
|
|
|
|
|
|
| 59 |
/* 1.35+ explicit helper container */
|
| 60 |
+
section[data-testid="stFileUploader"] [data-testid="stFileUploaderInstructions"] { display:none !important; }
|
| 61 |
+
/* Fallback: any paragraph/small helper inside uploader */
|
| 62 |
+
section[data-testid="stFileUploader"] p, section[data-testid="stFileUploader"] small { display:none !important; }
|
| 63 |
+
|
| 64 |
+
/* Center headers & cells in all st.dataframe tables */
|
| 65 |
+
[data-testid="stDataFrame"] table td,
|
| 66 |
+
[data-testid="stDataFrame"] table th {
|
| 67 |
+
text-align: center !important;
|
| 68 |
+
vertical-align: middle !important;
|
| 69 |
}
|
| 70 |
</style>
|
| 71 |
""", unsafe_allow_html=True)
|
|
|
|
| 76 |
def inline_logo(path="logo.png") -> str:
|
| 77 |
try:
|
| 78 |
p = Path(path)
|
| 79 |
+
if not p.exists():
|
| 80 |
+
return ""
|
| 81 |
return f"data:image/png;base64,{base64.b64encode(p.read_bytes()).decode('ascii')}"
|
| 82 |
except Exception:
|
| 83 |
return ""
|
|
|
|
| 95 |
if st.session_state.get("auth_ok", False):
|
| 96 |
return
|
| 97 |
|
| 98 |
+
st.sidebar.image("logo.png", use_column_width=False)
|
| 99 |
st.sidebar.markdown("### ST_GeoMech_UCS\nSmart Thinking • Secure Access")
|
| 100 |
pwd = st.sidebar.text_input("Access key", type="password", placeholder="••••••••")
|
| 101 |
if st.sidebar.button("Unlock", type="primary"):
|
|
|
|
| 128 |
def pearson_r(y_true, y_pred) -> float:
|
| 129 |
a = np.asarray(y_true, dtype=float)
|
| 130 |
p = np.asarray(y_pred, dtype=float)
|
| 131 |
+
if a.size < 2:
|
| 132 |
+
return float("nan")
|
| 133 |
return float(np.corrcoef(a, p)[0, 1])
|
| 134 |
|
| 135 |
@st.cache_resource(show_spinner=False)
|
|
|
|
| 142 |
xl = pd.ExcelFile(bio)
|
| 143 |
return {sh: xl.parse(sh) for sh in xl.sheet_names}
|
| 144 |
|
| 145 |
+
def read_book_bytes(b: bytes):
|
| 146 |
+
return parse_excel(b) if b else {}
|
| 147 |
|
| 148 |
def ensure_cols(df, cols):
|
| 149 |
miss = [c for c in cols if c not in df.columns]
|
|
|
|
| 155 |
def find_sheet(book, names):
|
| 156 |
low2orig = {k.lower(): k for k in book.keys()}
|
| 157 |
for nm in names:
|
| 158 |
+
if nm.lower() in low2orig:
|
| 159 |
+
return low2orig[nm.lower()]
|
| 160 |
return None
|
| 161 |
|
| 162 |
def _nice_tick0(xmin: float, step: int = 100) -> float:
|
| 163 |
return step * math.floor(xmin / step) if np.isfinite(xmin) else xmin
|
| 164 |
|
|
|
|
|
|
|
|
|
|
| 165 |
# =========================
|
| 166 |
# Cross plot (Matplotlib, fixed limits & ticks)
|
| 167 |
# =========================
|
|
|
|
| 172 |
fixed_min, fixed_max = 6000, 10000
|
| 173 |
ticks = np.arange(fixed_min, fixed_max + 1, 1000)
|
| 174 |
|
|
|
|
| 175 |
fig, ax = plt.subplots(
|
| 176 |
+
figsize=((CROSS_W or 500)/100, (CROSS_H or 420)/100),
|
| 177 |
dpi=100,
|
| 178 |
+
constrained_layout=False
|
| 179 |
)
|
| 180 |
|
| 181 |
# points
|
|
|
|
| 211 |
|
| 212 |
# extra room for the large labels
|
| 213 |
fig.subplots_adjust(left=0.23, bottom=0.20, right=0.98, top=0.98)
|
|
|
|
| 214 |
return fig
|
| 215 |
|
| 216 |
+
# =========================
|
| 217 |
+
# Track plot (Plotly)
|
| 218 |
+
# =========================
|
| 219 |
+
def track_plot(df, include_actual=True):
|
| 220 |
+
depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
|
| 221 |
+
if depth_col is not None:
|
| 222 |
+
y = pd.Series(df[depth_col]).astype(float)
|
| 223 |
+
ylab = depth_col
|
| 224 |
+
y_range = [float(y.max()), float(y.min())] # reverse
|
| 225 |
+
else:
|
| 226 |
+
y = pd.Series(np.arange(1, len(df) + 1))
|
| 227 |
+
ylab = "Point Index"
|
| 228 |
+
y_range = [float(y.max()), float(y.min())]
|
| 229 |
+
|
| 230 |
+
# X (UCS) range & ticks
|
| 231 |
+
x_series = pd.Series(df.get("UCS_Pred", pd.Series(dtype=float))).astype(float)
|
| 232 |
+
if include_actual and TARGET in df.columns:
|
| 233 |
+
x_series = pd.concat([x_series, pd.Series(df[TARGET]).astype(float)], ignore_index=True)
|
| 234 |
+
x_lo, x_hi = float(x_series.min()), float(x_series.max())
|
| 235 |
+
x_pad = 0.03 * (x_hi - x_lo if x_hi > x_lo else 1.0)
|
| 236 |
+
xmin, xmax = x_lo - x_pad, x_hi + x_pad
|
| 237 |
+
tick0 = _nice_tick0(xmin, step=100)
|
| 238 |
+
|
| 239 |
+
fig = go.Figure()
|
| 240 |
+
fig.add_trace(go.Scatter(
|
| 241 |
+
x=df["UCS_Pred"], y=y, mode="lines",
|
| 242 |
+
line=dict(color=COLORS["pred"], width=1.8),
|
| 243 |
+
name="UCS_Pred",
|
| 244 |
+
hovertemplate="UCS_Pred: %{x:.0f}<br>"+ylab+": %{y}<extra></extra>"
|
| 245 |
+
))
|
| 246 |
+
if include_actual and TARGET in df.columns:
|
| 247 |
+
fig.add_trace(go.Scatter(
|
| 248 |
+
x=df[TARGET], y=y, mode="lines",
|
| 249 |
+
line=dict(color=COLORS["actual"], width=2.0, dash="dot"),
|
| 250 |
+
name="UCS (actual)",
|
| 251 |
+
hovertemplate="UCS (actual): %{x:.0f}<br>"+ylab+": %{y}<extra></extra>"
|
| 252 |
+
))
|
| 253 |
+
|
| 254 |
+
fig.update_layout(
|
| 255 |
+
width=TRACK_W, height=TRACK_H, paper_bgcolor="#fff", plot_bgcolor="#fff",
|
| 256 |
+
margin=dict(l=72, r=18, t=36, b=48), hovermode="closest",
|
| 257 |
+
font=dict(size=FONT_SZ),
|
| 258 |
+
legend=dict(
|
| 259 |
+
x=0.98, y=0.05, xanchor="right", yanchor="bottom",
|
| 260 |
+
bgcolor="rgba(255,255,255,0.75)", bordercolor="#ccc", borderwidth=1
|
| 261 |
+
),
|
| 262 |
+
legend_title_text=""
|
| 263 |
+
)
|
| 264 |
+
fig.update_xaxes(
|
| 265 |
+
title_text="<b>UCS (psi)</b>", title_font=dict(size=16),
|
| 266 |
+
side="top", range=[xmin, xmax],
|
| 267 |
+
ticks="outside", tickformat=",.0f", tickmode="auto", tick0=tick0,
|
| 268 |
+
showline=True, linewidth=1.2, linecolor="#444", mirror=True,
|
| 269 |
+
showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
|
| 270 |
+
)
|
| 271 |
+
fig.update_yaxes(
|
| 272 |
+
title_text=f"<b>{ylab}</b>", title_font=dict(size=16),
|
| 273 |
+
range=y_range, ticks="outside",
|
| 274 |
+
showline=True, linewidth=1.2, linecolor="#444", mirror=True,
|
| 275 |
+
showgrid=True, gridcolor="rgba(0,0,0,0.12)", automargin=True
|
| 276 |
+
)
|
| 277 |
+
return fig
|
| 278 |
|
| 279 |
+
# ---------- Preview modal (matplotlib) ----------
|
| 280 |
+
def preview_tracks(df: pd.DataFrame, cols: list[str]):
|
| 281 |
+
cols = [c for c in cols if c in df.columns]
|
| 282 |
+
n = len(cols)
|
| 283 |
+
if n == 0:
|
| 284 |
+
fig, ax = plt.subplots(figsize=(4, 2))
|
| 285 |
+
ax.text(0.5,0.5,"No selected columns",ha="center",va="center")
|
| 286 |
+
ax.axis("off")
|
| 287 |
+
return fig
|
| 288 |
+
fig, axes = plt.subplots(1, n, figsize=(2.2*n, 7.0), sharey=True, dpi=100)
|
| 289 |
+
if n == 1: axes = [axes]
|
| 290 |
+
idx = np.arange(1, len(df) + 1)
|
| 291 |
+
for ax, col in zip(axes, cols):
|
| 292 |
+
ax.plot(df[col], idx, '-', lw=1.4, color="#333")
|
| 293 |
+
ax.set_xlabel(col); ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
|
| 294 |
+
ax.grid(True, linestyle=":", alpha=0.3)
|
| 295 |
+
for s in ax.spines.values(): s.set_visible(True)
|
| 296 |
+
axes[0].set_ylabel("Point Index")
|
| 297 |
+
return fig
|
| 298 |
|
| 299 |
@dialog("Preview data")
|
| 300 |
def preview_modal(book: dict[str, pd.DataFrame]):
|
|
|
|
| 365 |
# =========================
|
| 366 |
# Branding in Sidebar
|
| 367 |
# =========================
|
| 368 |
+
st.sidebar.image("logo.png", use_column_width=False)
|
| 369 |
st.sidebar.markdown(
|
| 370 |
"<div style='font-weight:800;font-size:1.2rem;'>ST_GeoMech_UCS</div>"
|
| 371 |
"<div style='color:#667085;'>Real-Time UCS Tracking While Drilling</div>",
|
|
|
|
| 457 |
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 458 |
|
| 459 |
left, spacer, right = st.columns(PLOT_COLS)
|
| 460 |
+
# Robust nudge: only make the inner columns if weight > 0
|
| 461 |
+
if CROSS_NUDGE and CROSS_NUDGE > 0:
|
| 462 |
pad, plotcol = left.columns([CROSS_NUDGE, 1])
|
| 463 |
+
else:
|
| 464 |
+
plotcol = left
|
| 465 |
+
with plotcol:
|
| 466 |
+
st.pyplot(cross_plot_static(df[TARGET], df["UCS_Pred"]), use_container_width=False)
|
| 467 |
with right:
|
| 468 |
st.plotly_chart(
|
| 469 |
track_plot(df, include_actual=True),
|
|
|
|
| 515 |
for c in FEATURES:
|
| 516 |
if pd.api.types.is_numeric_dtype(tbl[c]):
|
| 517 |
tbl[c] = tbl[c].round(2)
|
| 518 |
+
tbl["Violations"] = pd.DataFrame({f:(df[f]<ranges[f][0])|(df[f]>ranges[f][1]) for f in FEATURES}).loc[any_viol].apply(
|
| 519 |
+
lambda r:", ".join([c for c,v in r.items() if v]), axis=1
|
| 520 |
+
)
|
| 521 |
st.session_state.results["m_val"]={
|
| 522 |
"R": pearson_r(df[TARGET], df["UCS_Pred"]),
|
| 523 |
"RMSE": rmse(df[TARGET], df["UCS_Pred"]),
|
|
|
|
| 532 |
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 533 |
|
| 534 |
left, spacer, right = st.columns(PLOT_COLS)
|
| 535 |
+
if CROSS_NUDGE and CROSS_NUDGE > 0:
|
| 536 |
pad, plotcol = left.columns([CROSS_NUDGE, 1])
|
| 537 |
+
else:
|
| 538 |
+
plotcol = left
|
| 539 |
+
with plotcol:
|
| 540 |
+
st.pyplot(
|
| 541 |
+
cross_plot_static(st.session_state.results["Validate"][TARGET],
|
| 542 |
+
st.session_state.results["Validate"]["UCS_Pred"]),
|
| 543 |
+
use_container_width=False
|
| 544 |
+
)
|
| 545 |
with right:
|
| 546 |
st.plotly_chart(
|
| 547 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|