Karthik Reddy
added crop recommendation system and restructured files
e2cc26b
Raw
History Blame Contribute Delete
5.73 kB
# 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()