Spaces:
Sleeping
Sleeping
| # scripts/forecast_single.py | |
| import pandas as pd | |
| import numpy as np | |
| from pathlib import Path | |
| import joblib | |
| import sys | |
| # ---- CONFIG ---- | |
| DATA_PATH = Path("merged_crop_data_with_weather.csv") | |
| PAIRS_PATH = Path("mandi_crop_pairs.csv") | |
| OUT_DIR = Path("outputs/forecasts") | |
| FORECAST_HORIZON = 14 | |
| MODEL_PATH = Path("models/price_lgbm_model.pkl") | |
| def load_full_data() -> pd.DataFrame: | |
| df = pd.read_csv(DATA_PATH) | |
| df["date"] = pd.to_datetime(df["date"]) | |
| return df | |
| def load_model(): | |
| print(f"Loading model from {MODEL_PATH} ...") | |
| model = joblib.load(MODEL_PATH) | |
| return model | |
| def get_series_for_pair(full_df: pd.DataFrame, mandi_name: str, commodity_name: str) -> pd.DataFrame: | |
| mask = (full_df["Mandi"] == mandi_name) & (full_df["Commodity"] == commodity_name) | |
| sub = full_df.loc[mask].copy() | |
| sub = sub.sort_values("date") | |
| return sub | |
| def _sanitize_for_path(text: str) -> str: | |
| return ( | |
| str(text) | |
| .strip() | |
| .replace("/", "_") | |
| .replace("\\", "_") | |
| .replace(" ", "_") | |
| ) | |
| def save_forecast(forecast_df: pd.DataFrame, mandi_name: str, commodity_name: str) -> Path: | |
| safe_mandi = _sanitize_for_path(mandi_name) | |
| safe_commodity = _sanitize_for_path(commodity_name) | |
| out_dir = OUT_DIR / f"Mandi={safe_mandi}" / f"Commodity={safe_commodity}" | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| out_path = out_dir / "forecast.csv" | |
| forecast_df.to_csv(out_path, index=False) | |
| print(f"[OK] Saved forecast for {mandi_name} - {commodity_name} -> {out_path}") | |
| return out_path | |
| def make_feature_row_for_date( | |
| hist: pd.DataFrame, | |
| forecast_date: pd.Timestamp, | |
| mandi_name: str, | |
| commodity_name: str, | |
| ) -> pd.DataFrame | None: | |
| hist = hist.sort_values("date") | |
| prices = hist["ModalPrice"].values | |
| if len(prices) < 7: | |
| return None | |
| def get_lag(k: int) -> float: | |
| if len(prices) >= k: | |
| return float(prices[-k]) | |
| else: | |
| return np.nan | |
| lag_1 = get_lag(1) | |
| lag_2 = get_lag(2) | |
| lag_3 = get_lag(3) | |
| lag_7 = get_lag(7) | |
| if any(np.isnan(v) for v in [lag_1, lag_2, lag_3, lag_7]): | |
| return None | |
| dayofweek = forecast_date.dayofweek | |
| month = forecast_date.month | |
| data = { | |
| "lag_1": [lag_1], | |
| "lag_2": [lag_2], | |
| "lag_3": [lag_3], | |
| "lag_7": [lag_7], | |
| "dayofweek": [dayofweek], | |
| "month": [month], | |
| "Mandi": [mandi_name], | |
| "Commodity": [commodity_name], | |
| } | |
| feat_df = pd.DataFrame(data) | |
| feat_df["Mandi"] = feat_df["Mandi"].astype("category") | |
| feat_df["Commodity"] = feat_df["Commodity"].astype("category") | |
| return feat_df | |
| def ml_forecast_single_pair( | |
| model, | |
| full_df: pd.DataFrame, | |
| mandi_name: str, | |
| commodity_name: str, | |
| horizon: int = FORECAST_HORIZON, | |
| ) -> pd.DataFrame | None: | |
| series_df = get_series_for_pair(full_df, mandi_name, commodity_name) | |
| if series_df.empty: | |
| print(f"[SKIP] No data for {mandi_name} - {commodity_name}") | |
| return None | |
| if len(series_df) < 30: | |
| print(f"[SKIP] Too few rows ({len(series_df)}) for {mandi_name} - {commodity_name}") | |
| return None | |
| hist = series_df.sort_values("date")[["date", "ModalPrice"]].copy() | |
| forecasts = [] | |
| last_date = hist["date"].max() | |
| for step in range(horizon): | |
| forecast_date = last_date + pd.Timedelta(days=1) | |
| feat_row = make_feature_row_for_date( | |
| hist=hist, | |
| forecast_date=forecast_date, | |
| mandi_name=mandi_name, | |
| commodity_name=commodity_name, | |
| ) | |
| if feat_row is None: | |
| print(f"[SKIP] Not enough history for ML forecast {mandi_name} - {commodity_name}") | |
| return None | |
| pred_price = float(model.predict(feat_row)[0]) | |
| forecasts.append( | |
| { | |
| "date": forecast_date, | |
| "Mandi": mandi_name, | |
| "Commodity": commodity_name, | |
| "pred_modal_price": pred_price, | |
| } | |
| ) | |
| hist = pd.concat( | |
| [hist, pd.DataFrame({"date": [forecast_date], "ModalPrice": [pred_price]})], | |
| ignore_index=True, | |
| ) | |
| last_date = forecast_date | |
| forecast_df = pd.DataFrame(forecasts) | |
| return forecast_df | |
| def main(): | |
| print("Loading full data...") | |
| full_df = load_full_data() | |
| print("Loading model...") | |
| model = load_model() | |
| # 1) If user passed CLI args → use them | |
| if len(sys.argv) == 3: | |
| mandi_name = sys.argv[1] | |
| commodity_name = sys.argv[2] | |
| print(f"Using CLI args: {mandi_name} - {commodity_name}") | |
| else: | |
| # 2) Else auto-pick first pair with enough history | |
| print("No CLI args given, auto-selecting a pair with enough history...") | |
| pairs_df = pd.read_csv(PAIRS_PATH) | |
| mandi_name = None | |
| commodity_name = None | |
| for _, row in pairs_df.iterrows(): | |
| m = row["Mandi"] | |
| c = row["Commodity"] | |
| sub = get_series_for_pair(full_df, m, c) | |
| if len(sub) >= 30: | |
| mandi_name = m | |
| commodity_name = c | |
| print(f"Selected pair: {mandi_name} - {commodity_name} (rows={len(sub)})") | |
| break | |
| if mandi_name is None: | |
| print("Could not find any (Mandi, Commodity) pair with >= 30 rows.") | |
| return | |
| print(f"Running ML forecast demo for: {mandi_name} - {commodity_name}") | |
| forecast_df = ml_forecast_single_pair(model, full_df, mandi_name, commodity_name) | |
| if forecast_df is None: | |
| print("Forecast returned None.") | |
| return | |
| save_forecast(forecast_df, mandi_name, commodity_name) | |
| if __name__ == "__main__": | |
| main() | |