import numpy as np
from sklearn.datasets import make_moons
from sklearn.metrics import balanced_accuracy_score, confusion_matrix, f1_score
from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

X, y = make_moons(n_samples=1000, noise=0.25, random_state=7)
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42, stratify=y
)
pipeline = Pipeline([
    ("scale", StandardScaler()),
    ("model", SVC(kernel="rbf")),
])
parameters = {
    "model__C": [0.1, 1.0, 10.0],
    "model__gamma": [0.1, 1.0, "scale"],
}
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
search = GridSearchCV(
    pipeline, parameters, scoring="balanced_accuracy", cv=cv,
    return_train_score=True, refit=True, n_jobs=1, error_score="raise"
)
search.fit(X_train, y_train)
results = search.cv_results_
assert len(results["params"]) == 9
for index in np.argsort(results["mean_test_score"])[::-1]:
    # mean_test_score here refers to CV validation folds.
    print(results["params"][index],
          "train", round(results["mean_train_score"][index], 3),
          "validation", round(results["mean_test_score"][index], 3))

print("Selected parameters:", search.best_params_)
print("Mean validation score:", search.best_score_)
prediction = search.best_estimator_.predict(X_test)
print("Final test balanced accuracy:", balanced_accuracy_score(y_test, prediction))
print("Final test F1:", f1_score(y_test, prediction))
print("Confusion matrix, classes=[0, 1]:")
print(confusion_matrix(y_test, prediction, labels=[0, 1]))

# Check the one-weight gradient-descent example from the lesson.
w, x, target, learning_rate = 0.0, 2.0, 4.0, 0.1
loss_before = 0.5 * (w * x - target) ** 2
gradient = (w * x - target) * x
w -= learning_rate * gradient
loss_after = 0.5 * (w * x - target) ** 2
print("One gradient step:", loss_before, "->", loss_after)
