# -- Code Cell --
import numpy as np
import pandas as pd
train = pd.read_csv("train.csv")
test = pd.read_csv("test.csv")

# -- Code Cell --
# =========================
# Workfloew: Train -> Face Tune -> Scanez cu SHAP -> Drop -> Refit -> fac submission.csv
# =========================

import numpy as np
import pandas as pd

from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.metrics import mean_absolute_error

import shap


# -------------------------
# 1) CONFIG
# -------------------------
target = "delay_minutes"

cat_features = ["weather", "weekday", "comfort_class"]
num_features = ["SampleID", "distance_km",  "avg_speed_kmh","num_stops","special_events", "num_cars", "ticket_price"]


# -------------------------
# 2) FEATURE ENGINEERING (same for train/test)
# -------------------------
def build_features(df: pd.DataFrame) -> pd.DataFrame:
    X = df[num_features + ["departure_time"] + cat_features].copy()

    # departure_time -> minutes in day
    t = pd.to_datetime(X["departure_time"], format="%H:%M", errors="coerce")
    X["departure_time"] = t.dt.hour * 60 + t.dt.minute

    # one-hot
    X = pd.get_dummies(X, columns=cat_features, drop_first=True)
    return X


X = build_features(train)
y = train[target].copy()

X_test_raw = build_features(test)

# align test columns to train columns (after one-hot)
X_test_raw = X_test_raw.reindex(columns=X.columns, fill_value=0)


# -------------------------
# 3) SPLIT for local validation (MAE)
# -------------------------
X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, random_state=42
)


# -------------------------
# 4) RANDOM SEARCH (no early stopping)
# -------------------------
base = XGBRegressor(
    objective="reg:squarederror",
    random_state=42,
    n_jobs=-1,
    tree_method="hist"
)

param_dist = {
    "n_estimators": [200, 400, 600, 800, 1200],
    "learning_rate": [0.01, 0.03, 0.05, 0.1],
    "max_depth": [3, 4, 5, 6, 8],
    "min_child_weight": [1, 3, 5, 10],
    "subsample": [0.6, 0.8, 1.0],
    "colsample_bytree": [0.6, 0.8, 1.0],
    "reg_alpha": [0, 0.1, 1.0],
    "reg_lambda": [1.0, 2.0, 5.0],
    "gamma": [0, 0.1, 0.3],
}

search = RandomizedSearchCV(
    base,
    param_distributions=param_dist,
    n_iter=40,
    cv=3,
    scoring="neg_mean_absolute_error",
    n_jobs=-1,
    random_state=42,
    verbose=1
)

search.fit(X_train, y_train)

best = search.best_estimator_
pred_val = best.predict(X_val)

print("Best params:", search.best_params_)
print("MAE (val):", mean_absolute_error(y_val, pred_val))


# -------------------------
# 5) SHAP -> pick bottom 10% to drop (optional)
# -------------------------
X_shap = X_val.sample(min(2000, len(X_val)), random_state=42)

explainer = shap.TreeExplainer(best)
shap_values = explainer.shap_values(X_shap)

# plots (optional, comment if you don't want)
shap.summary_plot(shap_values, X_shap, plot_type="bar")
shap.summary_plot(shap_values, X_shap)

mean_abs = np.abs(shap_values).mean(axis=0)
imp = pd.Series(mean_abs, index=X_shap.columns).sort_values(ascending=False)

print("\nTop 20 SHAP:")
print(imp.head(20))
print("\nBottom 30 SHAP:")
print(imp.tail(30))

threshold = imp.quantile(0.10)  # bottom 10%
to_drop = imp[imp <= threshold].index.tolist()
print("\nDrop candidates (bottom 10%):", to_drop)


# -------------------------
# 6) Refit on FULL TRAIN (X,y) with dropped features
# -------------------------
X_full2 = X.drop(columns=to_drop, errors="ignore")

model2 = XGBRegressor(**best.get_params())
model2.fit(X_full2, y)


