Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -36,25 +36,15 @@ BOLD_FONT = "Arial Black, Arial, sans-serif" # used for bold axis titles & tick
|
|
| 36 |
# =========================
|
| 37 |
st.set_page_config(page_title="ST_GeoMech_UCS", page_icon="logo.png", layout="wide")
|
| 38 |
|
| 39 |
-
# This is the new CSS block
|
| 40 |
st.markdown("""
|
| 41 |
<style>
|
| 42 |
-
/* Add a light gray background to the main app container */
|
| 43 |
-
.stApp {
|
| 44 |
-
background-color: #f0f2f6;
|
| 45 |
-
}
|
| 46 |
-
|
| 47 |
-
/* Change the color of the main page title (h1) to light blue */
|
| 48 |
-
h1 {
|
| 49 |
-
color: #add8e6;
|
| 50 |
-
}
|
| 51 |
-
|
| 52 |
/* CSS to make the header sticky */
|
| 53 |
.fixed-header-container {
|
| 54 |
position: sticky;
|
| 55 |
top: 0;
|
| 56 |
z-index: 1000;
|
| 57 |
-
background-color: #f0f2f6; /*
|
| 58 |
padding: 10px 0;
|
| 59 |
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
| 60 |
}
|
|
@@ -443,6 +433,11 @@ st.sidebar.markdown(f"""
|
|
| 443 |
""", unsafe_allow_html=True
|
| 444 |
)
|
| 445 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
# =========================
|
| 447 |
# The main sticky container is defined here, before the app logic
|
| 448 |
# This ensures it's rendered at the top of the page regardless of state.
|
|
@@ -476,9 +471,7 @@ with st.container():
|
|
| 476 |
# =========================
|
| 477 |
if st.session_state.app_step == "intro":
|
| 478 |
st.header("Welcome!")
|
| 479 |
-
st.markdown(
|
| 480 |
-
"This software is developed by *Smart Thinking AI-Solutions Team* to estimate UCS from drilling data."
|
| 481 |
-
)
|
| 482 |
st.subheader("How It Works")
|
| 483 |
st.markdown(
|
| 484 |
"1) **Upload your data to build the case and preview the performance of our model.** \n"
|
|
@@ -486,8 +479,7 @@ if st.session_state.app_step == "intro":
|
|
| 486 |
"3) **Proceed to Validation** (with actual UCS) or **Proceed to Prediction** (no UCS)."
|
| 487 |
)
|
| 488 |
if st.button("Start Showcase", type="primary"):
|
| 489 |
-
st.session_state.app_step = "dev"
|
| 490 |
-
st.rerun()
|
| 491 |
|
| 492 |
# =========================
|
| 493 |
# CASE BUILDING
|
|
@@ -504,70 +496,47 @@ if st.session_state.app_step == "dev":
|
|
| 504 |
tmp = read_book_bytes(st.session_state.dev_file_bytes)
|
| 505 |
if tmp:
|
| 506 |
df0 = next(iter(tmp.values()))
|
| 507 |
-
st.sidebar.caption(
|
| 508 |
-
f"**Data loaded:** {st.session_state.dev_file_name} • {df0.shape[0]} rows × {df0.shape[1]} cols"
|
| 509 |
-
)
|
| 510 |
|
| 511 |
-
if st.sidebar.button(
|
| 512 |
-
"Preview data",
|
| 513 |
-
use_container_width=True,
|
| 514 |
-
disabled=not st.session_state.dev_file_loaded
|
| 515 |
-
):
|
| 516 |
preview_modal(read_book_bytes(st.session_state.dev_file_bytes))
|
| 517 |
st.session_state.dev_preview = True
|
| 518 |
|
| 519 |
run = st.sidebar.button("Run Model", type="primary", use_container_width=True)
|
| 520 |
-
if st.sidebar.button("Proceed to Validation ▶", use_container_width=True):
|
| 521 |
-
|
| 522 |
-
st.rerun()
|
| 523 |
-
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True):
|
| 524 |
-
st.session_state.app_step = "predict"
|
| 525 |
-
st.rerun()
|
| 526 |
|
| 527 |
if run and st.session_state.dev_file_bytes:
|
| 528 |
book = read_book_bytes(st.session_state.dev_file_bytes)
|
| 529 |
-
sh_train = find_sheet(
|
| 530 |
-
|
| 531 |
-
)
|
| 532 |
-
sh_test = find_sheet(
|
| 533 |
-
book, ["Test", "Testing", "testing2", "test", "testing"]
|
| 534 |
-
)
|
| 535 |
if sh_train is None or sh_test is None:
|
| 536 |
-
st.error(
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
st.stop()
|
| 540 |
-
tr = book[sh_train].copy()
|
| 541 |
-
te = book[sh_test].copy()
|
| 542 |
-
if not (ensure_cols(tr, FEATURES + [TARGET]) and ensure_cols(te, FEATURES + [TARGET])):
|
| 543 |
-
st.error("Missing required columns.")
|
| 544 |
-
st.stop()
|
| 545 |
tr["UCS_Pred"] = model.predict(tr[FEATURES])
|
| 546 |
te["UCS_Pred"] = model.predict(te[FEATURES])
|
| 547 |
|
| 548 |
-
st.session_state.results["Train"] =
|
| 549 |
-
st.session_state.results["
|
| 550 |
-
st.session_state.results["m_train"] = {
|
| 551 |
"R": pearson_r(tr[TARGET], tr["UCS_Pred"]),
|
| 552 |
"RMSE": rmse(tr[TARGET], tr["UCS_Pred"]),
|
| 553 |
"MAE": mean_absolute_error(tr[TARGET], tr["UCS_Pred"])
|
| 554 |
}
|
| 555 |
-
st.session_state.results["m_test"]
|
| 556 |
"R": pearson_r(te[TARGET], te["UCS_Pred"]),
|
| 557 |
"RMSE": rmse(te[TARGET], te["UCS_Pred"]),
|
| 558 |
"MAE": mean_absolute_error(te[TARGET], te["UCS_Pred"])
|
| 559 |
}
|
| 560 |
|
| 561 |
-
tr_min = tr[FEATURES].min().to_dict()
|
| 562 |
-
|
| 563 |
-
st.session_state.train_ranges = {f: (float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
| 564 |
st.success("Case has been built and results are displayed below.")
|
| 565 |
|
| 566 |
def _dev_block(df, m):
|
| 567 |
-
c1,
|
| 568 |
-
c1.metric("R", f"{m['R']:.2f}")
|
| 569 |
-
c2.metric("RMSE", f"{m['RMSE']:.2f}")
|
| 570 |
-
c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 571 |
|
| 572 |
# 2-column layout, big gap (prevents overlap)
|
| 573 |
col_cross, col_track = st.columns([3, 2], gap="large")
|
|
@@ -583,18 +552,16 @@ if st.session_state.app_step == "dev":
|
|
| 583 |
if "Train" in st.session_state.results or "Test" in st.session_state.results:
|
| 584 |
tab1, tab2 = st.tabs(["Training", "Testing"])
|
| 585 |
if "Train" in st.session_state.results:
|
| 586 |
-
with tab1:
|
| 587 |
-
_dev_block(st.session_state.results["Train"], st.session_state.results["m_train"])
|
| 588 |
if "Test" in st.session_state.results:
|
| 589 |
-
with tab2:
|
| 590 |
-
_dev_block(st.session_state.results["Test"], st.session_state.results["m_test"])
|
| 591 |
|
| 592 |
# =========================
|
| 593 |
# VALIDATION (with actual UCS)
|
| 594 |
# =========================
|
| 595 |
if st.session_state.app_step == "validate":
|
| 596 |
st.sidebar.header("Validate the Model")
|
| 597 |
-
up = st.sidebar.file_uploader("Upload Validation Excel", type=["xlsx",
|
| 598 |
if up is not None:
|
| 599 |
book = read_book_bytes(up.getvalue())
|
| 600 |
if book:
|
|
@@ -603,78 +570,54 @@ if st.session_state.app_step == "validate":
|
|
| 603 |
if st.sidebar.button("Preview data", use_container_width=True, disabled=(up is None)):
|
| 604 |
preview_modal(read_book_bytes(up.getvalue()))
|
| 605 |
go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
|
| 606 |
-
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True):
|
| 607 |
-
|
| 608 |
-
st.rerun()
|
| 609 |
-
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True):
|
| 610 |
-
st.session_state.app_step = "predict"
|
| 611 |
-
st.rerun()
|
| 612 |
|
| 613 |
if go_btn and up is not None:
|
| 614 |
book = read_book_bytes(up.getvalue())
|
| 615 |
-
name = find_sheet(book, ["Validation",
|
| 616 |
df = book[name].copy()
|
| 617 |
-
if not ensure_cols(df, FEATURES
|
| 618 |
-
st.error("Missing required columns.")
|
| 619 |
-
st.stop()
|
| 620 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 621 |
-
st.session_state.results["Validate"]
|
| 622 |
|
| 623 |
-
ranges = st.session_state.train_ranges
|
| 624 |
-
oor_pct = 0.0
|
| 625 |
-
tbl = None
|
| 626 |
if ranges:
|
| 627 |
-
any_viol = pd.DataFrame(
|
| 628 |
-
|
| 629 |
-
).any(axis=1)
|
| 630 |
-
oor_pct = float(any_viol.mean() * 100.0)
|
| 631 |
if any_viol.any():
|
| 632 |
tbl = df.loc[any_viol, FEATURES].copy()
|
| 633 |
for c in FEATURES:
|
| 634 |
-
if pd.api.types.is_numeric_dtype(tbl[c]):
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
{f: (df[f] < ranges[f][0]) | (df[f] > ranges[f][1]) for f in FEATURES}
|
| 638 |
-
).loc[any_viol].apply(lambda r: ", ".join([c for c, v in r.items() if v]), axis=1)
|
| 639 |
-
st.session_state.results["m_val"] = {
|
| 640 |
"R": pearson_r(df[TARGET], df["UCS_Pred"]),
|
| 641 |
"RMSE": rmse(df[TARGET], df["UCS_Pred"]),
|
| 642 |
"MAE": mean_absolute_error(df[TARGET], df["UCS_Pred"])
|
| 643 |
}
|
| 644 |
-
st.session_state.results["sv_val"]
|
| 645 |
-
|
| 646 |
-
"pred_min": float(df["UCS_Pred"].min()),
|
| 647 |
-
"pred_max": float(df["UCS_Pred"].max()),
|
| 648 |
-
"oor": oor_pct
|
| 649 |
-
}
|
| 650 |
-
st.session_state.results["oor_tbl"] = tbl
|
| 651 |
|
| 652 |
if "Validate" in st.session_state.results:
|
| 653 |
m = st.session_state.results["m_val"]
|
| 654 |
-
c1,
|
| 655 |
-
c1.metric("R", f"{m['R']:.2f}")
|
| 656 |
-
c2.metric("RMSE", f"{m['RMSE']:.2f}")
|
| 657 |
-
c3.metric("MAE", f"{m['MAE']:.2f}")
|
| 658 |
|
| 659 |
col_cross, col_track = st.columns([3, 2], gap="large")
|
| 660 |
with col_cross:
|
| 661 |
st.pyplot(
|
| 662 |
-
cross_plot_static(
|
| 663 |
-
|
| 664 |
-
st.session_state.results["Validate"]["UCS_Pred"]
|
| 665 |
-
),
|
| 666 |
use_container_width=True
|
| 667 |
)
|
| 668 |
with col_track:
|
| 669 |
st.plotly_chart(
|
| 670 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|
| 671 |
-
use_container_width=True,
|
| 672 |
-
config={"displayModeBar": False, "scrollZoom": True}
|
| 673 |
)
|
| 674 |
|
| 675 |
sv = st.session_state.results["sv_val"]
|
| 676 |
-
if sv["oor"] > 0:
|
| 677 |
-
st.warning("Some inputs fall outside **training min–max** ranges.")
|
| 678 |
if st.session_state.results["oor_tbl"] is not None:
|
| 679 |
st.write("*Out-of-range rows (vs. Training min–max):*")
|
| 680 |
df_centered_rounded(st.session_state.results["oor_tbl"])
|
|
@@ -684,7 +627,7 @@ if st.session_state.app_step == "validate":
|
|
| 684 |
# =========================
|
| 685 |
if st.session_state.app_step == "predict":
|
| 686 |
st.sidebar.header("Prediction (No Actual UCS)")
|
| 687 |
-
up = st.sidebar.file_uploader("Upload Prediction Excel", type=["xlsx",
|
| 688 |
if up is not None:
|
| 689 |
book = read_book_bytes(up.getvalue())
|
| 690 |
if book:
|
|
@@ -693,52 +636,41 @@ if st.session_state.app_step == "predict":
|
|
| 693 |
if st.sidebar.button("Preview data", use_container_width=True, disabled=(up is None)):
|
| 694 |
preview_modal(read_book_bytes(up.getvalue()))
|
| 695 |
go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
|
| 696 |
-
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True):
|
| 697 |
-
st.session_state.app_step = "dev"
|
| 698 |
-
st.rerun()
|
| 699 |
|
| 700 |
if go_btn and up is not None:
|
| 701 |
-
book = read_book_bytes(up.getvalue())
|
| 702 |
-
name = list(book.keys())[0]
|
| 703 |
df = book[name].copy()
|
| 704 |
-
if not ensure_cols(df, FEATURES):
|
| 705 |
-
st.error("Missing required columns.")
|
| 706 |
-
st.stop()
|
| 707 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 708 |
-
st.session_state.results["PredictOnly"]
|
| 709 |
|
| 710 |
-
ranges = st.session_state.train_ranges
|
| 711 |
-
oor_pct = 0.0
|
| 712 |
if ranges:
|
| 713 |
-
any_viol = pd.DataFrame(
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
"
|
| 719 |
-
"
|
| 720 |
-
"
|
| 721 |
-
"
|
| 722 |
-
"pred_std": float(df["UCS_Pred"].std(ddof=0)),
|
| 723 |
-
"oor": oor_pct
|
| 724 |
}
|
| 725 |
|
| 726 |
if "PredictOnly" in st.session_state.results:
|
| 727 |
-
df = st.session_state.results["PredictOnly"]
|
| 728 |
-
sv = st.session_state.results["sv_pred"]
|
| 729 |
|
| 730 |
-
col_left, col_right = st.columns([2,
|
| 731 |
with col_left:
|
| 732 |
table = pd.DataFrame({
|
| 733 |
-
"Metric": ["# points",
|
| 734 |
-
"Value":
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
-
|
| 739 |
-
|
| 740 |
-
f'{sv["oor"]:.1f}%'
|
| 741 |
-
]
|
| 742 |
})
|
| 743 |
st.success("Predictions ready ✓")
|
| 744 |
df_centered_rounded(table, hide_index=True)
|
|
@@ -746,8 +678,7 @@ if st.session_state.app_step == "predict":
|
|
| 746 |
with col_right:
|
| 747 |
st.plotly_chart(
|
| 748 |
track_plot(df, include_actual=False),
|
| 749 |
-
use_container_width=True,
|
| 750 |
-
config={"displayModeBar": False, "scrollZoom": True}
|
| 751 |
)
|
| 752 |
|
| 753 |
# =========================
|
|
|
|
| 36 |
# =========================
|
| 37 |
st.set_page_config(page_title="ST_GeoMech_UCS", page_icon="logo.png", layout="wide")
|
| 38 |
|
| 39 |
+
# This is the new CSS block for the sticky header and message box
|
| 40 |
st.markdown("""
|
| 41 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
/* CSS to make the header sticky */
|
| 43 |
.fixed-header-container {
|
| 44 |
position: sticky;
|
| 45 |
top: 0;
|
| 46 |
z-index: 1000;
|
| 47 |
+
background-color: #f0f2f6; /* Adjust to match your app's background color */
|
| 48 |
padding: 10px 0;
|
| 49 |
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
| 50 |
}
|
|
|
|
| 433 |
""", unsafe_allow_html=True
|
| 434 |
)
|
| 435 |
|
| 436 |
+
# =========================
|
| 437 |
+
# Reusable Sticky Header Function
|
| 438 |
+
# This function is now removed, replaced by the fixed-header-container
|
| 439 |
+
# =========================
|
| 440 |
+
|
| 441 |
# =========================
|
| 442 |
# The main sticky container is defined here, before the app logic
|
| 443 |
# This ensures it's rendered at the top of the page regardless of state.
|
|
|
|
| 471 |
# =========================
|
| 472 |
if st.session_state.app_step == "intro":
|
| 473 |
st.header("Welcome!")
|
| 474 |
+
st.markdown("This software is developed by *Smart Thinking AI-Solutions Team* to estimate UCS from drilling data.")
|
|
|
|
|
|
|
| 475 |
st.subheader("How It Works")
|
| 476 |
st.markdown(
|
| 477 |
"1) **Upload your data to build the case and preview the performance of our model.** \n"
|
|
|
|
| 479 |
"3) **Proceed to Validation** (with actual UCS) or **Proceed to Prediction** (no UCS)."
|
| 480 |
)
|
| 481 |
if st.button("Start Showcase", type="primary"):
|
| 482 |
+
st.session_state.app_step = "dev"; st.rerun()
|
|
|
|
| 483 |
|
| 484 |
# =========================
|
| 485 |
# CASE BUILDING
|
|
|
|
| 496 |
tmp = read_book_bytes(st.session_state.dev_file_bytes)
|
| 497 |
if tmp:
|
| 498 |
df0 = next(iter(tmp.values()))
|
| 499 |
+
st.sidebar.caption(f"**Data loaded:** {st.session_state.dev_file_name} • {df0.shape[0]} rows × {df0.shape[1]} cols")
|
|
|
|
|
|
|
| 500 |
|
| 501 |
+
if st.sidebar.button("Preview data", use_container_width=True, disabled=not st.session_state.dev_file_loaded):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 502 |
preview_modal(read_book_bytes(st.session_state.dev_file_bytes))
|
| 503 |
st.session_state.dev_preview = True
|
| 504 |
|
| 505 |
run = st.sidebar.button("Run Model", type="primary", use_container_width=True)
|
| 506 |
+
if st.sidebar.button("Proceed to Validation ▶", use_container_width=True): st.session_state.app_step="validate"; st.rerun()
|
| 507 |
+
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 508 |
|
| 509 |
if run and st.session_state.dev_file_bytes:
|
| 510 |
book = read_book_bytes(st.session_state.dev_file_bytes)
|
| 511 |
+
sh_train = find_sheet(book, ["Train","Training","training2","train","training"])
|
| 512 |
+
sh_test = find_sheet(book, ["Test","Testing","testing2","test","testing"])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 513 |
if sh_train is None or sh_test is None:
|
| 514 |
+
st.error("Workbook must include Train/Training/training2 and Test/Testing/testing2 sheets."); st.stop()
|
| 515 |
+
tr = book[sh_train].copy(); te = book[sh_test].copy()
|
| 516 |
+
if not (ensure_cols(tr, FEATURES+[TARGET]) and ensure_cols(te, FEATURES+[TARGET])):
|
| 517 |
+
st.error("Missing required columns."); st.stop()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
tr["UCS_Pred"] = model.predict(tr[FEATURES])
|
| 519 |
te["UCS_Pred"] = model.predict(te[FEATURES])
|
| 520 |
|
| 521 |
+
st.session_state.results["Train"]=tr; st.session_state.results["Test"]=te
|
| 522 |
+
st.session_state.results["m_train"]={
|
|
|
|
| 523 |
"R": pearson_r(tr[TARGET], tr["UCS_Pred"]),
|
| 524 |
"RMSE": rmse(tr[TARGET], tr["UCS_Pred"]),
|
| 525 |
"MAE": mean_absolute_error(tr[TARGET], tr["UCS_Pred"])
|
| 526 |
}
|
| 527 |
+
st.session_state.results["m_test"]={
|
| 528 |
"R": pearson_r(te[TARGET], te["UCS_Pred"]),
|
| 529 |
"RMSE": rmse(te[TARGET], te["UCS_Pred"]),
|
| 530 |
"MAE": mean_absolute_error(te[TARGET], te["UCS_Pred"])
|
| 531 |
}
|
| 532 |
|
| 533 |
+
tr_min = tr[FEATURES].min().to_dict(); tr_max = tr[FEATURES].max().to_dict()
|
| 534 |
+
st.session_state.train_ranges = {f:(float(tr_min[f]), float(tr_max[f])) for f in FEATURES}
|
|
|
|
| 535 |
st.success("Case has been built and results are displayed below.")
|
| 536 |
|
| 537 |
def _dev_block(df, m):
|
| 538 |
+
c1,c2,c3 = st.columns(3)
|
| 539 |
+
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
|
|
|
|
|
|
| 540 |
|
| 541 |
# 2-column layout, big gap (prevents overlap)
|
| 542 |
col_cross, col_track = st.columns([3, 2], gap="large")
|
|
|
|
| 552 |
if "Train" in st.session_state.results or "Test" in st.session_state.results:
|
| 553 |
tab1, tab2 = st.tabs(["Training", "Testing"])
|
| 554 |
if "Train" in st.session_state.results:
|
| 555 |
+
with tab1: _dev_block(st.session_state.results["Train"], st.session_state.results["m_train"])
|
|
|
|
| 556 |
if "Test" in st.session_state.results:
|
| 557 |
+
with tab2: _dev_block(st.session_state.results["Test"], st.session_state.results["m_test"])
|
|
|
|
| 558 |
|
| 559 |
# =========================
|
| 560 |
# VALIDATION (with actual UCS)
|
| 561 |
# =========================
|
| 562 |
if st.session_state.app_step == "validate":
|
| 563 |
st.sidebar.header("Validate the Model")
|
| 564 |
+
up = st.sidebar.file_uploader("Upload Validation Excel", type=["xlsx","xls"])
|
| 565 |
if up is not None:
|
| 566 |
book = read_book_bytes(up.getvalue())
|
| 567 |
if book:
|
|
|
|
| 570 |
if st.sidebar.button("Preview data", use_container_width=True, disabled=(up is None)):
|
| 571 |
preview_modal(read_book_bytes(up.getvalue()))
|
| 572 |
go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
|
| 573 |
+
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
|
| 574 |
+
if st.sidebar.button("Proceed to Prediction ▶", use_container_width=True): st.session_state.app_step="predict"; st.rerun()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 575 |
|
| 576 |
if go_btn and up is not None:
|
| 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.error("Missing required columns."); st.stop()
|
|
|
|
|
|
|
| 581 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 582 |
+
st.session_state.results["Validate"]=df
|
| 583 |
|
| 584 |
+
ranges = st.session_state.train_ranges; oor_pct = 0.0; tbl=None
|
|
|
|
|
|
|
| 585 |
if ranges:
|
| 586 |
+
any_viol = pd.DataFrame({f:(df[f]<ranges[f][0])|(df[f]>ranges[f][1]) for f in FEATURES}).any(axis=1)
|
| 587 |
+
oor_pct = float(any_viol.mean()*100.0)
|
|
|
|
|
|
|
| 588 |
if any_viol.any():
|
| 589 |
tbl = df.loc[any_viol, FEATURES].copy()
|
| 590 |
for c in FEATURES:
|
| 591 |
+
if pd.api.types.is_numeric_dtype(tbl[c]): tbl[c] = tbl[c].round(2)
|
| 592 |
+
tbl["Violations"] = pd.DataFrame({f:(df[f]<ranges[f][0])|(df[f]>ranges[f][1]) for f in FEATURES}).loc[any_viol].apply(lambda r:", ".join([c for c,v in r.items() if v]), axis=1)
|
| 593 |
+
st.session_state.results["m_val"]={
|
|
|
|
|
|
|
|
|
|
| 594 |
"R": pearson_r(df[TARGET], df["UCS_Pred"]),
|
| 595 |
"RMSE": rmse(df[TARGET], df["UCS_Pred"]),
|
| 596 |
"MAE": mean_absolute_error(df[TARGET], df["UCS_Pred"])
|
| 597 |
}
|
| 598 |
+
st.session_state.results["sv_val"]={"n":len(df),"pred_min":float(df["UCS_Pred"].min()),"pred_max":float(df["UCS_Pred"].max()),"oor":oor_pct}
|
| 599 |
+
st.session_state.results["oor_tbl"]=tbl
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 600 |
|
| 601 |
if "Validate" in st.session_state.results:
|
| 602 |
m = st.session_state.results["m_val"]
|
| 603 |
+
c1,c2,c3 = st.columns(3)
|
| 604 |
+
c1.metric("R", f"{m['R']:.2f}"); c2.metric("RMSE", f"{m['RMSE']:.2f}"); c3.metric("MAE", f"{m['MAE']:.2f}")
|
|
|
|
|
|
|
| 605 |
|
| 606 |
col_cross, col_track = st.columns([3, 2], gap="large")
|
| 607 |
with col_cross:
|
| 608 |
st.pyplot(
|
| 609 |
+
cross_plot_static(st.session_state.results["Validate"][TARGET],
|
| 610 |
+
st.session_state.results["Validate"]["UCS_Pred"]),
|
|
|
|
|
|
|
| 611 |
use_container_width=True
|
| 612 |
)
|
| 613 |
with col_track:
|
| 614 |
st.plotly_chart(
|
| 615 |
track_plot(st.session_state.results["Validate"], include_actual=True),
|
| 616 |
+
use_container_width=True, config={"displayModeBar": False, "scrollZoom": True}
|
|
|
|
| 617 |
)
|
| 618 |
|
| 619 |
sv = st.session_state.results["sv_val"]
|
| 620 |
+
if sv["oor"] > 0: st.warning("Some inputs fall outside **training min–max** ranges.")
|
|
|
|
| 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"])
|
|
|
|
| 627 |
# =========================
|
| 628 |
if st.session_state.app_step == "predict":
|
| 629 |
st.sidebar.header("Prediction (No Actual UCS)")
|
| 630 |
+
up = st.sidebar.file_uploader("Upload Prediction Excel", type=["xlsx","xls"])
|
| 631 |
if up is not None:
|
| 632 |
book = read_book_bytes(up.getvalue())
|
| 633 |
if book:
|
|
|
|
| 636 |
if st.sidebar.button("Preview data", use_container_width=True, disabled=(up is None)):
|
| 637 |
preview_modal(read_book_bytes(up.getvalue()))
|
| 638 |
go_btn = st.sidebar.button("Predict", type="primary", use_container_width=True)
|
| 639 |
+
if st.sidebar.button("⬅ Back to Case Building", use_container_width=True): st.session_state.app_step="dev"; st.rerun()
|
|
|
|
|
|
|
| 640 |
|
| 641 |
if go_btn and up is not None:
|
| 642 |
+
book = read_book_bytes(up.getvalue()); name = list(book.keys())[0]
|
|
|
|
| 643 |
df = book[name].copy()
|
| 644 |
+
if not ensure_cols(df, FEATURES): st.error("Missing required columns."); st.stop()
|
|
|
|
|
|
|
| 645 |
df["UCS_Pred"] = model.predict(df[FEATURES])
|
| 646 |
+
st.session_state.results["PredictOnly"]=df
|
| 647 |
|
| 648 |
+
ranges = st.session_state.train_ranges; oor_pct = 0.0
|
|
|
|
| 649 |
if ranges:
|
| 650 |
+
any_viol = pd.DataFrame({f:(df[f]<ranges[f][0])|(df[f]>ranges[f][1]) for f in FEATURES}).any(axis=1)
|
| 651 |
+
oor_pct = float(any_viol.mean()*100.0)
|
| 652 |
+
st.session_state.results["sv_pred"]={
|
| 653 |
+
"n":len(df),
|
| 654 |
+
"pred_min":float(df["UCS_Pred"].min()),
|
| 655 |
+
"pred_max":float(df["UCS_Pred"].max()),
|
| 656 |
+
"pred_mean":float(df["UCS_Pred"].mean()),
|
| 657 |
+
"pred_std":float(df["UCS_Pred"].std(ddof=0)),
|
| 658 |
+
"oor":oor_pct
|
|
|
|
|
|
|
| 659 |
}
|
| 660 |
|
| 661 |
if "PredictOnly" in st.session_state.results:
|
| 662 |
+
df = st.session_state.results["PredictOnly"]; sv = st.session_state.results["sv_pred"]
|
|
|
|
| 663 |
|
| 664 |
+
col_left, col_right = st.columns([2,3], gap="large")
|
| 665 |
with col_left:
|
| 666 |
table = pd.DataFrame({
|
| 667 |
+
"Metric": ["# points","Pred min","Pred max","Pred mean","Pred std","OOR %"],
|
| 668 |
+
"Value": [sv["n"],
|
| 669 |
+
round(sv["pred_min"],2),
|
| 670 |
+
round(sv["pred_max"],2),
|
| 671 |
+
round(sv["pred_mean"],2),
|
| 672 |
+
round(sv["pred_std"],2),
|
| 673 |
+
f'{sv["oor"]:.1f}%']
|
|
|
|
|
|
|
| 674 |
})
|
| 675 |
st.success("Predictions ready ✓")
|
| 676 |
df_centered_rounded(table, hide_index=True)
|
|
|
|
| 678 |
with col_right:
|
| 679 |
st.plotly_chart(
|
| 680 |
track_plot(df, include_actual=False),
|
| 681 |
+
use_container_width=True, config={"displayModeBar": False, "scrollZoom": True}
|
|
|
|
| 682 |
)
|
| 683 |
|
| 684 |
# =========================
|