Download scripts/app.py from wasicse/mvppred: direct link, hf CLI and curl.
- Browser
- Download file 7.38 kB
-
https://huggingface.co/spaces/wasicse/mvppred/resolve/main/scripts/app.py
- Command line
-
hf download hf://spaces/wasicse/mvppred/scripts/app.py
-
curl -L -o app.py https://huggingface.co/spaces/wasicse/mvppred/resolve/main/scripts/app.py
7.38 kB
| import os | |
| import sys | |
| from pathlib import Path | |
| import joblib | |
| import numpy as np | |
| import pandas as pd | |
| import streamlit as st | |
| from huggingface_hub import hf_hub_download | |
| ARTIFACT_REPO = "wasicse/mvppred-artifacts" | |
| ARTIFACT_FILES = [ | |
| "angle_bundle.joblib", | |
| "bite_bundle.joblib", | |
| "distance_capacity_bundle.joblib", | |
| "endurance_bundle.joblib", | |
| "jump_accel_bundle.joblib", | |
| "jump_distance_bundle.joblib", | |
| "jump_power_bundle.joblib", | |
| "jump_vel_bundle.joblib", | |
| "sprint_bundle.joblib", | |
| ] | |
| def ensure_artifacts(): | |
| outdir = Path("artifacts_inference") | |
| outdir.mkdir(exist_ok=True) | |
| for name in ARTIFACT_FILES: | |
| target = outdir / name | |
| if not target.exists(): | |
| downloaded = hf_hub_download( | |
| repo_id=ARTIFACT_REPO, | |
| filename=name, | |
| repo_type="model", | |
| ) | |
| target.write_bytes(Path(downloaded).read_bytes()) | |
| ensure_artifacts() | |
| ROOT = Path(__file__).resolve().parents[1] | |
| if str(ROOT) not in sys.path: | |
| sys.path.insert(0, str(ROOT)) | |
| from infer import predict_with_confidence | |
| BUNDLE_DIR = "artifacts_inference" | |
| INTERVAL = "q90" | |
| CONFIDENCE_EMOJI = {"High": "🟢 High", "Medium": "🟡 Medium", "Low": "🔴 Low"} | |
| def load_bundle(path: str): | |
| return joblib.load(path) | |
| st.set_page_config(page_title="Lizard Performance Predictor", layout="wide") | |
| st.title("MVPpred: Lizard Performance Predictor") | |
| st.caption("Enter morphological measurements (use -1 for missing) to predict lizard performance traits.") | |
| if not os.path.isdir(BUNDLE_DIR): | |
| st.error(f"Bundle directory not found: {BUNDLE_DIR}") | |
| st.stop() | |
| bundle_files = sorted([f for f in os.listdir(BUNDLE_DIR) if f.endswith("_bundle.joblib")]) | |
| if not bundle_files: | |
| st.error("No *_bundle.joblib files found in bundle directory.") | |
| st.stop() | |
| targets = [f.replace("_bundle.joblib", "") for f in bundle_files] | |
| selected_targets = st.multiselect("Targets to predict", targets, default=targets) | |
| st.divider() | |
| default_sample = { | |
| "sex_num": 0, | |
| "mass": 4.7, | |
| "svl": 62.09, | |
| "hl": 18.55, | |
| "hw": 10.22, | |
| "hh": 6.24, | |
| "femur": 12.44, | |
| "tibia": 10.93, | |
| "metat": 4.18, | |
| "hindtoe": 9.54, | |
| "humerus": 8.43, | |
| "radius": 7.19, | |
| "metac": 2.48, | |
| "foretoe": 6.28, | |
| "tail": 72.69, | |
| } | |
| st.subheader("Enter sample") | |
| with st.form("manual_input_form"): | |
| default_sex = "m" if int(default_sample["sex_num"]) == 0 else "f" | |
| sex = st.selectbox("Sex", ["m", "f"], index=0 if default_sex == "m" else 1, | |
| help="Biological sex of the lizard") | |
| col1, col2, col3 = st.columns(3) | |
| with col1: | |
| mass = st.number_input("mass", value=float(default_sample["mass"]), help="Body mass (g)") | |
| svl = st.number_input("svl", value=float(default_sample["svl"]), help="Snout-vent length (mm)") | |
| hl = st.number_input("hl", value=float(default_sample["hl"]), help="Head length (mm)") | |
| hw = st.number_input("hw", value=float(default_sample["hw"]), help="Head width (mm)") | |
| hh = st.number_input("hh", value=float(default_sample["hh"]), help="Head height (mm)") | |
| with col2: | |
| femur = st.number_input("femur", value=float(default_sample["femur"]), help="Femur length (mm)") | |
| tibia = st.number_input("tibia", value=float(default_sample["tibia"]), help="Tibia length (mm)") | |
| metat = st.number_input("metat", value=float(default_sample["metat"]), help="Metatarsal length (mm)") | |
| hindtoe = st.number_input("hindtoe", value=float(default_sample["hindtoe"]), help="Longest hind toe length (mm)") | |
| with col3: | |
| humerus = st.number_input("humerus", value=float(default_sample["humerus"]), help="Humerus length (mm)") | |
| radius = st.number_input("radius", value=float(default_sample["radius"]), help="Radius length (mm)") | |
| metac = st.number_input("metac", value=float(default_sample["metac"]), help="Metacarpal length (mm)") | |
| foretoe = st.number_input("foretoe", value=float(default_sample["foretoe"]), help="Longest fore toe length (mm)") | |
| tail = st.number_input("tail", value=float(default_sample["tail"]), help="Tail length (mm)") | |
| run_btn = st.form_submit_button("Run predictions", use_container_width=True) | |
| if run_btn: | |
| if not selected_targets: | |
| st.warning("Please select at least one target.") | |
| st.stop() | |
| input_row = { | |
| "sex": 0 if sex == "m" else 1, | |
| "mass": mass, | |
| "svl": svl, | |
| "hl": hl, | |
| "hw": hw, | |
| "hh": hh, | |
| "femur": femur, | |
| "tibia": tibia, | |
| "metat": metat, | |
| "hindtoe": hindtoe, | |
| "humerus": humerus, | |
| "radius": radius, | |
| "metac": metac, | |
| "foretoe": foretoe, | |
| "tail": tail, | |
| } | |
| df = pd.DataFrame([input_row]) | |
| progress = st.progress(0) | |
| status = st.empty() | |
| all_outputs = [] | |
| n = len(selected_targets) | |
| for i, t in enumerate(selected_targets, start=1): | |
| status.write(f"Running {t} ({i}/{n}) …") | |
| path = os.path.join(BUNDLE_DIR, f"{t}_bundle.joblib") | |
| bundle = load_bundle(path) | |
| out = predict_with_confidence(bundle, df, interval=INTERVAL) | |
| out.insert(0, "target", t) | |
| all_outputs.append(out.reset_index(drop=True)) | |
| progress.progress(i / n) | |
| progress.empty() | |
| status.success("Predictions complete.") | |
| result = pd.concat(all_outputs, axis=0, ignore_index=True) | |
| # Map confidence label to emoji version | |
| result["confidence_label"] = result["confidence_label"].map( | |
| lambda x: CONFIDENCE_EMOJI.get(x, x) | |
| ) | |
| # Drop confidence_score — label is sufficient | |
| display_result = result.drop(columns=["confidence_score"]).copy() | |
| for col in ["prediction", "lower", "upper"]: | |
| display_result[col] = display_result[col].round(3) | |
| # Hide note column if all empty | |
| if display_result["note"].str.strip().eq("").all(): | |
| display_result = display_result.drop(columns=["note"]) | |
| st.subheader("Predictions") | |
| st.caption("Interval shown is the 90% conformal prediction interval.") | |
| st.markdown( | |
| """ | |
| <style> | |
| div[data-testid="stDataFrame"] th, div[data-testid="stDataFrame"] td { | |
| text-align: center !important; | |
| } | |
| </style> | |
| """, | |
| unsafe_allow_html=True, | |
| ) | |
| col_cfg = { | |
| "target": st.column_config.TextColumn("Target", width="small"), | |
| "prediction": st.column_config.NumberColumn("Prediction", format="%.3f", width="small"), | |
| "lower": st.column_config.NumberColumn("Lower (90%)", format="%.3f", width="small"), | |
| "upper": st.column_config.NumberColumn("Upper (90%)", format="%.3f", width="small"), | |
| "confidence_label": st.column_config.TextColumn("Confidence", width="small"), | |
| } | |
| if "note" in display_result.columns: | |
| col_cfg["note"] = st.column_config.TextColumn("Note", width="medium") | |
| st.dataframe(display_result, use_container_width=True, hide_index=True, column_config=col_cfg) | |
| csv_out = result.to_csv(index=False).encode("utf-8") | |
| st.download_button( | |
| "Download results CSV", | |
| csv_out, | |
| file_name="predictions_with_confidence.csv", | |
| mime="text/csv", | |
| ) | |