UCS2014 commited on
Commit
1d41b0a
·
verified ·
1 Parent(s): 000b7bd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +57 -35
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import io, json, os, base64
2
  from pathlib import Path
3
  import streamlit as st
@@ -5,14 +6,12 @@ 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
  # =========================
@@ -98,9 +97,9 @@ def find_sheet(book, names):
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()]))
@@ -115,14 +114,14 @@ def cross_plot_interactive(actual, pred, size=(3.9, 3.9)):
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(
@@ -130,17 +129,18 @@ def cross_plot_interactive(actual, pred, size=(3.9, 3.9)):
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
@@ -150,7 +150,7 @@ def cross_plot_interactive(actual, pred, size=(3.9, 3.9)):
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
@@ -187,21 +187,22 @@ def depth_or_index_track_interactive(df, title=None, include_actual=True):
187
  width=int(3.1 * 100),
188
  height=int((7.6 if depth_col is not None else 7.2) * 100),
189
  )
 
190
  fig.update_xaxes(
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,12 +305,19 @@ if meta_path.exists():
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
  # =========================
@@ -397,13 +405,13 @@ if st.session_state.app_step == "dev":
397
  file_bytes = train_test_file.getvalue()
398
  size = len(file_bytes)
399
  except Exception:
400
- file_bytes = b""
401
- size = 0
402
  sig = (train_test_file.name, size)
403
  if sig != st.session_state.dev_file_signature and size > 0:
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,7 +456,7 @@ if st.session_state.app_step == "dev":
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
@@ -538,15 +546,17 @@ if st.session_state.app_step == "dev":
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
 
@@ -618,9 +628,19 @@ if st.session_state.app_step == "predict":
618
  if sv["oor_pct"] > 0:
619
  st.warning("Some validation inputs fall outside the **training min–max** ranges. Interpret predictions with caution.")
620
 
621
- c1,c2,c3,c4 = st.columns(4)
622
- c1.metric("points", f"{sv['n_points']}"); c2.metric("Pred min", f"{sv['pred_min']:.2f}")
623
- c3.metric("Pred max", f"{sv['pred_max']:.2f}"); c4.metric("OOR %", f"{sv['oor_pct']:.1f}%")
 
 
 
 
 
 
 
 
 
 
624
 
625
  left, right = st.columns([0.9, 0.55])
626
  with left:
@@ -658,15 +678,17 @@ if st.session_state.app_step == "predict":
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
 
 
1
+ # app.py
2
  import io, json, os, base64
3
  from pathlib import Path
4
  import streamlit as st
 
6
  import numpy as np
7
  import joblib
8
 
9
+ # keep matplotlib ONLY for the preview modal (static thumbnails)
10
  import matplotlib
11
  matplotlib.use("Agg")
12
  import matplotlib.pyplot as plt
13
 
 
14
  import plotly.graph_objects as go
 
15
  from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
16
 
17
  # =========================
 
97
  if nm.lower() in low2orig: return low2orig[nm.lower()]
98
  return None
99
 
100
+ # ---------- Interactive plotting (full outline, bold axis titles) ----------
101
  def cross_plot_interactive(actual, pred, size=(3.9, 3.9)):
102
+ """Interactive cross-plot: blue points, dashed 1:1, equal axes, no title, numeric ticks, full box outline."""
103
  a = pd.Series(actual).astype(float)
104
  p = pd.Series(pred).astype(float)
105
  lo = float(np.nanmin([a.min(), p.min()]))
 
114
  x=a, y=p, mode="markers",
115
  marker=dict(size=6, color=COLORS["pred"]),
116
  hovertemplate="Actual: %{x:.2f}<br>Pred: %{y:.2f}<extra></extra>",
117
+ showlegend=False
118
  ))
119
 
120
  # 1:1 line (dashed grey)
121
  fig.add_trace(go.Scatter(
122
  x=[x0, x1], y=[x0, x1], mode="lines",
123
  line=dict(color=COLORS["ref"], width=1.2, dash="dash"),
124
+ hoverinfo="skip", showlegend=False
125
  ))
126
 
127
  fig.update_layout(
 
129
  margin=dict(l=50, r=10, t=10, b=36),
130
  hovermode="closest", font=dict(size=13)
131
  )
132
+ # FULL OUTLINE via mirror=True (top/right) + showline
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", mirror=True,
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", mirror=True,
144
  showgrid=True, gridcolor="rgba(0,0,0,0.12)",
145
  tickformat=",.0f", scaleanchor="x", scaleratio=1,
