Skip to main content
LearnMLAcademy
Beginner10 min read

Scikit-learn Essentials

Build scikit-learn preprocessing pipelines, models, and transformers, with Python code, architecture diagrams, worked examples, and interview quiz.

What you will master in this lesson
Intuitive core mechanisms & practical real-world intuition
Step-by-step mathematical breakdown & visual diagrams
Runnable Python implementation & practical trade-offs

Scikit-Learn: The Complete ML Toolkit

Scikit-learn is one of the most widely used machine-learning libraries in Python. It provides a clean, consistent API for common stages of the ML workflow — from preprocessing and model training to evaluation and hyperparameter tuning. Once you understand its basic pattern, trying a different algorithm often requires only a small code change.

Scikit-learn in Simple Words

Think of scikit-learn as a toolbox for machine learning. You give it data, choose a model, let the model learn from examples, and then ask it to make predictions.

1

Prepare Data

Put your examples into X and the correct answers into y.

2

Choose Model

For example, LinearRegression or KNeighborsClassifier.

3

Learn

model.fit(X_train, y_train)

4

Predict

model.predict(X_test)

A tiny example

Study hoursMarks
140
250
360
470

From these examples, a simple regression model can learn the pattern and predict a mark for a new number of study hours.

🔄

Consistent API

fit() predict() score() for all models

🧮

40+ Algorithms

Regression, classification, clustering

⚙️

Preprocessing

Scaling, encoding, imputation

📊

Model Evaluation

Cross-val, metrics, GridSearch

The basic scikit-learn pattern

1

Create

model = Algorithm()

Choose the algorithm and its settings.

2

Fit

model.fit(X_train, y_train)

Learn patterns from the training examples.

3

Predict

model.predict(X_test)

Use the trained model on unseen examples.

4

Evaluate

accuracy_score(y_test, y_pred)

Compare predictions with the correct answers.

fit(), transform(), predict() — what is the difference?

MethodSimple meaningExample
fit()Learn from dataScaler learns mean; model learns a pattern
transform()Change data using what was learnedScale new values
predict()Give an answer for new dataPredict Pass/Fail
Python
pip install scikit-learn

import sklearn
print(sklearn.__version__)   # shows your installed version
Output
1.x.x  # exact version depends on your environment
🚀

1. Your First Complete ML Workflow

Start with one tiny example, then see a real built-in dataset

Start small: predict marks from study hours

Here the input X is study hours and the target y is marks.

Python
from sklearn.linear_model import LinearRegression
import numpy as np

X = np.array([[1], [2], [3], [4]])
y = np.array([40, 50, 60, 70])

model = LinearRegression()
model.fit(X, y)

prediction = model.predict([[5]])
print(prediction[0])
Output
80.0
1. Data
Give examples.
2. fit()
Learn the pattern.
3. predict()
Ask about 5 hours.
4. Result
Predicted marks = 80.

Now try a real built-in dataset: Iris

Why split the data?

150 flowers
all examples
→
120 train
model learns here
+
30 test
final check here
Python
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score

# 1. Load dataset (150 iris flowers, 4 features, 3 species)
X, y = load_iris(return_X_y=True)
print(f"Dataset shape: {X.shape}")  # (150, 4)

# 2. Split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.2, random_state=42  # 80% train, 20% test
)
print(f"Training: {X_train.shape}, Test: {X_test.shape}")

# 3. Instantiate model
model = KNeighborsClassifier(n_neighbors=5)

# 4. Train
model.fit(X_train, y_train)

# 5. Predict
y_pred = model.predict(X_test)

# 6. Evaluate
acc = accuracy_score(y_test, y_pred)
print(f"Accuracy: {acc:.2%}")
Output
Dataset shape: (150, 4)
Training: (120, 4), Test: (30, 4)
Accuracy: 100.00%
⚙️

2. Preprocessing — Prepare Data for Models

Most ML models require scaled, encoded, clean inputs. Scikit-learn makes this easy

Feature Scaling

Before scaling

Age: 20–60    Salary: ₹20,000–₹2,00,000

Salary numbers are much larger than age numbers.

After scaling

Both features are placed on comparable numerical scales.

This helps algorithms that are sensitive to distances or feature magnitude.

Python
from sklearn.preprocessing import StandardScaler, MinMaxScaler
import numpy as np

X = np.array([[1, 200], [2, 150], [3, 300], [4, 250]])

# StandardScaler: each feature is centred around 0 and scaled to unit variance
# Commonly useful for scale-sensitive models such as SVM, KNN and PCA
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
print("StandardScaler output:")
print(X_scaled.round(2))
print(f"Mean: {X_scaled.mean(axis=0).round(2)}")   # should be [0, 0]
print(f"Std:  {X_scaled.std(axis=0).round(2)}")    # should be [1, 1]
Output
StandardScaler output:
[[-1.34 -0.45]
 [-0.45 -1.34]
 [ 0.45  1.34]
 [ 1.34  0.45]]
Mean: [0. 0.]
Std:  [1. 1.]
Python
from sklearn.preprocessing import MinMaxScaler

# MinMaxScaler: maps each feature into the range [0, 1]
# Useful when you want features on the same bounded scale
minmax = MinMaxScaler()
X_minmax = minmax.fit_transform(X)
print("MinMaxScaler output (all values 0–1):")
print(X_minmax.round(2))
Output
MinMaxScaler output (all values 0–1):
[[0.   0.33]
 [0.33 0.  ]
 [0.67 1.  ]
 [1.   0.67]]

Encoding Categorical Features

Important: LabelEncoder is intended for the target y (the answer you want to predict), not for ordinary input columns in X. For nominal input features such as city names, use OneHotEncoder or a suitable alternative.
Python
from sklearn.preprocessing import LabelEncoder

# LabelEncoder is useful for target labels (y)
species = ['setosa', 'versicolor', 'virginica', 'setosa']

le = LabelEncoder()
y_encoded = le.fit_transform(species)
print("Encoded target:", y_encoded)
print("Classes:", le.classes_)
print("Decode:", le.inverse_transform([0, 2]))
Output
Encoded target: [0 1 2 0]
Classes: ['setosa' 'versicolor' 'virginica']
Decode: ['setosa' 'virginica']
Python
# One-Hot Encoding — creates binary columns for nominal input features
from sklearn.preprocessing import OneHotEncoder
import numpy as np

cities = ['Delhi', 'Mumbai', 'Pune', 'Delhi', 'Mumbai']
cities_2d = np.array(cities).reshape(-1, 1)

ohe = OneHotEncoder(sparse_output=False)
X_ohe = ohe.fit_transform(cities_2d)
print("One-Hot Encoded:")
print(X_ohe)
print("Feature names:", ohe.get_feature_names_out(['City']))
Output
One-Hot Encoded:
[[1. 0. 0.]
 [0. 1. 0.]
 [0. 0. 1.]
 [1. 0. 0.]
 [0. 1. 0.]]
Feature names: ['City_Delhi' 'City_Mumbai' 'City_Pune']

Handling Missing Values

Python
from sklearn.impute import SimpleImputer
import numpy as np

X = np.array([[1, 2], [np.nan, 4], [7, np.nan], [4, 3]])

# Mean imputation for numerical columns
imputer = SimpleImputer(strategy='mean')   # or 'median', 'most_frequent'
X_filled = imputer.fit_transform(X)
print("After imputation:")
print(X_filled)
Output
After imputation:
[[1.   2.  ]
 [4.   4.  ]   ← NaN replaced with column mean (1+7+4)/3 = 4
 [7.   3.  ]   ← NaN replaced with column mean (2+4+3)/3 = 3
 [4.   3.  ]]
🧠

3. Common Algorithms — Same API, Different Models

The beauty of scikit-learn: swap one algorithm for another by changing just one line

Python
from sklearn.linear_model    import LinearRegression, LogisticRegression, Ridge
from sklearn.tree            import DecisionTreeClassifier
from sklearn.ensemble        import RandomForestClassifier, GradientBoostingClassifier
from sklearn.svm             import SVC
from sklearn.neighbors       import KNeighborsClassifier
from sklearn.naive_bayes     import GaussianNB
from sklearn.datasets        import load_breast_cancer
from sklearn.model_selection import train_test_split
from sklearn.preprocessing   import StandardScaler

# Load dataset
X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

scaler = StandardScaler()
X_train_s = scaler.fit_transform(X_train)
X_test_s  = scaler.transform(X_test)   # ← NEVER fit on test data!

# Try multiple classifiers — identical API for all!
classifiers = {
    'Logistic Regression': LogisticRegression(max_iter=1000),
    'Decision Tree':       DecisionTreeClassifier(max_depth=5, random_state=42),
    'Random Forest':       RandomForestClassifier(n_estimators=100, random_state=42),
    'SVM':                 SVC(kernel='rbf'),
    'KNN':                 KNeighborsClassifier(n_neighbors=5),
    'Naive Bayes':         GaussianNB(),
    'Gradient Boosting':   GradientBoostingClassifier(n_estimators=100, random_state=42),
}

print(f"{'Algorithm':<25} {'Accuracy':>10}")
print("-" * 37)
for name, clf in classifiers.items():
    clf.fit(X_train_s, y_train)
    acc = clf.score(X_test_s, y_test)
    print(f"{name:<25} {acc:>10.2%}")
Output
Algorithm                 Accuracy
-------------------------------------
Logistic Regression          97.37%
Decision Tree                94.74%
Random Forest                96.49%
SVM                          98.25%
KNN                          94.74%
Naive Bayes                  96.49%
Gradient Boosting            95.61%

# Example output; exact scores can vary slightly across library versions.
Scaling note: the example uses one scaled dataset so several algorithms can be compared with the same input. Scaling is especially important for models such as SVM, KNN and many linear models, while tree-based models such as Decision Trees and Random Forests generally do not require feature scaling.
📊

4. Model Evaluation — Going Beyond Accuracy

Accuracy alone can be misleading. Use the right metric for your problem

Python
from sklearn.metrics import (
    accuracy_score, precision_score, recall_score, f1_score,
    confusion_matrix, classification_report, roc_auc_score
)
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer
from sklearn.model_selection import train_test_split

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

clf = RandomForestClassifier(n_estimators=100, random_state=42)
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)

print(f"Accuracy:  {accuracy_score(y_test, y_pred):.4f}")
print(f"Precision: {precision_score(y_test, y_pred):.4f}")
print(f"Recall:    {recall_score(y_test, y_pred):.4f}")
print(f"F1-Score:  {f1_score(y_test, y_pred):.4f}")
print(f"ROC-AUC:   {roc_auc_score(y_test, clf.predict_proba(X_test)[:,1]):.4f}")

print("\nConfusion Matrix:")
print(confusion_matrix(y_test, y_pred))

print("\nFull Classification Report:")
print(classification_report(y_test, y_pred, target_names=['Malignant','Benign']))
Output
Accuracy:  0.9649
Precision: 0.9589
Recall:    0.9859
F1-Score:  0.9722
ROC-AUC:   0.9953

Confusion Matrix:
[[40  3]
 [ 1 70]]

Full Classification Report:
              precision    recall  f1-score   support
   Malignant       0.98      0.93      0.95        43
      Benign       0.96      0.99      0.97        71
    accuracy                           0.96       114
   macro avg       0.97      0.96      0.96       114
weighted avg       0.97      0.96      0.96       114

# Example output; exact values can vary slightly across versions.
🔄

5. Cross-Validation — Reliable Model Evaluation

A single train/test split can be lucky or unlucky. Cross-validation repeats the evaluation on different folds

5-fold cross-validation — one simple picture

TEST
TRAIN
TRAIN
TRAIN
TRAIN
TRAIN
TEST
TRAIN
TRAIN
TRAIN
TRAIN
TRAIN
TEST
TRAIN
TRAIN
TRAIN
TRAIN
TRAIN
TEST
TRAIN
TRAIN
TRAIN
TRAIN
TRAIN
TEST

Each fold gets one turn as the test fold. The five scores are then summarized.

Python
from sklearn.model_selection import cross_val_score, StratifiedKFold
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_iris
import numpy as np

X, y = load_iris(return_X_y=True)
clf = RandomForestClassifier(n_estimators=100, random_state=42)

# 5-fold cross validation
cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(clf, X, y, cv=cv, scoring='accuracy')

print("Fold scores:", scores.round(4))
print(f"Mean:  {scores.mean():.4f}")
print(f"Std:   {scores.std():.4f}  ← lower = more consistent")
print(f"Rough spread (mean ± 2×std): {scores.mean():.2%} ± {2*scores.std():.2%}")
Output
Fold scores: [0.9667 0.9667 0.9333 0.9667 0.9000]
Mean:  0.9467
Std:   0.0267  ← lower means the fold scores vary less
Rough spread (mean ± 2×std): 94.67% ± 5.33%

# This is a simple spread summary, not a formal confidence interval.
🎛️

6. Hyperparameter Tuning with GridSearchCV

Automatically find the best hyperparameter combination using cross-validation

Python
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)

# Define the search grid
param_grid = {
    'n_estimators': [50, 100, 200],
    'max_depth':    [None, 5, 10],
    'min_samples_split': [2, 5],
}

rf = RandomForestClassifier(random_state=42)

# GridSearchCV tries every combination with 5-fold CV
# 3 × 3 × 2 = 18 combinations × 5 folds = 90 model fits
grid_search = GridSearchCV(
    rf, param_grid, cv=5, scoring='accuracy',
    n_jobs=-1, verbose=1
)
grid_search.fit(X, y)

print("Best parameters:", grid_search.best_params_)
print(f"Best CV accuracy: {grid_search.best_score_:.4f}")

# Use the best model directly
best_model = grid_search.best_estimator_
Output
Fitting 5 folds for each of 18 candidates, totalling 90 fits
Best parameters: {...}
Best CV accuracy: 0.96...

# The exact winner can vary with scikit-learn version and CV details.
# Always trust grid_search.best_params_ from your own run.
🧩

7. ColumnTransformer — Treat Different Columns Differently

Scale numeric columns and encode categorical columns in one clean preprocessing step

Imagine this small dataset

AgeSalaryCity
2550000Delhi
Age + Salary
StandardScaler
City
OneHotEncoder
Combined
model-ready features
Python
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder

preprocessor = ColumnTransformer([
    ('numbers', StandardScaler(), ['Age', 'Salary']),
    ('city', OneHotEncoder(handle_unknown='ignore'), ['City']),
])

# fit_transform() learns the preprocessing rules and transforms the data
X_ready = preprocessor.fit_transform(df)
🔗

8. Pipeline — Chain Preprocessing + Model Together

Pipelines keep preprocessing and modelling together and help prevent data leakage

Raw Data
→
Impute
fill missing values
→
Scale
→
Model
→
Prediction
Python
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.datasets import load_breast_cancer

X, y = load_breast_cancer(return_X_y=True)
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# Define pipeline steps: preprocessing → model
pipeline = Pipeline([
    ('imputer', SimpleImputer(strategy='median')),  # Step 1: Fill NaN
    ('scaler',  StandardScaler()),                   # Step 2: Scale
    ('model',   RandomForestClassifier(n_estimators=100, random_state=42)),  # Step 3
])

# fit() applies all steps in sequence to training data only
pipeline.fit(X_train, y_train)

# predict() applies preprocessing then prediction automatically
print(f"Test accuracy: {pipeline.score(X_test, y_test):.4f}")

# Cross-validate the entire pipeline (no leakage!)
cv_scores = cross_val_score(pipeline, X, y, cv=5)
print(f"CV accuracy: {cv_scores.mean():.4f} ± {cv_scores.std():.4f}")
Output
Test accuracy: 0.9649
CV accuracy: 0.9561 ± 0.0228

# Example output; exact values can vary slightly across versions.

🎯 Scikit-Learn Quick Reference

Data Splitting

Train/test splittrain_test_split(X, y, test_size=0.2)
K-Fold CVcross_val_score(model, X, y, cv=5)

Preprocessing

StandardiseStandardScaler().fit_transform(X)
NormaliseMinMaxScaler().fit_transform(X)
Encode target yLabelEncoder().fit_transform(y)
Fill missingSimpleImputer(strategy='mean').fit_transform(X)

Metrics

Accuracyaccuracy_score(y_true, y_pred)
F1f1_score(y_true, y_pred)
Confusion matrixconfusion_matrix(y_true, y_pred)
ROC-AUCroc_auc_score(y_true, y_prob)

Tuning

Grid searchGridSearchCV(model, param_grid, cv=5)
Random searchRandomizedSearchCV(model, param_dist, n_iter=20)
PipelinePipeline([('scaler', ...), ('model', ...)])

Common Scikit-learn Questions

What is scikit-learn mainly used for?

It is commonly used for classical machine-learning tasks such as preprocessing, regression, classification, clustering, model evaluation and hyperparameter tuning.

What is the difference between fit() and predict()?

fit() learns from training data. predict() uses the learned model to produce answers for new data.

Why should preprocessing be fitted only on training data?

If information from the test data is used while learning preprocessing rules, the evaluation can become unrealistically optimistic. This is called data leakage.

When should I use a Pipeline?

Use a Pipeline when preprocessing and modelling should always run together. It keeps the workflow reproducible and is especially useful with cross-validation and tuning.

Continue learning: Review Train/Test Split, Feature Scaling, Cross-Validation, and Hyperparameter Tuning when you reach those topics in the curriculum.

Apply this concept in a real hands-on project

You have studied the idea. Now use it in working Python code, see the calculation and output, and explain what changed. Each project includes step-by-step instructions, full source and tests.

Practice challenge: predict an outcome first, run the code, and explain the difference.

Track Your Progress0% curriculum done

Finished "Scikit-learn Essentials"?

Mark this lesson as completed to update your progress bar and track your milestones across the full curriculum.

Overall Progress0 / 167 lessons

Was this tutorial helpful?

Your feedback helps us continuously refine and improve our open-access tutorials.

Summary / Key Takeaways

  • Scikit-learn estimators share fit, predict, transform, and score conventions.
  • Split data before learning any preprocessing parameters.
  • Pipeline chains preprocessing and modelling without leakage.
  • ColumnTransformer applies different transformations to different column groups.
  • Cross-validation and search must wrap the complete pipeline.
  • Save the fitted pipeline so inference reproduces training behavior.

Knowledge Check

Progress: 0/4

Test your active recall. Choose one answer for each question, then check it to reveal the step-by-step reasoning.

Question 1 of 4
What is the primary purpose of scikit-learn's Pipeline object?
Question 2 of 4
Why do you call fit_transform() on X_train but only transform() on X_test?
Question 3 of 4
cross_val_score(model, X, y, cv=5, scoring='accuracy') returns what?
Question 4 of 4
What does setting random_state=42 do in a sklearn model or data split?

Knowledge Check Review & Explanations

Review the questions, verified correct answers, and conceptual explanations for this lesson.

1What is the primary purpose of scikit-learn's Pipeline object?▼

Correct Answer: To chain preprocessing steps and a model into a single object — ensuring that transformers are fitted only on training data during cross-validation, preventing data leakage automatically

Without Pipeline, it's very easy to accidentally call fit_transform() on the full dataset before splitting, or to apply different transforms to train and test. Pipeline prevents this by ensuring fit() only ever touches training data.

2Why do you call fit_transform() on X_train but only transform() on X_test?▼

Correct Answer: fit() learns parameters (e.g., mean and std for StandardScaler) from the data it sees. If you fit on test data, those test statistics influence your preprocessing — a form of leakage. Fit once on train, then apply (transform) to both

Recomputing the scaler's mean and std from test data means your preprocessing parameters 'know' about the test set. The model has indirectly seen test data before evaluation — optimistically biasing your results.

3cross_val_score(model, X, y, cv=5, scoring='accuracy') returns what?▼

Correct Answer: An array of 5 floats — one accuracy score per fold — showing how performance varies across different train/test splits of the data

Each of the 5 folds is a different train/test split. The returned array lets you compute mean and std, which is far more informative than a single split estimate.

4What does setting random_state=42 do in a sklearn model or data split?▼

Correct Answer: It seeds the random number generator so that any randomness in the algorithm (shuffling, initialisation) produces the same result every run — making experiments reproducible

Without a fixed random_state, the same code run twice gives different results, making debugging and comparing experiments unreliable. Always set random_state in any code you share or publish.

Keep moving through the path

Continue Learning

Free Download: ML Interview Cheatsheet

100 essential Machine Learning interview questions and answers covering foundations, algorithms, evaluation, deep learning, Python, system design and interview strategy. Enter your email to download it instantly.

Optional Learning Resources

These resources may complement the lesson. They are optional and are not required to use ML Academy.

As an Amazon Associate I earn from qualifying purchases. Other links are identified separately when they are not affiliate links.

Next