Download src/model.py from HarryDataScience/mumbai-crowd-predictor: direct link, hf CLI and curl.
- Browser
- Download file 6.03 kB
-
https://huggingface.co/spaces/HarryDataScience/mumbai-crowd-predictor/resolve/main/src/model.py
- Command line
-
hf download hf://spaces/HarryDataScience/mumbai-crowd-predictor/src/model.py
-
curl -L -o model.py https://huggingface.co/spaces/HarryDataScience/mumbai-crowd-predictor/resolve/main/src/model.py
6.03 kB
| """ | |
| Advanced ML Model for Mumbai Local Train Crowd Density Prediction | |
| Uses RandomForest with enhanced feature engineering | |
| """ | |
| import pandas as pd | |
| import numpy as np | |
| from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier | |
| from sklearn.preprocessing import LabelEncoder | |
| from sklearn.model_selection import cross_val_score | |
| from sklearn.metrics import accuracy_score, classification_report | |
| import joblib | |
| import os | |
| LABEL_MAP = {"Low": 0, "Medium": 1, "Extreme": 2} | |
| REVERSE_LABEL_MAP = {0: "Low", 1: "Medium", 2: "Extreme"} | |
| DAY_ORDER = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"] | |
| DAY_MAP = {d: i for i, d in enumerate(DAY_ORDER)} | |
| HOUR_WEIGHTS = { | |
| 5: 0.3, 6: 0.8, 7: 1.9, 8: 2.0, 9: 1.7, | |
| 10: 1.1, 11: 1.0, 12: 1.0, 13: 1.0, 14: 1.0, | |
| 15: 1.1, 16: 1.3, 17: 1.8, 18: 2.0, 19: 1.9, | |
| 20: 1.4, 21: 0.8, 22: 0.5 | |
| } | |
| LINE_MAP = {"Western": 0, "Central": 1, "Harbour": 2} | |
| DIR_MAP = {"Northbound": 0, "Southbound": 1} | |
| MODEL_PATH = os.path.join(os.path.dirname(__file__), "../data/model.pkl") | |
| ENCODERS_PATH = os.path.join(os.path.dirname(__file__), "../data/encoders.pkl") | |
| def engineer_features(df: pd.DataFrame) -> pd.DataFrame: | |
| df = df.copy() | |
| df["day_num"] = df["day_of_week"].map(DAY_MAP) | |
| df["is_weekday"] = (df["day_num"] < 5).astype(int) | |
| df["is_weekend"] = (df["day_num"] >= 5).astype(int) | |
| df["hour_weight"] = df["hour"].map(HOUR_WEIGHTS).fillna(0.5) | |
| df["line_num"] = df["line"].map(LINE_MAP).fillna(0) | |
| df["dir_num"] = df["direction"].map(DIR_MAP).fillna(0) | |
| # Peak hour flags | |
| df["is_morning_peak"] = ((df["hour"] >= 7) & (df["hour"] <= 9)).astype(int) | |
| df["is_evening_peak"] = ((df["hour"] >= 17) & (df["hour"] <= 19)).astype(int) | |
| df["is_peak"] = ((df["is_morning_peak"] == 1) | (df["is_evening_peak"] == 1)).astype(int) | |
| df["is_offpeak"] = ((df["hour"] < 6) | (df["hour"] > 21)).astype(int) | |
| # Directional rush interactions | |
| df["morning_southbound"] = ((df["is_morning_peak"] == 1) & (df["dir_num"] == 1)).astype(int) | |
| df["evening_northbound"] = ((df["is_evening_peak"] == 1) & (df["dir_num"] == 0)).astype(int) | |
| df["rush_dir_interaction"] = df["morning_southbound"] + df["evening_northbound"] | |
| # Weekend peak dampening | |
| df["effective_weight"] = df["hour_weight"] * (1.2 if 1 else 1.0) | |
| df["weekday_weight"] = df["hour_weight"] * df["is_weekday"] * 1.2 + df["hour_weight"] * df["is_weekend"] * 0.8 | |
| # Station encoding (label encoding) | |
| le_station = LabelEncoder() | |
| le_dest = LabelEncoder() | |
| all_stations = list(pd.concat([df["station"], df["destination"]]).unique()) | |
| le_station.fit(all_stations) | |
| le_dest.fit(all_stations) | |
| df["station_enc"] = le_station.transform(df["station"]) | |
| df["dest_enc"] = le_dest.transform(df["destination"]) | |
| return df, le_station, le_dest | |
| FEATURE_COLS = [ | |
| "hour", "day_num", "is_weekday", "is_weekend", | |
| "hour_weight", "line_num", "dir_num", | |
| "is_morning_peak", "is_evening_peak", "is_peak", "is_offpeak", | |
| "morning_southbound", "evening_northbound", "rush_dir_interaction", | |
| "weekday_weight", "station_enc", "dest_enc" | |
| ] | |
| def train_model(df: pd.DataFrame): | |
| df_feat, le_station, le_dest = engineer_features(df) | |
| df_feat["crowd_label"] = df_feat["crowd_level"].map(LABEL_MAP) | |
| X = df_feat[FEATURE_COLS] | |
| y = df_feat["crowd_label"] | |
| model = RandomForestClassifier( | |
| n_estimators=200, | |
| max_depth=12, | |
| min_samples_split=4, | |
| random_state=42, | |
| n_jobs=-1, | |
| class_weight="balanced" | |
| ) | |
| model.fit(X, y) | |
| preds = model.predict(X) | |
| acc = accuracy_score(y, preds) | |
| report = classification_report(y, preds, target_names=["Low", "Medium", "Extreme"]) | |
| # Save model | |
| os.makedirs(os.path.dirname(MODEL_PATH), exist_ok=True) | |
| joblib.dump(model, MODEL_PATH) | |
| joblib.dump({"le_station": le_station, "le_dest": le_dest}, ENCODERS_PATH) | |
| return model, acc, report, le_station, le_dest | |
| def load_model(): | |
| if os.path.exists(MODEL_PATH) and os.path.exists(ENCODERS_PATH): | |
| model = joblib.load(MODEL_PATH) | |
| encoders = joblib.load(ENCODERS_PATH) | |
| return model, encoders["le_station"], encoders["le_dest"] | |
| return None, None, None | |
| def predict_crowd( | |
| model, le_station, le_dest, | |
| line: str, station: str, destination: str, | |
| direction: str, hour: int, day_of_week: str | |
| ) -> dict: | |
| try: | |
| station_enc = le_station.transform([station])[0] | |
| dest_enc = le_dest.transform([destination])[0] | |
| except ValueError: | |
| station_enc = 0 | |
| dest_enc = 0 | |
| day_num = DAY_MAP.get(day_of_week, 0) | |
| is_weekday = int(day_num < 5) | |
| is_weekend = int(day_num >= 5) | |
| hour_weight = HOUR_WEIGHTS.get(hour, 0.5) | |
| line_num = LINE_MAP.get(line, 0) | |
| dir_num = DIR_MAP.get(direction, 0) | |
| is_morning_peak = int(7 <= hour <= 9) | |
| is_evening_peak = int(17 <= hour <= 19) | |
| is_peak = int(is_morning_peak or is_evening_peak) | |
| is_offpeak = int(hour < 6 or hour > 21) | |
| morning_southbound = int(is_morning_peak and dir_num == 1) | |
| evening_northbound = int(is_evening_peak and dir_num == 0) | |
| rush_dir = morning_southbound + evening_northbound | |
| weekday_weight = hour_weight * 1.2 if is_weekday else hour_weight * 0.8 | |
| features = [[ | |
| hour, day_num, is_weekday, is_weekend, | |
| hour_weight, line_num, dir_num, | |
| is_morning_peak, is_evening_peak, is_peak, is_offpeak, | |
| morning_southbound, evening_northbound, rush_dir, | |
| weekday_weight, station_enc, dest_enc | |
| ]] | |
| pred = model.predict(features)[0] | |
| proba = model.predict_proba(features)[0] | |
| label = REVERSE_LABEL_MAP[pred] | |
| confidence = float(max(proba)) * 100 | |
| return { | |
| "crowd_level": label, | |
| "crowd_code": int(pred), | |
| "confidence": round(confidence, 1), | |
| "probabilities": { | |
| "Low": round(float(proba[0]) * 100, 1), | |
| "Medium": round(float(proba[1]) * 100, 1), | |
| "Extreme": round(float(proba[2]) * 100, 1), | |
| } | |
| } | |