File size: 4,290 Bytes
3fdedfb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
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__"


@st.cache_resource
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()