from pathlib import Path
import pandas as pd
from sklearn.ensemble import HistGradientBoostingClassifier
from solution import (add_region_features, build_product_table, make_product_candidates,
make_submission, validate_submission_frame, contact_statistics)
DATA = Path("data")
PARAMS = dict(max_iter=200, learning_rate=0.03, max_leaf_nodes=15, min_samples_leaf=1000,
l2_regularization=1.0, random_state=42)
PRODUCTS = [1, 2, 3, 4] # продукт 5 исключён по валидации
SHARE = 0.20
# 1. загрузка
history = pd.read_csv(DATA / "history_clients.csv")
campaigns = pd.read_csv(DATA / "campaign_history.csv")
products = pd.read_csv(DATA / "products.csv", encoding="utf-8-sig")
regions = pd.read_csv(DATA / "region_profiles.csv")
public = pd.read_csv(DATA / "public_clients.csv")
# 2. признаки: колонки клиента + региональные, кроме client_id и eligible_*
history = add_region_features(history, regions)
public = add_region_features(public, regions)
features = [c for c in history.columns if c != "client_id" and not c.startswith("eligible_")]
# 3. кандидаты: public-клиент × доступный ему продукт
prod = products.set_index("product_id")
cands = make_product_candidates(public, products)
cands = cands[cands["product_id"].isin(PRODUCTS)].copy()
cands["score"] = 0.0
x_public = public.set_index("client_id")[features]
# 4. две модели на продукт (offered = 1 / offered = 0), score = uplift × маржа − стоимость
for j in PRODUCTS:
t = build_product_table(history, campaigns, j)
t = t[t[f"eligible_p{j}"] == 1]
m1 = HistGradientBoostingClassifier(**PARAMS).fit(t.loc[t["offered"] == 1, features], t.loc[t["offered"] == 1, "purchased"])
m0 = HistGradientBoostingClassifier(**PARAMS).fit(t.loc[t["offered"] == 0, features], t.loc[t["offered"] == 0, "purchased"])
m = cands["product_id"] == j
x = x_public.loc[cands.loc[m, "client_id"]]
cands.loc[m, "score"] = (m1.predict_proba(x)[:, 1] - m0.predict_proba(x)[:, 1]) * prod.loc[j, "margin_rub"] - prod.loc[j, "contact_cost_rub"]
print(f"продукт {j} готов")
# 5. лучший продукт на клиента, только score > 0, не более 20%
best = cands.sort_values(["score", "client_id"], ascending=[False, True]).drop_duplicates("client_id")
best = best[best["score"] > 0].head(int(SHARE * len(public)))
# 6. сборка, проверка, сохранение
offers = best.rename(columns={"product_id": "offer_id"})[["client_id", "offer_id"]]
submission = make_submission(public, offers)
diagnostics = validate_submission_frame(submission, public, SHARE)
assert diagnostics["is_valid"], diagnostics
print(diagnostics)
print(contact_statistics(submission))
submission.to_csv("submission.csv", index=False)
print("сохранено: submission.csv")