# -------------------------
# 7) Build final test matrix (same drop + same columns)
# -------------------------
X_test2 = X_test_raw.drop(columns=to_drop, errors="ignore")
X_test2 = X_test2.reindex(columns=X_full2.columns, fill_value=0)


# -------------------------
# 8) Predict + round + export submission.csv
# -------------------------
pred_test = model2.predict(X_test2)
pred_test_int = np.rint(pred_test).astype(int)      # rounded to nearest int
pred_test_int = np.clip(pred_test_int, 0, None)     # optional: no negative delays

submission = pd.DataFrame({
    "SampleID": test["SampleID"].values,
    "delay_minutes": pred_test_int
})

submission.to_csv("submission.csv", index=False)
print("\nSaved: submission.csv")
print(submission.head())

# -- Code Cell --
import numpy as np
import pandas as pd

from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.metrics import mean_absolute_error

import shap


# -----------------------
# DATE DE BAZA
# -----------------------
target = "delay_minutes"

cat_features = ["weather", "weekday", "comfort_class"]
num_features = [
    "SampleID", "distance_km", "avg_speed_kmh",
    "num_stops", "special_events", "num_cars", "ticket_price"
]


# -----------------------
# PREGATIRE TRAIN
# -----------------------
X = train[num_features + ["departure_time"] + cat_features].copy()
y = train[target]

# transform departure_time (destul de basic)
t = pd.to_datetime(X["departure_time"], format="%H:%M", errors="coerce")
X["departure_time"] = t.dt.hour * 60 + t.dt.minute

# one hot encoding
X = pd.get_dummies(X, columns=cat_features, drop_first=True)


# -----------------------
# PREGATIRE TEST
# -----------------------
X_test = test[num_features + ["departure_time"] + cat_features].copy()

t2 = pd.to_datetime(X_test["departure_time"], format="%H:%M", errors="coerce")
X_test["departure_time"] = t2.dt.hour * 60 + t2.dt.minute

X_test = pd.get_dummies(X_test, columns=cat_features, drop_first=True)

# ne asiguram ca are aceleasi coloane ca train
X_test = X_test.reindex(columns=X.columns, fill_value=0)


# -----------------------
# SPLIT
# -----------------------
X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, random_state=42
)


# -----------------------
# RANDOM SEARCH (simplu)
# -----------------------
model = XGBRegressor(
    objective="reg:squarederror",
    random_state=42,
    n_jobs=-1,
    tree_method="hist"
)

param_dist = {
    "n_estimators": [200, 400, 600, 800],
    "learning_rate": [0.03, 0.05, 0.1],
    "max_depth": [3, 4, 6],
    "subsample": [0.8, 1.0],
    "colsample_bytree": [0.6, 0.8, 1.0],
}

search = RandomizedSearchCV(
    model,
    param_dist,
    n_iter=20,
    cv=3,
    scoring="neg_mean_absolute_error",
    n_jobs=-1,
    random_state=42,
    verbose=1
)

search.fit(X_train, y_train)

best = search.best_estimator_

pred = best.predict(X_val)
print("MAE:", mean_absolute_error(y_val, pred))


# -----------------------
# SHAP (folosit doar ca sa vad ce conteaza)
# -----------------------
X_shap = X_val.sample(min(1500, len(X_val)), random_state=42)

explainer = shap.TreeExplainer(best)
shap_values = explainer.shap_values(X_shap)

shap.summary_plot(shap_values, X_shap, plot_type="bar")

# calculez importanta foarte basic
mean_abs = np.abs(shap_values).mean(axis=0)
imp = pd.Series(mean_abs, index=X_shap.columns).sort_values()

# iau cele mai slabe cateva
to_drop = imp.head(10).index.tolist()
print("Drop:", to_drop)


# -----------------------
# REFIT PE TOT TRAIN
# -----------------------
X2 = X.drop(columns=to_drop, errors="ignore")

model2 = XGBRegressor(**best.get_params())
model2.fit(X2, y)


# -----------------------
# TEST FINAL + SUBMISSION
# -----------------------
X_test2 = X_test.drop(columns=to_drop, errors="ignore")
X_test2 = X_test2.reindex(columns=X2.columns, fill_value=0)

