Spaces:
Sleeping
Sleeping
| import pickle | |
| from pathlib import Path | |
| import pandas as pd | |
| import streamlit as st | |
| st.set_page_config(page_title="UrbanNest Rent Predictor", layout="wide") | |
| ARTIFACT_DIR = Path("models") | |
| INFERENCE_BUNDLE_PATH = ARTIFACT_DIR / "inference_bundle.pkl" | |
| UNKNOWN_CATEGORY_TOKEN = "__UNK__" | |
| def load_inference_artifacts(): | |
| if not INFERENCE_BUNDLE_PATH.exists(): | |
| raise FileNotFoundError( | |
| "Missing models/inference_bundle.pkl. Run train.ipynb first." | |
| ) | |
| with open(INFERENCE_BUNDLE_PATH, "rb") as f: | |
| bundle = pickle.load(f) | |
| required_keys = {"model", "label_encoders", "feature_columns", "feature_metadata", "model_metadata"} | |
| missing_keys = required_keys - set(bundle.keys()) | |
| if missing_keys: | |
| raise KeyError(f"inference_bundle.pkl is missing keys: {sorted(missing_keys)}") | |
| return ( | |
| bundle["model"], | |
| bundle["label_encoders"], | |
| bundle["feature_columns"], | |
| bundle["feature_metadata"], | |
| bundle["model_metadata"], | |
| ) | |
| def _build_numeric_input(feature_name, metadata): | |
| dtype = metadata["dtype"] | |
| min_value = metadata["min"] | |
| max_value = metadata["max"] | |
| default_value = metadata["mean"] | |
| if "int" in dtype: | |
| return st.number_input( | |
| label=feature_name, | |
| min_value=int(min_value), | |
| max_value=int(max_value), | |
| value=int(round(default_value)), | |
| step=1, | |
| ) | |
| return st.number_input( | |
| label=feature_name, | |
| min_value=float(min_value), | |
| max_value=float(max_value), | |
| value=float(default_value), | |
| step=0.1, | |
| format="%.4f", | |
| ) | |
| def main(): | |
| st.title("UrbanNest Analytics: Dynamic House Rent Prediction") | |
| st.write("Provide property details below to estimate monthly rent (INR).") | |
| try: | |
| model, label_encoders, feature_columns, feature_metadata, model_metadata = load_inference_artifacts() | |
| except Exception as exc: | |
| st.error(f"Failed to load model artifacts: {exc}") | |
| st.stop() | |
| st.caption( | |
| f"Best training method: {model_metadata['best_method']} | " | |
| f"CV MAE: {model_metadata['best_cv_mae']:.2f} | " | |
| f"Test MAE: {model_metadata['test_mae']:.2f}" | |
| ) | |
| user_inputs = {} | |
| left_col, right_col = st.columns(2) | |
| column_index = 0 | |
| for feature_name in feature_columns: | |
| target_column = left_col if column_index % 2 == 0 else right_col | |
| with target_column: | |
| if feature_name in label_encoders: | |
| classes = [str(item) for item in label_encoders[feature_name].classes_] | |
| if UNKNOWN_CATEGORY_TOKEN in classes: | |
| display_options = [c for c in classes if c != UNKNOWN_CATEGORY_TOKEN] | |
| display_options.append("Other / Unknown") | |
| user_inputs[feature_name] = st.selectbox(feature_name, options=display_options) | |
| else: | |
| user_inputs[feature_name] = st.selectbox(feature_name, options=classes) | |
| else: | |
| user_inputs[feature_name] = _build_numeric_input(feature_name, feature_metadata[feature_name]) | |
| column_index += 1 | |
| if st.button("Predict"): | |
| encoded_row = {} | |
| for feature_name in feature_columns: | |
| feature_value = user_inputs[feature_name] | |
| if feature_name in label_encoders: | |
| raw_value = str(feature_value) | |
| if raw_value == "Other / Unknown": | |
| raw_value = UNKNOWN_CATEGORY_TOKEN | |
| encoded_row[feature_name] = int(label_encoders[feature_name].transform([raw_value])[0]) | |
| else: | |
| dtype = feature_metadata[feature_name]["dtype"] | |
| if "int" in dtype: | |
| encoded_row[feature_name] = int(feature_value) | |
| else: | |
| encoded_row[feature_name] = float(feature_value) | |
| model_input = pd.DataFrame([encoded_row], columns=feature_columns) | |
| prediction = float(model.predict(model_input)[0]) | |
| st.success(f"Predicted Monthly Rent: INR {prediction:,.2f}") | |
| if __name__ == "__main__": | |
| main() | |