Skip to content

AI Integration · Machine Learning

KNN Classification: A Hands-On Guide with Python and Scikit-learn

Implement K-Nearest Neighbors classification using scikit-learn with data visualization, model training, and performance evaluation on real datasets.

Anurag Verma

Anurag Verma

7 min read

KNN Classification: A Hands-On Guide with Python and Scikit-learn

Sponsored

Share

This is a hands-on KNN classification walkthrough in Python: load a labeled dataset, scale it correctly, fit a k-nearest neighbors classifier with scikit-learn, and read the accuracy honestly instead of taking it at face value. Every step below runs as-is against the same dataset, from raw CSV to a tuned, evaluated model. If you want the theory behind why distance-based voting works before touching code, the KNN algorithm explainer covers that side.

Import necessary libraries:

A KNN classification pipeline in scikit-learn needs five libraries: numpy and pandas for loading and reshaping data, seaborn and matplotlib for visualizing class separation before modeling, and scikit-learn itself for the KNeighborsClassifier, the train/test split, feature scaling, and accuracy scoring used later in this walkthrough.

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.neighbors import KNeighborsClassifier
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score

Load the data:

The code reads a csv file containing the dataset into a pandas dataframe using pd.read_csv() method.

# Load your data into a pandas dataframe
df = pd.read_csv("/kaggle/input/knn-data1/KNN_Project_Data")
df
XVPMGWYHTRATTLLZIGGAHYKREDFSGUUBMGJMJHZCTARGET CLASS
01636.670614817.9885252565.995189358.347163550.4174911618.8708972147.641254330.7278931494.878631845.1360880
11013.402760577.5873322644.141273280.4282031161.8733912084.107872853.404981447.1576191193.032521861.0818091
21300.035501820.5186972025.854469525.562292922.2062612552.355407818.676686845.4914921968.3675131647.1862911
31059.3475421066.866418612.000041480.827789419.467495685.666983852.867810341.6647841154.3913681450.9353570
41018.3405261313.679056950.622661724.742174843.0659031370.554164905.469453658.118202539.4593501899.8507920
9951343.0606001289.142057407.307449567.5647641000.953905919.602401485.269059668.0073971124.7729962127.6282900
996938.8470571142.8843312096.064295483.242220522.7557711703.1697822007.548635533.514816379.264597567.2005451
997921.994822607.9969012065.482529497.107790457.4304271577.5062051659.197738186.854577978.3401071943.3049121
9981157.069348602.7491601548.809995646.8095281335.7378201455.5043902788.366441552.3881071264.8180791331.8790201
9991287.1500251303.6000852247.287535664.3624791132.682562991.7749412007.676371251.916948846.167511952.8957511

1000 rows × 11 columns

Data visualization:

The code creates a scatter plot matrix using the sns.pairplot method from the seaborn library and plots it using the plt.show method from the matplotlib library. This scatter plot matrix is used to visualize the relationships between the variables in the data.

# scatter plot matrix
sns.pairplot(df, hue='TARGET CLASS')
plt.show()

KNN visualization

Split the dataset into training and testing sets:

The code splits the dataset into two parts: training and testing. The train_test_split method from scikit-learn is used to split the data into 80% training data and 20% testing data. The X variable is assigned the values of the dataframe with the target column dropped and y variable is assigned the values of the target column.

# Split the dataset into training and testing sets
X = df.drop("TARGET CLASS", axis=1)
y = df["TARGET CLASS"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

Pre-process the data:

The code scales the data using the StandardScaler method from scikit-learn. The method fit_transform is applied to the training data and transform is applied to the testing data. This scaling is necessary because different features in the data have different ranges, and it is important to pre-process the data before applying a machine learning model.

# Pre-process the data
scaler = StandardScaler()
X_train = scaler.fit_transform(X_train)
X_test = scaler.transform(X_test)

Training and testing the KNN model:

The code trains a K-nearest neighbors (KNN) model using the training data and the KNeighborsClassifier method from scikit-learn. The KNN model is then tested using the testing data, and the accuracy of the model is calculated using the accuracy_score method from scikit-learn.

# Train the KNN model
knn = KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)

