dima806's picture
Upload 51 files
3677ab8 verified
Raw
History Blame Contribute Delete
3.32 kB
"""Batch churn probability prediction for all active customers.
Run as a module:
uv run python -m src.prediction.batch_predict
"""
import pandas as pd
from src.config import (
CHURN_PERIOD_DAYS,
DATA_DIR,
PREDICTIONS_DIR,
RISK_HIGH,
RISK_MEDIUM,
)
from src.feature_engineering.pipeline import build_feature_matrix
from src.models.train import get_feature_cols, load_model
from src.preprocessing.clean import clean_customers, clean_orders
from src.target.churn_target import compute_churn_target
from src.utils.logger import get_logger
logger = get_logger(__name__)
def assign_risk_bucket(prob: float) -> str:
"""Map a churn probability to a risk segment label.
Args:
prob: Predicted churn probability in [0, 1].
Returns:
One of ``"high"``, ``"medium"``, or ``"low"``.
"""
if prob >= RISK_HIGH:
return "high"
if prob >= RISK_MEDIUM:
return "medium"
return "low"
def run_batch_prediction(
orders: pd.DataFrame | None = None,
customers: pd.DataFrame | None = None,
) -> pd.DataFrame:
"""Generate batch churn predictions for all eligible customers.
The model is trained on the historical window ending at
``T_max − CHURN_PERIOD_DAYS``. Predictions represent estimated churn
probability over the *next* ``CHURN_PERIOD_DAYS`` days.
Args:
orders: Pre-loaded orders DataFrame (reads from disk if None).
customers: Pre-loaded customers DataFrame (reads from disk if None).
Returns:
DataFrame with columns [customer_id, churn_probability, risk_bucket].
Also persisted to ``predictions/churn_predictions.csv``.
"""
if orders is None:
orders = pd.read_csv(DATA_DIR / "orders.csv", parse_dates=["order_date", "contract_date"])
if customers is None:
customers = pd.read_csv(
DATA_DIR / "customers.csv",
parse_dates=["registration_date", "birth_date", "last_profile_update"],
)
orders = clean_orders(orders)
customers = clean_customers(customers)
_, feature_end_date, t_max = compute_churn_target(orders, customers, CHURN_PERIOD_DAYS)
logger.info(f"Building features up to {feature_end_date.date()} (T_max={t_max.date()})")
features = build_feature_matrix(orders, customers, feature_end_date)
model = load_model()
feature_cols = get_feature_cols(features)
X = features[feature_cols]
probs = model.predict_proba(X)[:, 1]
predictions = pd.DataFrame(
{
"customer_id": features["customer_id"].values,
"churn_probability": probs.round(6),
"risk_bucket": [assign_risk_bucket(p) for p in probs],
}
)
PREDICTIONS_DIR.mkdir(parents=True, exist_ok=True)
output_path = PREDICTIONS_DIR / "churn_predictions.csv"
predictions.to_csv(output_path, index=False)
dist = predictions["risk_bucket"].value_counts().to_dict()
logger.info(
f"Predictions saved → {output_path} "
f"high={dist.get('high', 0)}, medium={dist.get('medium', 0)}, low={dist.get('low', 0)}"
)
return predictions
if __name__ == "__main__":
logger.info("=== Batch Prediction ===")
preds = run_batch_prediction()
logger.info(f"Generated {len(preds):,} predictions")
logger.info("=== Done ===")