pred_test = model2.predict(X_test2)
pred_test = np.rint(pred_test).astype(int)
pred_test = np.clip(pred_test, 0, None)

submission = pd.DataFrame({
    "SampleID": test["SampleID"],
    "delay_minutes": pred_test
})

submission.to_csv("submission.csv", index=False)
print(submission.head())


# -- Code Cell --
import numpy as np
import pandas as pd

from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.metrics import mean_absolute_error

import shap


target = "delay_minutes"

cat_features = ["weather", "weekday", "comfort_class"]
num_features = [
    "SampleID", "distance_km", "avg_speed_kmh",
    "num_stops", "special_events", "num_cars", "ticket_price"
]


# -------------------
# TRAIN FEATURES
# -------------------
X = train[num_features + ["departure_time"] + cat_features].copy()
y = train[target].copy()

t = pd.to_datetime(X["departure_time"], format="%H:%M", errors="coerce")
X["departure_time"] = t.dt.hour * 60 + t.dt.minute

X = pd.get_dummies(X, columns=cat_features, drop_first=True)


# -------------------
# TEST FEATURES
# -------------------
X_test = test[num_features + ["departure_time"] + cat_features].copy()

t2 = pd.to_datetime(X_test["departure_time"], format="%H:%M", errors="coerce")
X_test["departure_time"] = t2.dt.hour * 60 + t2.dt.minute

X_test = pd.get_dummies(X_test, columns=cat_features, drop_first=True)

# super important: aceleasi coloane ca train
X_test = X_test.reindex(columns=X.columns, fill_value=0)


# -------------------
# SPLIT
# -------------------
X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, random_state=42
)


# -------------------
# RANDOM SEARCH (ca in varianta buna)
# -------------------
base = XGBRegressor(
    objective="reg:squarederror",
    random_state=42,
    n_jobs=-1,
    tree_method="hist"
)

param_dist = {
    "n_estimators": [200, 400, 600, 800, 1200],
    "learning_rate": [0.01, 0.03, 0.05, 0.1],
    "max_depth": [3, 4, 5, 6, 8],
    "min_child_weight": [1, 3, 5, 10],
    "subsample": [0.6, 0.8, 1.0],
    "colsample_bytree": [0.6, 0.8, 1.0],
    "reg_alpha": [0, 0.1, 1.0],
    "reg_lambda": [1.0, 2.0, 5.0],
    "gamma": [0, 0.1, 0.3],
}

search = RandomizedSearchCV(
    base,
    param_distributions=param_dist,
    n_iter=40,
    cv=3,
    scoring="neg_mean_absolute_error",
    n_jobs=-1,
    random_state=42,
    verbose=1
)

search.fit(X_train, y_train)

best = search.best_estimator_

pred = best.predict(X_val)
print("Best params:", search.best_params_)
print("MAE:", mean_absolute_error(y_val, pred))


# -------------------
# SHAP (bottom 10%, ca in varianta buna)
# -------------------
X_shap = X_val.sample(min(2000, len(X_val)), random_state=42)

explainer = shap.TreeExplainer(best)
shap_values = explainer.shap_values(X_shap)

shap.summary_plot(shap_values, X_shap, plot_type="bar")
shap.summary_plot(shap_values, X_shap)

mean_abs = np.abs(shap_values).mean(axis=0)
imp = pd.Series(mean_abs, index=X_shap.columns).sort_values(ascending=False)

threshold = imp.quantile(0.10)   # bottom 10%
to_drop = imp[imp <= threshold].index.tolist()

print("Drop candidates (bottom 10%):", to_drop)


# -------------------
# REFIT PE TOT TRAIN (X,y) FARA to_drop
# -------------------
X_full2 = X.drop(columns=to_drop, errors="ignore")

model2 = XGBRegressor(**best.get_params())
model2.fit(X_full2, y)


# -------------------
# SUBMISSION
# -------------------
X_test2 = X_test.drop(columns=to_drop, errors="ignore")
X_test2 = X_test2.reindex(columns=X_full2.columns, fill_value=0)

