library(reticulate)
py_require(c("pandas", "numpy", "scikit-learn", "matplotlib",
"seaborn", "palmerpenguins"))Palmer Penguins kNN Tuning (Python)
This Quarto document runs the Python translation of kNN_example_penguins2.qmd through reticulate.
Load, clean, and visualize the data
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from sklearn.metrics import ConfusionMatrixDisplay, accuracy_score, confusion_matrix
from sklearn.model_selection import GridSearchCV, StratifiedKFold, train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
try:
from palmerpenguins import load_penguins
penguins = load_penguins()
except ImportError:
penguins = sns.load_dataset("penguins")
penguins = penguins.drop(columns=["island", "sex"])
print(penguins["species"].value_counts().sort_index())species
Adelie 152
Chinstrap 68
Gentoo 124
Name: count, dtype: int64
print(penguins.isna().sum())species 0
bill_length_mm 2
bill_depth_mm 2
flipper_length_mm 2
body_mass_g 2
dtype: int64
print(penguins.describe(include="all")) species bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
count 344 342.000000 342.000000 342.000000 342.000000
unique 3 NaN NaN NaN NaN
top Adelie NaN NaN NaN NaN
freq 152 NaN NaN NaN NaN
mean NaN 43.921930 17.151170 200.915205 4201.754386
std NaN 5.459584 1.974793 14.061714 801.954536
min NaN 32.100000 13.100000 172.000000 2700.000000
25% NaN 39.225000 15.600000 190.000000 3550.000000
50% NaN 44.450000 17.300000 197.000000 4050.000000
75% NaN 48.500000 18.700000 213.000000 4750.000000
max NaN 59.600000 21.500000 231.000000 6300.000000
# Equivalent to step_naomit() in the R recipe.
penguins = penguins.dropna().copy()
sns.scatterplot(data=penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")
plt.tight_layout()
plt.show()
sns.pairplot(penguins, hue="species")
plt.show()
Split the data and define the model
X = penguins.drop(columns="species")
y = penguins["species"]
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.20, stratify=y, random_state=123
)
# Scaling is inside the pipeline so each cross-validation fold learns its own
# scaling parameters, preventing information leakage.
pipeline = Pipeline(
[
("scale", StandardScaler()),
("knn", KNeighborsClassifier(weights="uniform")),
]
)Tune k with five-fold cross-validation
neighbor_grid = list(range(4, 16))
folds = StratifiedKFold(n_splits=5, shuffle=True, random_state=234)
search = GridSearchCV(
pipeline,
param_grid={"knn__n_neighbors": neighbor_grid},
cv=folds,
scoring="accuracy",
return_train_score=False,
)
search.fit(X_train, y_train)GridSearchCV(cv=StratifiedKFold(n_splits=5, random_state=234, shuffle=True),
estimator=Pipeline(steps=[('scale', StandardScaler()),
('knn', KNeighborsClassifier())]),
param_grid={'knn__n_neighbors': [4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15]},
scoring='accuracy')In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook. On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
Parameters
Fitted attributes
Parameters
Fitted attributes
4 features
| bill_length_mm |
| bill_depth_mm |
| flipper_length_mm |
| body_mass_g |
Parameters
Fitted attributes
cv_results = pd.DataFrame(search.cv_results_)[
["param_knn__n_neighbors", "mean_test_score", "std_test_score", "rank_test_score"]
].sort_values("rank_test_score")
print(cv_results.to_string(index=False)) param_knn__n_neighbors mean_test_score std_test_score rank_test_score
5 0.985320 0.007342 1
4 0.978047 0.013571 2
6 0.978047 0.017926 3
11 0.977980 0.013734 4
13 0.977980 0.013734 4
7 0.974343 0.014615 6
9 0.974343 0.014615 6
8 0.974343 0.014615 6
10 0.970640 0.014683 9
12 0.967003 0.017872 10
14 0.967003 0.017872 10
15 0.967003 0.007414 10
print(f"Best k: {search.best_params_['knn__n_neighbors']}")Best k: 5
print(f"Best CV accuracy: {search.best_score_:.4f}")Best CV accuracy: 0.9853
Evaluate the finalized model
# GridSearchCV refits the best pipeline on all training data.
final_model = search.best_estimator_
test_predictions = final_model.predict(X_test)
print(f"Test accuracy: {accuracy_score(y_test, test_predictions):.4f}")Test accuracy: 1.0000
labels = final_model.classes_
cm = confusion_matrix(y_test, test_predictions, labels=labels)
print(pd.DataFrame(cm, index=labels, columns=labels)) Adelie Chinstrap Gentoo
Adelie 30 0 0
Chinstrap 0 14 0
Gentoo 0 0 25
ConfusionMatrixDisplay(cm, display_labels=labels).plot(cmap="Blues")<sklearn.metrics._plot.confusion_matrix.ConfusionMatrixDisplay object at 0x7f300267f380>
plt.title("Testing confusion matrix")
plt.tight_layout()
plt.show()