UCS2014 commited on
Commit
e0c0056
·
verified ·
1 Parent(s): 46118dc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +84 -17
app.py CHANGED
@@ -4,6 +4,8 @@ import streamlit as st
4
  import pandas as pd
5
  import numpy as np
6
  import joblib
 
 
7
  import matplotlib.pyplot as plt
8
  from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
9
 
@@ -35,11 +37,11 @@ st.markdown(
35
  <style>
36
  .stApp { background: #FFFFFF; }
37
  section[data-testid="stSidebar"] { background: #F6F9FC; }
38
- .block-container { padding-top: .5rem; padding-bottom: .5rem; } /* less vertical padding */
39
  .stButton>button{ background:#007bff; color:#fff; font-weight:bold; border-radius:8px; border:none; padding:10px 24px; }
40
  .stButton>button:hover{ background:#0056b3; }
41
  .st-hero { display:flex; align-items:center; gap:16px; padding-top: 4px; }
42
- .st-hero .brand { width:110px; height:110px; object-fit:contain; } /* enlarged logo */
43
  .st-hero h1 { margin:0; line-height:1.05; }
44
  .st-hero .tagline { margin:2px 0 0 2px; color:#6b7280; font-size:1.05rem; font-style:italic; }
45
  [data-testid="stBlock"]{ margin-top:0 !important; }
@@ -51,6 +53,19 @@ st.markdown(
51
  # =========================
52
  # Helpers
53
  # =========================
 
 
 
 
 
 
 
 
 
 
 
 
 
54
  def _get_model_url():
55
  """Read optional MODEL_URL from environment only (avoid st.secrets banner)."""
56
  return (os.environ.get("MODEL_URL", "") or "").strip()
@@ -107,34 +122,27 @@ def depth_or_index_track(df, title=None, include_actual=True):
107
  Narrow, tall track: predicted solid blue; actual dotted yellow.
108
  Works for either Depth on Y or Index on Y.
109
  """
110
- # Find depth-like column if available
111
  depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
112
-
113
- # Narrow width, tall height for logging look
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
 
118
  if depth_col is not None:
119
- ax.plot(df["UCS_Pred"], df[depth_col],
120
- '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
121
  if include_actual and TARGET in df.columns:
122
- ax.plot(df[TARGET], df[depth_col],
123
- ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
124
  ax.set_ylabel(depth_col); ax.set_xlabel("UCS")
125
  ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
126
  else:
127
  idx = np.arange(1, len(df) + 1)
128
- ax.plot(df["UCS_Pred"], idx,
129
- '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
130
  if include_actual and TARGET in df.columns:
131
- ax.plot(df[TARGET], idx,
132
- ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
133
  ax.set_ylabel("Point Index"); ax.set_xlabel("UCS")
134
  ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
135
 
136
  ax.grid(True, linestyle=":", alpha=0.4)
137
- if title: ax.set_title(title, pad=8) # keep no title by passing None
138
  ax.legend(loc="best")
139
  return fig
140
 
@@ -173,6 +181,54 @@ def inline_logo(path="logo.png") -> str:
173
  except Exception:
174
  return ""
175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
176
  # =========================
177
  # Model presence (local or optional download)
178
  # =========================
@@ -278,6 +334,12 @@ if st.session_state.app_step == "dev":
278
  st.sidebar.header("Model Development Data")
279
  train_test_file = st.sidebar.file_uploader("Upload Data (Excel)", type=["xlsx","xls"], key="dev_upload")
280
 
 
 
 
 
 
 
281
  run_btn = st.sidebar.button("Run Model", type="primary", use_container_width=True)
282
 
283
  # Proceed button BELOW run, always visible; enables immediately after first successful run
@@ -339,8 +401,7 @@ if st.session_state.app_step == "dev":
339
  df = st.session_state.results["Train"]; m = st.session_state.results["metrics_train"]
340
  c1,c2,c3 = st.columns(3)
341
  c1.metric("R²", f"{m['R2']:.4f}"); c2.metric("RMSE", f"{m['RMSE']:.4f}"); c3.metric("MAE", f"{m['MAE']:.4f}")
342
- # Narrow track column for log-look
343
- left, right = st.columns([0.9, 0.55])
344
  with left:
345
  st.pyplot(cross_plot(df[TARGET], df["UCS_Pred"], "Training: Actual vs Predicted"), use_container_width=True)
346
  with right:
@@ -380,6 +441,13 @@ if st.session_state.app_step == "dev":
380
  if st.session_state.app_step == "predict":
381
  st.sidebar.header("Prediction (Validation)")
382
  validation_file = st.sidebar.file_uploader("Upload Validation Excel", type=["xlsx","xls"], key="val_upload")
 
 
 
 
 
 
 
383
  predict_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
384
  st.sidebar.button("⬅ Back", on_click=lambda: st.session_state.update(app_step="dev"), use_container_width=True)
385
 
@@ -428,7 +496,6 @@ if st.session_state.app_step == "predict":
428
  st.subheader("Validation Results")
429
  sv = st.session_state.results["summary_val"]; oor_table = st.session_state.results.get("oor_table")
430
 
431
- # Show OOR warning above the plots when applicable
432
  if sv["oor_pct"] > 0:
433
  st.warning("Some validation inputs fall outside the **training min–max** ranges. Interpret predictions with caution.")
434
 
 
4
  import pandas as pd
5
  import numpy as np
6
  import joblib
7
+ import matplotlib
8
+ matplotlib.use("Agg") # safe non-GUI backend for cloud
9
  import matplotlib.pyplot as plt
10
  from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
11
 
 
37
  <style>
38
  .stApp { background: #FFFFFF; }
39
  section[data-testid="stSidebar"] { background: #F6F9FC; }
40
+ .block-container { padding-top: .5rem; padding-bottom: .5rem; }
41
  .stButton>button{ background:#007bff; color:#fff; font-weight:bold; border-radius:8px; border:none; padding:10px 24px; }
42
  .stButton>button:hover{ background:#0056b3; }
43
  .st-hero { display:flex; align-items:center; gap:16px; padding-top: 4px; }
44
+ .st-hero .brand { width:110px; height:110px; object-fit:contain; } /* bigger logo */
45
  .st-hero h1 { margin:0; line-height:1.05; }
46
  .st-hero .tagline { margin:2px 0 0 2px; color:#6b7280; font-size:1.05rem; font-style:italic; }
47
  [data-testid="stBlock"]{ margin-top:0 !important; }
 
53
  # =========================
54
  # Helpers
55
  # =========================
56
+
57
+ # Modal dialog fallback for older Streamlit
58
+ try:
59
+ dialog = st.dialog # Streamlit ≥1.31
60
+ except AttributeError:
61
+ def dialog(title):
62
+ def deco(fn):
63
+ def wrapper(*args, **kwargs):
64
+ with st.expander(title, expanded=True):
65
+ return fn(*args, **kwargs)
66
+ return wrapper
67
+ return deco
68
+
69
  def _get_model_url():
70
  """Read optional MODEL_URL from environment only (avoid st.secrets banner)."""
71
  return (os.environ.get("MODEL_URL", "") or "").strip()
 
122
  Narrow, tall track: predicted solid blue; actual dotted yellow.
123
  Works for either Depth on Y or Index on Y.
124
  """
 
125
  depth_col = next((c for c in df.columns if 'depth' in str(c).lower()), None)
 
 
126
  fig_w = 3.1
127
  fig_h = 7.6 if depth_col is not None else 7.2
128
  fig, ax = plt.subplots(figsize=(fig_w, fig_h), dpi=100)
129
 
130
  if depth_col is not None:
131
+ ax.plot(df["UCS_Pred"], df[depth_col], '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
 
132
  if include_actual and TARGET in df.columns:
133
+ ax.plot(df[TARGET], df[depth_col], ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
 
134
  ax.set_ylabel(depth_col); ax.set_xlabel("UCS")
135
  ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
136
  else:
137
  idx = np.arange(1, len(df) + 1)
138
+ ax.plot(df["UCS_Pred"], idx, '-', lw=1.8, color=COLORS["pred"], label="UCS_Pred")
 
139
  if include_actual and TARGET in df.columns:
140
+ ax.plot(df[TARGET], idx, ':', lw=2.0, color=COLORS["actual"], alpha=0.95, label="UCS (actual)")
 
141
  ax.set_ylabel("Point Index"); ax.set_xlabel("UCS")
142
  ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
143
 
144
  ax.grid(True, linestyle=":", alpha=0.4)
145
+ if title: ax.set_title(title, pad=8)
146
  ax.legend(loc="best")
147
  return fig
148
 
 
181
  except Exception:
182
  return ""
183
 
184
+ # ---------- Preview modal helpers ----------
185
+
186
+ def make_index_tracks(df: pd.DataFrame, cols: list[str]):
187
+ """Build a side-by-side index track figure (log-style) for given columns."""
188
+ cols = [c for c in cols if c in df.columns]
189
+ n = len(cols)
190
+ if n == 0:
191
+ fig, ax = plt.subplots(figsize=(4, 2))
192
+ ax.text(0.5, 0.5, "No selected columns in sheet", ha="center", va="center")
193
+ ax.axis("off")
194
+ return fig
195
+ width_per = 2.2 # width per track
196
+ fig_h = 7.0
197
+ fig, axes = plt.subplots(1, n, figsize=(width_per * n, fig_h), sharey=True, dpi=100)
198
+ if n == 1:
199
+ axes = [axes]
200
+ idx = np.arange(1, len(df) + 1)
201
+ for ax, col in zip(axes, cols):
202
+ ax.plot(df[col], idx, '-', lw=1.4, color="#333333")
203
+ ax.set_xlabel(col)
204
+ ax.xaxis.set_label_position('top'); ax.xaxis.tick_top(); ax.invert_yaxis()
205
+ ax.grid(True, linestyle=":", alpha=0.3)
206
+ axes[0].set_ylabel("Point Index")
207
+ return fig
208
+
209
+ def stats_table(df: pd.DataFrame, cols: list[str]) -> pd.DataFrame:
210
+ cols = [c for c in cols if c in df.columns]
211
+ if not cols:
212
+ return pd.DataFrame({"Feature": [], "Min": [], "Max": [], "Mean": [], "Std": []})
213
+ out = df[cols].agg(['min', 'max', 'mean', 'std']).T
214
+ out = out.rename(columns={"min": "Min", "max": "Max", "mean": "Mean", "std": "Std"})
215
+ out = out.reset_index().rename(columns={"index": "Feature"})
216
+ return out
217
+
218
+ @dialog("Preview data")
219
+ def preview_modal(book: dict[str, pd.DataFrame], default_cols: list[str]):
220
+ if not book:
221
+ st.info("No data loaded yet.")
222
+ return
223
+ sheet = st.selectbox("Sheet", list(book.keys()), index=0)
224
+ df = book[sheet]
225
+ present = [c for c in default_cols if c in df.columns]
226
+ tab_tracks, tab_summary = st.tabs(["Tracks", "Summary"])
227
+ with tab_tracks:
228
+ st.pyplot(make_index_tracks(df, present), use_container_width=True)
229
+ with tab_summary:
230
+ st.dataframe(stats_table(df, present), use_container_width=True)
231
+
232
  # =========================
233
  # Model presence (local or optional download)
234
  # =========================
 
334
  st.sidebar.header("Model Development Data")
335
  train_test_file = st.sidebar.file_uploader("Upload Data (Excel)", type=["xlsx","xls"], key="dev_upload")
336
 
337
+ # Preview button (uses modal)
338
+ preview_btn = st.sidebar.button("Preview data", use_container_width=True, disabled=(train_test_file is None))
339
+ if preview_btn and train_test_file is not None:
340
+ _book = read_book(train_test_file)
341
+ preview_modal(_book, FEATURES)
342
+
343
  run_btn = st.sidebar.button("Run Model", type="primary", use_container_width=True)
344
 
345
  # Proceed button BELOW run, always visible; enables immediately after first successful run
 
401
  df = st.session_state.results["Train"]; m = st.session_state.results["metrics_train"]
402
  c1,c2,c3 = st.columns(3)
403
  c1.metric("R²", f"{m['R2']:.4f}"); c2.metric("RMSE", f"{m['RMSE']:.4f}"); c3.metric("MAE", f"{m['MAE']:.4f}")
404
+ left, right = st.columns([0.9, 0.55]) # slim track column
 
405
  with left:
406
  st.pyplot(cross_plot(df[TARGET], df["UCS_Pred"], "Training: Actual vs Predicted"), use_container_width=True)
407
  with right:
 
441
  if st.session_state.app_step == "predict":
442
  st.sidebar.header("Prediction (Validation)")
443
  validation_file = st.sidebar.file_uploader("Upload Validation Excel", type=["xlsx","xls"], key="val_upload")
444
+
445
+ # Preview button for validation data
446
+ preview_val_btn = st.sidebar.button("Preview data", use_container_width=True, disabled=(validation_file is None))
447
+ if preview_val_btn and validation_file is not None:
448
+ _book = read_book(validation_file)
449
+ preview_modal(_book, FEATURES)
450
+
451
  predict_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
452
  st.sidebar.button("⬅ Back", on_click=lambda: st.session_state.update(app_step="dev"), use_container_width=True)
453
 
 
496
  st.subheader("Validation Results")
497
  sv = st.session_state.results["summary_val"]; oor_table = st.session_state.results.get("oor_table")
498
 
 
499
  if sv["oor_pct"] > 0:
500
  st.warning("Some validation inputs fall outside the **training min–max** ranges. Interpret predictions with caution.")
501