pred_test = model2.predict(X_test2)
pred_test_int = np.rint(pred_test).astype(int)
pred_test_int = np.clip(pred_test_int, 0, None)  # optional

submission = pd.DataFrame({
    "SampleID": test["SampleID"].values,
    "delay_minutes": pred_test_int
})

submission.to_csv("submission.csv", index=False)
print(submission.head())


# -- Code Cell --
import numpy as np
import pandas as pd

from xgboost import XGBRegressor
from sklearn.model_selection import train_test_split, RandomizedSearchCV
from sklearn.metrics import mean_absolute_error

import shap


target = "delay_minutes"
cat_features = ["weather", "weekday", "comfort_class"]
num_features = [
    "SampleID", "distance_km", "avg_speed_kmh",
    "num_stops", "special_events", "num_cars", "ticket_price"
]


#train
X = train[num_features + ["departure_time"] + cat_features].copy()
y = train[target].copy()
t = pd.to_datetime(X["departure_time"], format="%H:%M", errors="coerce")
X["departure_time"] = t.dt.hour * 60 + t.dt.minute
X = pd.get_dummies(X, columns=cat_features, drop_first=True)

#test
X_test = test[num_features + ["departure_time"] + cat_features].copy()
t2 = pd.to_datetime(X_test["departure_time"], format="%H:%M", errors="coerce")
X_test["departure_time"] = t2.dt.hour * 60 + t2.dt.minute
X_test = pd.get_dummies(X_test, columns=cat_features, drop_first=True)


X_test = X_test.reindex(columns=X.columns, fill_value=0)


X_train, X_val, y_train, y_val = train_test_split(
    X, y, test_size=0.2, random_state=42
)


base = XGBRegressor(
    objective="reg:squarederror",
    random_state=42,
    tree_method="hist"
)
param_dist = {
    "n_estimators": [200, 400, 600, 800, 1200],
    "learning_rate": [0.01, 0.03, 0.05, 0.1],
    "max_depth": [3, 4, 5, 6, 8],
    "min_child_weight": [1, 3, 5, 10],
    "subsample": [0.6, 0.8, 1.0],
    "colsample_bytree": [0.6, 0.8, 1.0],
    "reg_alpha": [0, 0.1, 1.0],
    "reg_lambda": [1.0, 2.0, 5.0],
    "gamma": [0, 0.1, 0.3],
}
search = RandomizedSearchCV(
    base,
    param_distributions=param_dist,
    n_iter=40,
    cv=3,
    scoring="neg_mean_absolute_error",
    n_jobs=-1,
    random_state=42,
    #verbose=1
)
search.fit(X_train, y_train)
model = search.best_estimator_
pred = model.predict(X_val)
print("Best params:", search.best_params_)
print("MAE1:", mean_absolute_error(y_val, pred))



X_shap = X_val.sample(min(2000, len(X_val)), random_state=42)

explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_shap)


mean_abs = np.abs(shap_values).mean(axis=0)
imp = pd.Series(mean_abs, index=X_shap.columns).sort_values(ascending=False)

threshold = imp.quantile(0.10)
to_drop = imp[imp <= threshold].index.tolist()

print("Drop:", to_drop)


X_train2 = X_train.drop(columns=to_drop, errors="ignore")
X_val2 = X_val.drop(columns=to_drop, errors="ignore")

model2 = XGBRegressor(**model.get_params())
model2.fit(X_train2, y_train)

pred2 = model2.predict(X_val2)
print("MAE2:", mean_absolute_error(y_val, pred2))



X_full2 = X.drop(columns=to_drop, errors="ignore")

model_final = XGBRegressor(**model.get_params())
model_final.fit(X_full2, y)

X_test2 = X_test.drop(columns=to_drop, errors="ignore")
X_test2 = X_test2.reindex(columns=X_full2.columns, fill_value=0)

pred_test = model_final.predict(X_test2)
pred_test_int = np.rint(pred_test).astype(int)


subm = pd.DataFrame({
    "SampleID": test["SampleID"].values,
    "delay_minutes": pred_test_int
})

subm.to_csv("submission.csv", index=False)