146
  automargin=True
 
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; full box outline."""
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
 
187
  width=int(3.1 * 100),
188
  height=int((7.6 if depth_col is not None else 7.2) * 100),
189
  )
190
+ # FULL OUTLINE via mirror=True (adds bottom/right sides)
191
  fig.update_xaxes(
192
  title_text="<b>UCS</b>", side="top",
193
+ ticks="outside", showline=True, linewidth=1.2, linecolor="#444", mirror=True,
194
  showgrid=True, gridcolor="rgba(0,0,0,0.12)",
195
  tickformat=",.0f", automargin=True
196
  )
197
  fig.update_yaxes(
198
  title_text=f"<b>{y_label}</b>", autorange="reversed",
199
+ ticks="outside", showline=True, linewidth=1.2, linecolor="#444", mirror=True,
200
  showgrid=True, gridcolor="rgba(0,0,0,0.12)",
201
  automargin=True
202
  )
203
  return fig
204
 
205
+ # ---------- Preview modal helpers (matplotlib static) ----------
206
  def make_index_tracks(df: pd.DataFrame, cols: list[str]):
207
  cols = [c for c in cols if c in df.columns]
208
  n = len(cols)
 
305
  FEATURES = meta.get("features", FEATURES); TARGET = meta.get("target", TARGET)
306
  except Exception: pass
307
  else:
308
+ def infer_features_from_model(m):
309
+ try:
310
+ if hasattr(m, "feature_names_in_") and len(getattr(m, "feature_names_in_")):
311
+ return [str(x) for x in m.feature_names_in_]
312
+ except Exception: pass
313
+ try:
314
+ if hasattr(m, "steps") and len(m.steps):
315
+ last = m.steps[-1][1]
316
+ if hasattr(last, "feature_names_in_") and len(last.feature_names_in_):
317
+ return [str(x) for x in last.feature_names_in_]
318
+ except Exception: pass
319
+ return None
320
+ infer = infer_features_from_model(model)
321
  if infer: FEATURES = infer
322
 
323
  # =========================
 
405
  file_bytes = train_test_file.getvalue()
406
  size = len(file_bytes)
407
  except Exception:
408
+ file_bytes = b""; size = 0
 
409
  sig = (train_test_file.name, size)
410
  if sig != st.session_state.dev_file_signature and size > 0:
411
  st.session_state.dev_file_signature = sig
412
  st.session_state.dev_file_name = train_test_file.name
413
  st.session_state.dev_file_bytes = file_bytes
414
+ # Inspect first sheet for rows/cols
415
  _book_tmp = read_book_bytes(file_bytes)
416
  if _book_tmp:
417
  first_df = next(iter(_book_tmp.values()))
 
456
  else:
457
  st.write("**Upload your data to build a case, then run the model to review development performance.**")
458
 
459
+ # If user clicked preview, open modal *after* helper so helper stays on top
460
  if st.session_state.dev_preview_request and st.session_state.dev_file_bytes:
461
  _book = read_book_bytes(st.session_state.dev_file_bytes)
462
  st.session_state.dev_previewed = True
 
546
  summary_df = pd.DataFrame(rows) if rows else None
547
  try:
548
  buf = io.BytesIO()
 
549
  with pd.ExcelWriter(buf, engine="openpyxl") as xw:
550
  for name, frame in sheets.items():
551
  frame.to_excel(xw, sheet_name=name[:31], index=False)
552
  if summary_df is not None:
553
  summary_df.to_excel(xw, sheet_name="Summary", index=False)
554
+ st.download_button(
555
+ "Export Development Results to Excel",
556
+ data=buf.getvalue(),
557
+ file_name="UCS_Dev_Results.xlsx",
558
+ mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
559
+ )
560
  except Exception as e:
561
  st.warning(str(e))
562
 
 
628
  if sv["oor_pct"] > 0:
629
  st.warning("Some validation inputs fall outside the **training min–max** ranges. Interpret predictions with caution.")
630
 
631
+ # Metrics like Development (R² / RMSE / MAE)
632
+ metrics_val = st.session_state.results.get("metrics_val")
633
+ if metrics_val is not None:
634
+ c1, c2, c3 = st.columns(3)
635
+ c1.metric("R²", f"{metrics_val['R2']:.4f}")
636
+ c2.metric("RMSE", f"{metrics_val['RMSE']:.4f}")
637
+ c3.metric("MAE", f"{metrics_val['MAE']:.4f}")
638
+ else:
639
+ # Fallback only if actual UCS isn't provided
640
+ c1, c2, c3 = st.columns(3)
641
+ c1.metric("# points", f"{sv['n_points']}")
642
+ c2.metric("Pred min", f"{sv['pred_min']:.2f}")
643
+ c3.metric("Pred max", f"{sv['pred_max']:.2f}")
644
 
645
  left, right = st.columns([0.9, 0.55])
646
  with left:
 
678
  summary_df = pd.DataFrame(rows) if rows else None
679
  try:
680
  buf = io.BytesIO()
 
681
  with pd.ExcelWriter(buf, engine="openpyxl") as xw:
682
+ for name, frame in sheets.items():
683
+ frame.to_excel(xw, sheet_name=name[:31], index=False)
684
  if summary_df is not None:
685
  summary_df.to_excel(xw, sheet_name="Summary", index=False)
686
+ st.download_button(
687
+ "Export Validation Results to Excel",
688
+ data=buf.getvalue(),
689
+ file_name="UCS_Validation_Results.xlsx",
690
+ mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
691
+ )
692
  except Exception as e:
693
  st.warning(str(e))
694