KNeighborsClassifier()

# Evaluate the model
y_pred = knn.predict(X_test)
accuracy = accuracy_score(y_test, y_pred)
print("Accuracy:", accuracy*100)

Accuracy: 82.0

Choosing k instead of accepting the default:

n_neighbors=5 is scikit-learn’s default, not a value tuned for this dataset. The standard way to pick k is an error-rate sweep: fit the model at several odd values of k, record the validation error at each, and look for the point where error stops dropping.

# Sweep k and record the error rate at each value
error_rates = []

for k in range(1, 40, 2):
    knn_k = KNeighborsClassifier(n_neighbors=k)
    knn_k.fit(X_train, y_train)
    pred_k = knn_k.predict(X_test)
    error_rates.append(np.mean(pred_k != y_test))

plt.plot(range(1, 40, 2), error_rates, marker='o')
plt.xlabel('k')
plt.ylabel('Error rate')
plt.show()

Odd values avoid ties in a two-class vote. The plot typically shows a steep drop, a flat basin, and then a slow climb as k grows large enough to average across the actual class boundary, the same underfitting-versus-overfitting tradeoff covered in bias vs. variance in predictive modeling. Pick k from inside that basin, not from whichever value happens to look lowest by a single point, since that can just be noise in this particular train/test split. Manually sweeping k works fine for one hyperparameter; once you’re tuning several at once, GridSearchCV does the same search more systematically.

Reading the results beyond accuracy:

Accuracy alone hides how the errors are distributed. A confusion matrix and a classification report show whether the model is making one type of mistake more than another, which matters more once the two classes are not perfectly balanced.

from sklearn.metrics import confusion_matrix, classification_report

print(confusion_matrix(y_test, y_pred))
print(classification_report(y_test, y_pred))

The confusion matrix breaks the 82% down into true positives, true negatives, false positives, and false negatives. The classification report adds precision and recall per class, which is the number to check before trusting accuracy on any dataset where one class shows up more often than the other, the full set of tradeoffs is covered in classification and regression evaluation metrics. On this dataset the classes are close to balanced, which is part of why a single accuracy number is a reasonable summary here, that would not hold on a fraud- or churn-style dataset where the minority class is the one that matters.

This notebook is one piece of a longer sequence; the 100-day data science roadmap has the full path from Python basics through model evaluation if you want the surrounding context.

Main Post: Complete-Data-Science-Bootcamp

Frequently asked questions

Why fit the scaler only on the training data?
Because the scaler learns the mean and standard deviation of what it is fitted on, and those are statistics about your data. Fit it on everything and the training process has indirectly seen the test set's distribution, so your test score is measuring a model with an unfair advantage. Fitting on train and calling transform on test mirrors production, where new data arrives after the scaler was already decided.
Does scaling matter more for KNN than for other models?
Yes, more than almost anywhere else. KNN is nothing but distance, and Euclidean distance sums squared differences across every feature at once, so a column ranging over thousands contributes vastly more than one ranging over hundreds regardless of which is actually predictive. Tree-based models split one feature at a time and are indifferent to scale; KNN is the opposite case.
Should I have changed k from the default of 5?
You should at least have checked. Five is scikit-learn's default, not a recommendation for your data. The standard approach is to sweep a range of k values and plot validation error against each, which usually shows a clear basin: too small and the model chases noise, too large and it averages across genuine class boundaries. For two classes an odd k also avoids ties.
Is 82% accuracy good here?
For a balanced two-class problem, yes, since random guessing sits at 50% and you have improved substantially on that. The number that would change this reading is the class balance. If 82% of rows carried one label, a model that always predicted that label would score the same, and the accuracy would be telling you about the dataset rather than the model. Check the balance, then look at the confusion matrix.
What does the pairplot actually tell me before I model?
Whether the problem is plausibly separable and how. If some pair of features already shows two visually distinct clusters, KNN will do well and you know roughly why. If every panel shows one undifferentiated blob, either the signal lives in a combination of more than two features, which KNN can still find, or it is not there and no amount of tuning will help. Either way it costs one line and sets your expectations honestly.

Sponsored

Sponsored

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored