← Back to list

Comparative Analysis of KNN and Gaussian Naive Bayes Through a Crop Recommendation Project

Introduction

Sachinkc · 2026-05-25 15:15 · 51 claps · 6.9 min read
#applied-ai #machine-learning #knn #naive-bayes #croprecommendation
Open on Medium ↗
Wiki topics: ML · Machine Learning EDU · Education & Learning

Comparative Analysis of KNN and Gaussian Naive Bayes Through a Crop Recommendation Project

Introduction

Recently, I started learning some fundamental Machine Learning classification algorithms, especially:

  • K-Nearest Neighbors (KNN)
  • Gaussian Naive Bayes

After learning the theoretical foundations of K-Nearest Neighbors (KNN) and Gaussian Naive Bayes, I decided to apply them to a practical real-world problem: Crop Recommendation. This project goes beyond simply achieving high accuracy. It demonstrates a complete, professional machine learning workflow from problem understanding and rigorous Exploratory Data Analysis (EDA) to proper preprocessing, feature scaling, cross-validation, hyperparameter tuning, and insightful model comparison.

Building a Crop Recommendation System

The idea was simple, a smart agricultural tool that recommends the best crop to plant based on environmental and soil conditions. I specifically chose the Crop Recommendation dataset because it is beginner-friendly and perfect for practicing classification algorithms.

Farmers often face difficulty selecting the best crop for their land due to changing environmental conditions.

A poor crop decision can lead to:

  • low productivity,
  • economic loss,
  • and inefficient use of resources.

Machine Learning can help by analyzing environmental data and making intelligent predictions.

Key Concepts Practiced in This Project

While implementing this project, I practiced several important concepts that I had recently learned:

  • Feature Scaling
  • Standardization vs Normalization
  • Label Encoding
  • Cross Validation
  • Hyperparameter Tuning
  • Model Evaluation
  • Confusion Matrix Analysis

Why I Chose KNN and Gaussian Naive Bayes

I selected KNN and Gaussian Naive Bayes because they represent two very different approaches to classification.

  • KNN is a distance-based algorithm that classifies samples based on neighboring data points.
  • Gaussian Naive Bayes is a probabilistic algorithm based on Bayes’ Theorem and feature independence assumptions.

By comparing both algorithms on the same dataset, I was able to better understand:

  • how preprocessing affects different models,
  • why scaling is critical for KNN,
  • and how model assumptions influence performance.

Dataset Overview

Dataset: Crop Recommendation Dataset (Kaggle)

The dataset contains:

  • 2200 rows
  • 7 input features
  • 22 crop categories

Key Features:

  • Nitrogen (N), Phosphorus (P), Potassium (K)
  • Temperature (°C), Humidity (%), pH, Rainfall (mm)

Target: 22 crop labels (Rice, Maize, Chickpea, etc.)

Importing Required Libraries

Before building the models, I imported the necessary libraries for:

  • data analysis,
  • visualization,
  • preprocessing,
  • model building,
  • and evaluation.
# Data analysis
import pandas as pd
import numpy as np

# Visualization
import matplotlib.pyplot as plt
import seaborn as sns

# Preprocessing
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import MinMaxScaler

# Model Selection
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import cross_val_score

# Models
from sklearn.neighbors import KNeighborsClassifier
from sklearn.naive_bayes import GaussianNB

# Evaluation
from sklearn.metrics import accuracy_score
from sklearn.metrics import confusion_matrix
from sklearn.metrics import classification_report

Loading the Dataset

df = pd.read_csv("data/raw/Crop_recommendation.csv")
df.head()

Before moving to model training, I explored the dataset to understand:

  • the number of rows and columns,
  • missing values,
  • feature distributions,
  • and class balance.
print(df.shape)
print(df.isnull().sum())

Observation

The dataset is clean and balanced:

  • no missing values,
  • balanced crop classes,
  • and numerical features suitable for classification algorithms.

Exploratory Data Analysis (EDA)

EDA helped me understand the structure and behavior of the dataset before preprocessing.

Statistical summary

key statistical insights such as mean, standard deviation, minimum, maximum, and quartile values of the dataset.

df.describe()

Feature Distribution Visualization

sns.set_style("whitegrid")

# Feature Distribution with KDE
features = df.drop('label', axis=1).columns

plt.figure(figsize=(15, 10))
for i, col in enumerate(features, 1):
    plt.subplot(3, 3, i)
    sns.histplot(data=df, x=col, kde=True, bins=25, color='#2E8B57', alpha=0.75)
    plt.title(f'{col}', fontsize=13, fontweight='bold')
    plt.xlabel('')
    plt.ylabel('')

plt.suptitle('Feature Distributions', fontsize=18, fontweight='bold', y=0.98)
plt.tight_layout()
plt.show()

Correlation Analysis

plt.figure(figsize=(11, 8))

sns.heatmap(
    df.drop('label', axis=1).corr(),
    annot=True,
    fmt='.2f',
    cmap='YlGnBu',
    linewidths=1,
    linecolor='white',
    square=True,
    cbar_kws={'shrink': 0.8}
)

plt.title('Feature Correlation Heatmap', fontsize=16, fontweight='bold', pad=15)
plt.xticks(rotation=45, ha='right')
plt.yticks(rotation=0)
plt.tight_layout()
plt.show()

What I Observed

I noticed that:

  • Some features like rainfall (range: ~20–300) have much larger values compared to pH (range: ~4–9).
  • feature distributions are not identical,
  • heatmap showed a strong positive correlation (0.74) between Phosphorus (P) and Potassium (K),
  • Other features had weak to moderate correlations, indicating low multicollinearity and supporting the independence assumption of Naive Bayes.

These observations strongly influenced my preprocessing strategy (especially standardization) and helped explain the performance difference between KNN and Gaussian Naive Bayes.

Label Encoding

The target labels are crop names such as: rice, maize, cotton, chickpea, etc.

Since machine learning models work with numbers, I converted the labels into numerical values using Label Encoding.

X = df.drop("label", axis=1)

y = df["label"]

encoder = LabelEncoder()

y_encoded = encoder.fit_transform(y)

Train-Test Split

To evaluate the models properly, I divided the dataset into:

  • training data (80%)
  • testing data (20%)
X_train, X_test, y_train, y_test = train_test_split(
    X,
    y_encoded,
    test_size=0.20,
    random_state=42,
    stratify=y_encoded
)

I used stratify=y_encoded to ensure that all crop categories are proportionally distributed across both training and testing sets.

Feature Scaling

Feature scaling became one of the most important concepts I learned during this project.

KNN is a distance-based algorithm, meaning:

features with larger numerical ranges can dominate predictions.

For example:

  • rainfall values can exceed 200,
  • while pH values usually stay between 4 and 9.

Without scaling:

  • rainfall would heavily influence distance calculations,
  • reducing the contribution of smaller-range features.

Standardization

I used StandardScaler to standardize the features.

scaler = StandardScaler()

X_train_scaled = scaler.fit_transform(X_train)

X_test_scaled = scaler.transform(X_test)

Standardization transforms data so that:

  • mean = 0
  • standard deviation = 1

This allows all features to contribute more equally.

Before vs After Scaling Visualization

  • Before Scaling : Features had drastically different ranges
sns.boxplot(data=X_train)

  • After Scaling : All features are now centered around 0 with similar scales.
sns.boxplot(data=pd.DataFrame(X_train_scaled))

Standardization vs Normalization

I also explored normalization using MinMaxScaler.

minmax = MinMaxScaler()

X_train_norm = minmax.fit_transform(X_train)

X_test_norm = minmax.transform(X_test)

What I Learned

  • Standardization worked slightly better for this project.
  • KNN performance improved significantly after scaling.
  • Gaussian Naive Bayes was less sensitive to scaling.

This helped me understand that:

preprocessing choices directly affect model behavior.

Building the KNN Model

KNN classifies data points based on their nearest neighbors.

The intuition is simple:

similar data points are likely to belong to the same class.

Hyperparameter Tuning with GridSearchCV

Instead of manually choosing parameters, I used GridSearchCV to automatically search for the best combination.

param_grid = {
    'n_neighbors': [3,5,7,9,11],
    'weights': ['uniform', 'distance'],
    'metric': ['euclidean', 'manhattan']
}

grid_search = GridSearchCV(
    KNeighborsClassifier(),
    param_grid,
    cv=5,
    scoring='accuracy',
    n_jobs=-1
)

grid_search.fit(X_train_scaled, y_train)

Why Hyperparameter Tuning Matters

Different parameter combinations can dramatically change model performance.

For example:

  • smaller K values may overfit,
  • larger K values may oversmooth predictions.

GridSearchCV helped me systematically find the best configuration.

Final KNN Model

knn_model = grid_search.best_estimator_

knn_model.fit(X_train_scaled, y_train)

knn_predictions = knn_model.predict(X_test_scaled)

Building Gaussian Naive Bayes

Unlike KNN, GaussianNB is a probabilistic algorithm.

It predicts classes using Bayes’ Theorem and assumes:

  • feature independence,
  • and Gaussian distributions.
nb_model = GaussianNB()

nb_model.fit(X_train, y_train)

nb_predictions = nb_model.predict(X_test)

Cross Validation

Initially, I evaluated the models using only a train-test split.

However, I learned that:

a single split may produce misleading results.

To improve reliability, I used 5-Fold Cross Validation.

cv_knn = cross_val_score(
    knn_model,
    X_train_scaled,
    y_train,
    cv=5,
    scoring='accuracy'
)

cv_nb = cross_val_score(
    nb_model,
    X_train,
    y_train,
    cv=5,
    scoring='accuracy'
)

Why Cross Validation Is Important

Cross-validation:

  • evaluates the model across multiple subsets,
  • reduces evaluation bias,
  • and gives more trustworthy performance estimates.

This was one of the most valuable concepts I practiced during this project.

Confusion Matrix

To better understand prediction behavior, I visualized the confusion matrix.

# Model Comparison Chart

models = ['KNN', 'GaussianNB']

accuracies = [
    knn_accuracy * 100,
    nb_accuracy * 100
]

plt.figure(figsize=(8,6))

bars = plt.bar(
    models,
    accuracies
)

plt.ylim(90, 100)

plt.ylabel('Accuracy (%)')

plt.title(
    'Model Accuracy Comparison',
    fontsize=16,
    fontweight='bold'
)

for bar in bars:

    height = bar.get_height()

    plt.text(
        bar.get_x() + bar.get_width()/2,
        height + 0.2,
        f'{height:.2f}%',
        ha='center',
        fontsize=12,
        fontweight='bold'
    )

plt.tight_layout()

plt.savefig(
    'reports/figures/model_comparison.png',
    dpi=300
)

plt.show()

Results and Model Comparison

| Metric                    | KNN      | GaussianNB |
| ------------------------- | -------- | ---------- |
| Test Accuracy             | 98.18%   | 99.55%     |
| Cross Validation Accuracy | 98.12%   | 99.43%     |
| Training Speed            | Moderate | Very Fast  |
| Scaling Sensitivity       | High     | Low        |

Key Observation

Gaussian Naive Bayes achieved slightly higher accuracy than KNN on this dataset.

One possible reason is that many features in the dataset show relatively independent behavior with near-Gaussian distributions, which aligns well with the assumptions of Gaussian Naive Bayes.

In contrast, KNN relies heavily on distance calculations and can still be affected by local feature variations even after scaling.

This taught me an important lesson:

simpler algorithms can perform extremely well when their assumptions align with the dataset.

Making Predictions on New Data

Finally, I tested the model on new input values.

new_data = np.array([
    [90, 42, 43, 21, 82, 6.5, 202]
])

new_scaled = scaler.transform(new_data)

prediction = knn_model.predict(new_scaled)

crop = encoder.inverse_transform(prediction)

print("Recommended Crop:", crop[0])

Key Learnings

This project helped me strengthen several important Machine Learning concepts:

  • Importance of preprocessing
  • Why feature scaling matters
  • Difference between normalization and standardization
  • Proper evaluation using cross-validation
  • Hyperparameter tuning using GridSearchCV
  • Understanding algorithm assumptions

Conclusion

This project helped me move beyond theory and apply Machine Learning concepts practically.

Rather than simply training models, I learned:

  • how preprocessing affects performance,
  • why validation matters,
  • and how different algorithms behave under the same dataset.

This project became an important step in transforming theoretical understanding into practical implementation.

GitHub Repository

You can find the complete implementation, notebook, and project structure here : **Crop_recommendation_project**


메타데이터
post_id
9e7b79cf3280
slug
comparative-analysis-of-knn-and-gaussian-naive-bayes-through-a-crop-recommendation-project-9e7b79cf3280
url
https://medium.com/@sachinkc263/comparative-analysis-of-knn-and-gaussian-naive-bayes-through-a-crop-recommendation-project-9e7b79cf3280
canonical_url
https://medium.com/@sachinkc263/comparative-analysis-of-knn-and-gaussian-naive-bayes-through-a-crop-recommendation-project-9e7b79cf3280
author_url
https://medium.com/@sachinkc263
status
ok
fetched_at
2026-06-12 18:14:10