import numpy as np
from sklearn.datasets import load_iris, load_wine, load_breast_cancer, load_digits
from sklearn.model_selection import StratifiedKFold
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, r2_score
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import (
RandomForestClassifier,
GradientBoostingClassifier,
HistGradientBoostingClassifier,
)
from sklearn.naive_bayes import GaussianNB
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LinearRegression, Lasso, ElasticNet, Ridge
SEED = 42
CONFIGS = {
"DecisionTree": lambda: DecisionTreeClassifier(max_depth=10, random_state=SEED),
"RandomForest": lambda: RandomForestClassifier(n_estimators=20, max_depth=10, random_state=SEED),
"GradientBoosting": lambda: GradientBoostingClassifier(n_estimators=100, max_depth=5, learning_rate=0.1, random_state=SEED),
"HistGBT": lambda: HistGradientBoostingClassifier(max_iter=100, max_depth=6, learning_rate=0.1, random_state=SEED),
"GaussianNB": lambda: GaussianNB(),
"KNN": lambda: KNeighborsClassifier(n_neighbors=5, weights="uniform"),
}
DATASETS = {
"iris": load_iris,
"wine": load_wine,
"breast_cancer": load_breast_cancer,
"digits": load_digits,
}
NEEDS_SCALING = {"KNN"}
BASELINES = [
("DecisionTree", "iris"),
("RandomForest", "iris"),
("GradientBoosting", "iris"),
("HistGBT", "iris"),
("GaussianNB", "iris"),
("KNN", "iris"),
("DecisionTree", "wine"),
("RandomForest", "wine"),
("GradientBoosting", "wine"),
("HistGBT", "wine"),
("GaussianNB", "wine"),
("DecisionTree", "breast_cancer"),
("RandomForest", "breast_cancer"),
("GradientBoosting", "breast_cancer"),
("HistGBT", "breast_cancer"),
("GaussianNB", "breast_cancer"),
("DecisionTree", "digits"),
("RandomForest", "digits"),
("GradientBoosting", "digits"),
("HistGBT", "digits"),
("GaussianNB", "digits"),
("KNN", "digits"),
]
def cross_val_score_stratified(model_fn, X, y, n_folds=5, seed=42, scale=False):
skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=seed)
scores = []
for train_idx, test_idx in skf.split(X, y):
X_train, X_test = X[train_idx], X[test_idx]
y_train, y_test = y[train_idx], y[test_idx]
if scale:
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)
model = model_fn()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
scores.append(accuracy_score(y_test, y_pred))
return scores
def main():
loaded = {}
for name, loader in DATASETS.items():
data = loader()
loaded[name] = (data.data, data.target)
print()
print("=" * 84)
print(" sklearn BASELINE — Classification (5-fold stratified CV, seed=42)")
print("=" * 84)
print(f" {'Model':<20} {'Dataset':<16} {'Mean':>10} {'Std':>10} {'Per-fold scores'}")
print(f" {'─' * 80}")
results = {}
for model_name, dataset_name in BASELINES:
X, y = loaded[dataset_name]
scale = model_name in NEEDS_SCALING
model_fn = CONFIGS[model_name]
scores = cross_val_score_stratified(model_fn, X, y, scale=scale)
mean = np.mean(scores)
std = np.std(scores)
results[(model_name, dataset_name)] = mean
fold_str = " ".join(f"{s:.4f}" for s in scores)
print(f" {model_name:<20} {dataset_name:<16} {mean:>10.4f} {std:>10.4f} [{fold_str}]")
scry_scores = {
("DecisionTree", "iris"): 0.9533,
("RandomForest", "iris"): 0.9533,
("GradientBoosting", "iris"): 0.9533,
("HistGBT", "iris"): 0.9333,
("GaussianNB", "iris"): 0.9600,
("KNN", "iris"): 0.9533,
("DecisionTree", "wine"): 0.9037,
("RandomForest", "wine"): 0.9495,
("GradientBoosting", "wine"): 0.9552,
("HistGBT", "wine"): 0.9776,
("GaussianNB", "wine"): 0.9776,
("DecisionTree", "breast_cancer"): 0.9526,
("RandomForest", "breast_cancer"): 0.9596,
("GradientBoosting", "breast_cancer"): 0.9474,
("HistGBT", "breast_cancer"): 0.9667,
("GaussianNB", "breast_cancer"): 0.9333,
("DecisionTree", "digits"): 0.8559,
("RandomForest", "digits"): 0.9466,
("GradientBoosting", "digits"): 0.9566,
("HistGBT", "digits"): 0.9716,
("GaussianNB", "digits"): 0.8392,
("KNN", "digits"): 0.9755,
}
print()
print("=" * 84)
print(" SIDE-BY-SIDE COMPARISON: scry-learn vs scikit-learn")
print("=" * 84)
print(f" {'Model':<20} {'Dataset':<16} {'sklearn':>10} {'scry':>10} {'Δ (scry-sk)':>12} {'Note'}")
print(f" {'─' * 80}")
for model_name, dataset_name in BASELINES:
sk = results[(model_name, dataset_name)]
scry = scry_scores.get((model_name, dataset_name))
if scry is None:
continue
delta = scry - sk
note = ""
if abs(delta) < 0.005:
note = "~match"
elif abs(delta) < 0.02:
note = "minor"
elif delta > 0:
note = "scry higher"
else:
note = "sklearn higher"
print(
f" {model_name:<20} {dataset_name:<16} {sk:>10.4f} {scry:>10.4f} {delta:>+12.4f} {note}"
)
print()
print(" Notes:")
print(" - Both use 5-fold StratifiedKFold, seed=42")
print(" - Differences come from: RNG (fastrand vs numpy), tie-breaking,")
print(" ceil vs floor for max_features, bootstrap sampling, init predictions")
print(" - These are implementation differences, not mathematical errors")
print()
if __name__ == "__main__":
main()