Spaces:
Sleeping
Sleeping
File size: 5,734 Bytes
61b4af1 | 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 | # 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()
|