Module 6

Data Analysis & Visualization

Cleaning, transforming, and visualizing data — the practical skills between raw data and a trained model.

20 lessonsAI & MLHarinIT Academy
Module 6 · Lesson 6.1

NumPy Advanced

NumPy (Numerical Python) is the fundamental numerical-computing library in the Python data-analysis ecosystem. While basic NumPy focuses on creating arrays and performing simple operations, Advanced NumPy focuses on efficient computation, multidimensional data manipulation, broadcasting, vectorization, memory management, linear algebra, and statistical operations.

6.1.1Learning Objectives

By the end of this topic, you should be able to

  • Work confidently with multidimensional NumPy arrays.
  • Understand NumPy's memory model and array dimensions.
  • Use broadcasting effectively.
  • Perform vectorized calculations without Python loops.
  • Apply advanced indexing and Boolean filtering.
  • Reshape, transpose, split, stack, and combine arrays.
  • Perform statistical calculations efficiently.
  • Work with matrices and linear algebra.
  • Generate random data for analysis and simulations.
  • Understand views versus copies.
  • Optimize numerical Python code.

6.1.2NumPy Array Fundamentals

A NumPy array is an object of type numpy.ndarray.

import numpy as np
data = np.array([10, 20, 30, 40, 50])
print(data)
print(type(data))
print(data.ndim)
print(data.shape)
print(data.size)
print(data.dtype)

Output

\[10 20 30 40 50\]

<class 'numpy.ndarray'>

1

(5,)

5

int64

Important properties

PropertyMeaning
ndimNumber of dimensions
shapeSize of each dimension
sizeTotal number of elements
dtypeData type
itemsizeBytes occupied by one element
nbytesTotal memory occupied

Example

data = np.array([
    [10, 20, 30],
\[40, 50, 60\]

])

print(data.ndim)
print(data.shape)
print(data.size)
print(data.dtype)
print(data.nbytes)

The shape is

(2, 3)

This means

  • 2 rows
  • 3 columns
  • 6 total elements

6.1.3Multidimensional Arrays

NumPy supports arrays with multiple dimensions.

1D

a = np.array([10, 20, 30])

Shape

(3,)

2D

b = np.array([
    [10, 20, 30],
\[40, 50, 60\]

])

Shape

(2, 3)

3D

c = np.array([
    [
        [1, 2],
\[3, 4\]

],

[

[5, 6],

\[7, 8\]

]

])

Shape

(2, 2, 2)

You can think of a 3D array as a collection of 2D tables.

6.1.4Array Data Types

NumPy supports specialized numerical data types.

a = np.array([1, 2, 3], dtype=np.int32)
print(a.dtype)

Common types include

  • int8
  • int16
  • int32
  • int64
  • float32
  • float64
  • bool
  • complex64
  • complex128

You can convert an array using astype()

prices = np.array([100, 200, 300])
prices_float = prices.astype(float)
print(prices_float)
print(prices_float.dtype)

Why is this important?

Data type affects

  • memory usage
  • computation speed
  • precision
  • compatibility with other libraries

For example

data = np.array([1, 2, 3, 4], dtype=np.int8)

uses significantly less memory than

data = np.array([1, 2, 3, 4], dtype=np.int64)

This becomes important when processing millions of records.

6.1.5Advanced Array Creation

NumPy provides several methods for creating arrays.

arange()

numbers = np.arange(1, 11)
print(numbers)

Output

\[1 2 3 4 5 6 7 8 9 10\]

With a step

numbers = np.arange(0, 20, 2)
print(numbers)

Output

\[0 2 4 6 8 10 12 14 16 18\]

linspace()

linspace() generates a specified number of equally spaced values.

numbers = np.linspace(0, 100, 11)
print(numbers)

Output

\[0. 10. 20. 30. 40. 50. 60. 70. 80. 90. 100.\]

This is especially useful for mathematical calculations and plotting.

zeros()

a = np.zeros((3, 4))
print(a)

Creates a 3 × 4 array containing zeros.

ones()

a = np.ones((2, 3))
print(a)

full()

a = np.full((3, 3), 100)
print(a)

Output

\[[100 100 100\]
\[100 100 100\]
\[100 100 100]\]

Identity Matrix

identity = np.eye(4)
print(identity)

Output

\[[1. 0. 0. 0.\]
\[0. 1. 0. 0.\]
\[0. 0. 1. 0.\]
\[0. 0. 0. 1.]\]

6.1.6Advanced Indexing

Basic indexing

data = np.array([10, 20, 30, 40, 50])
print(data[0])
print(data[-1])

Output

  • 10
  • 50
  • Slicing
print(data[1:4])

Output

\[20 30 40\]

You can also use a step

print(data[::2])

Output

\[10 30 50\]

6.1.72D Array Indexing

data = np.array([
    [10, 20, 30],
    [40, 50, 60],
\[70, 80, 90\]

])

Access a specific element

print(data[1, 2])

Output

  • 60
  • The first number represents the row and the second represents the column.
  • data[row, column]
  • Selecting a row
print(data[1])

Output

\[40 50 60\]

Selecting a column

print(data[:, 1])

Output

\[20 50 80\]

The : means "all rows".

6.1.8Boolean Indexing

Boolean indexing is one of the most useful NumPy techniques in data analysis.

sales = np.array([100, 250, 80, 450, 300])
high_sales = sales[sales > 200]
print(high_sales)

Output

\[250 450 300\]

You can create the Boolean condition separately

condition = sales > 200
print(condition)

Output

\[False True False True True\]

Then

print(sales[condition])

Multiple Conditions

Use & for AND

sales = np.array([100, 250, 80, 450, 300])
result = sales[(sales > 200) & (sales < 400)]
print(result)

Output

\[250 300\]

Use | for OR

result = sales[(sales < 100) | (sales > 400)]
print(result)

Output

\[80 450\]

6.1.9Fancy Indexing

Fancy indexing allows you to select specific positions.

data = np.array([10, 20, 30, 40, 50])
indexes = [0, 2, 4]
print(data[indexes])

Output

\[10 30 50\]

For a 2D array

data = np.array([
    [10, 20, 30],
    [40, 50, 60],
\[70, 80, 90\]

])

rows = [0, 2]
cols = [1, 2]
print(data[rows, cols])

Output

\[20 90\]

6.1.10Broadcasting

Broadcasting allows NumPy to perform operations between arrays with compatible shapes.

Consider

prices = np.array([100, 200, 300])
discount = 10
result = prices - discount
print(result)

Output

\[90 190 290\]

NumPy automatically applies 10 to every element.

This is broadcasting.

Broadcasting with 2D Arrays

sales = np.array([
    [100, 200, 300],
\[400, 500, 600\]

])

tax = np.array([10, 20, 30])
result = sales + tax
print(result)

Output

\[[110 220 330\]
\[410 520 630]\]

The tax array is effectively applied to every row.

6.1.11Broadcasting Rules

When NumPy performs operations between arrays, dimensions are compared from right to left.

Two dimensions are compatible when

They are equal, or

One of them is 1.

For example

Array A: (3, 4)

Array B: (4,)

These are compatible.

Another example

Array A: (3, 1)

Array B: (1, 4)

These are also compatible.

The result becomes

(3, 4)

Understanding broadcasting is extremely important for efficient numerical programming.

6.1.12Vectorization

Vectorization means performing operations on entire arrays instead of processing elements individually using Python loops.

Traditional Python

numbers = [1, 2, 3, 4, 5]
result = []
for number in numbers:
    result.append(number * 2)
print(result)

NumPy

numbers = np.array([1, 2, 3, 4, 5])
result = numbers * 2
print(result)

Output

\[2 4 6 8 10\]

NumPy's approach is generally faster and more concise for numerical operations.

6.1.13Universal Functions — ufuncs

NumPy provides optimized functions called universal functions, or ufuncs.

Examples

numbers = np.array([1, 4, 9, 16])
print(np.sqrt(numbers))
print(np.square(numbers))
print(np.exp(numbers))
print(np.log(numbers))

Other useful functions include

np.abs()
np.sin()
np.cos()
np.tan()
np.ceil()
np.floor()
np.round()

Example

prices = np.array([100.4, 200.6, 300.2])
print(np.round(prices))

Output

\[100. 201. 300.\]

6.1.14Aggregation Functions

NumPy provides efficient statistical functions.

sales = np.array([100, 200, 300, 400, 500])
print(np.sum(sales))
print(np.mean(sales))
print(np.median(sales))
print(np.min(sales))
print(np.max(sales))
print(np.std(sales))
print(np.var(sales))

Common functions

FunctionPurpose
np.sum()Total
np.mean()Average
np.median()Median
np.min()Minimum
np.max()Maximum
np.std()Standard deviation
np.var()Variance
np.percentile()Percentile

6.1.15Axis Operations

Consider

sales = np.array([
    [100, 200, 300],
\[400, 500, 600\]

])

Sum of all values

print(np.sum(sales))

Output

2100

Sum by column

print(np.sum(sales, axis=0))

Output

\[500 700 900\]

Sum by row

print(np.sum(sales, axis=1))

Output

\[600 1500\]

A useful way to remember

axis=0 → operate down rows → result for each column
axis=1 → operate across columns → result for each row

6.1.16Reshaping Arrays

Suppose

data = np.arange(1, 13)
print(data)

Output

\[1 2 3 4 5 6 7 8 9 10 11 12\]

Convert it into a 3 × 4 matrix

matrix = data.reshape(3, 4)
print(matrix)

Output

\[[ 1 2 3 4\]
\[5 6 7 8\]
\[9 10 11 12]\]

You can use -1 when NumPy should calculate one dimension

matrix = data.reshape(3, -1)
print(matrix)

6.1.17Flattening Arrays

Convert a multidimensional array into one dimension.

data = np.array([
    [1, 2, 3],
\[4, 5, 6\]

])

flat = data.flatten()
print(flat)

Output

\[1 2 3 4 5 6\]

Another option

flat = data.ravel()

There is an important difference

flatten() generally returns a copy.

ravel() attempts to return a view where possible.

6.1.18Transpose

Transpose converts rows into columns.

data = np.array([
    [1, 2, 3],
\[4, 5, 6\]

])

print(data.T)

Output

\[[1 4\]
\[2 5\]
\[3 6]\]

You can also use

np.transpose(data)

6.1.19Stacking Arrays

Vertical stacking

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.vstack((a, b))
print(result)

Output

\[[1 2 3\]
\[4 5 6]\]

Horizontal stacking

result = np.hstack((a, b))
print(result)

Output

\[1 2 3 4 5 6\]

For multidimensional arrays

a = np.array([[1, 2], [3, 4]])
b = np.array([[5, 6], [7, 8]])
result = np.vstack((a, b))
print(result)

6.1.20Concatenation

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
result = np.concatenate((a, b))
print(result)

Output

\[1 2 3 4 5 6\]

For 2D arrays

a = np.array([
    [1, 2],
\[3, 4\]

])

b = np.array([
    [5, 6],
\[7, 8\]

])

result = np.concatenate((a, b), axis=0)
print(result)

6.1.21Splitting Arrays

data = np.arange(1, 10)
parts = np.split(data, 3)
for part in parts:
print(part)

Output

\[1 2 3\]
\[4 5 6\]
\[7 8 9\]

Other functions

np.hsplit()
np.vsplit()
np.array_split()

array_split() is useful when the array cannot be divided equally.

6.1.22Sorting

data = np.array([50, 10, 40, 20, 30])
print(np.sort(data))

Output

\[10 20 30 40 50\]

To get the indexes that would sort the array

indexes = np.argsort(data)
print(indexes)

This is especially useful when working with related arrays.

6.1.23Searching

Find the index of the maximum value

sales = np.array([100, 500, 200, 800, 300])
print(np.argmax(sales))

Output

3

Find the minimum

print(np.argmin(sales))

Find all values satisfying a condition

indexes = np.where(sales > 300)
print(indexes)

Output

(array([1, 3]),)

6.1.24np.where()

np.where() is extremely useful for conditional transformations.
sales = np.array([100, 500, 200, 800, 300])
status = np.where(sales >= 500, "High", "Low")
print(status)

Output

\['Low' 'High' 'Low' 'High' 'Low'\]

This is similar to a SQL CASE WHEN.

For example

CASE

  • WHEN sales >= 500 THEN 'High'
  • ELSE 'Low'
  • END

This makes NumPy particularly useful for people working with SQL and analytics.

6.1.25Unique Values

customers = np.array([
    "A", "B", "A", "C", "B", "D"
])
print(np.unique(customers))

Output

\['A' 'B' 'C' 'D'\]

You can also get counts

values, counts = np.unique(

customers,

return_counts=True

)

print(values)
print(counts)

Output

\['A' 'B' 'C' 'D'\]
\[2 2 1 1\]

6.1.26Views vs Copies

This is an important advanced NumPy concept.

Consider

data = np.array([10, 20, 30, 40])
view = data[1:3]

view[0] = 999

print(data)

The original array may now contain

\[10 999 30 40\]

because slicing generally produces a view into the original array.

To create an independent copy

copy_data = data[1:3].copy()

copy_data[0] = 1000

Now changes to copy_data do not modify the original array.

Key distinction

View → shares underlying data

Copy → independent data

This is important when working with large datasets because unnecessary copies can consume significant memory.

6.1.27Memory Layout

NumPy arrays store data efficiently in memory.

You can inspect memory information

data = np.array([1, 2, 3, 4, 5])
print(data.itemsize)
print(data.nbytes)

For example, if each integer occupies 8 bytes

itemsize = 8
nbytes = 40

For large-scale analytics, memory-efficient data types can make a significant difference.

6.1.28Linear Algebra

NumPy provides a dedicated linear algebra module

np.linalg

Matrix multiplication

A = np.array([
    [1, 2],
\[3, 4\]

])

B = np.array([
    [5, 6],
\[7, 8\]

])

result = A @ B
print(result)

Output

\[[19 22\]
\[43 50]\]

You can also use

np.matmul(A, B)

Matrix inverse

A = np.array([
    [1, 2],
\[3, 4\]

])

inverse = np.linalg.inv(A)
print(inverse)

Determinant

det = np.linalg.det(A)
print(det)

Eigenvalues

values, vectors = np.linalg.eig(A)

print(values)
print(vectors)

Linear algebra is particularly important in machine learning, statistics, optimization, and scientific computing.

6.1.29Random Number Generation

NumPy provides a modern random-number API.

rng = np.random.default_rng(42)
numbers = rng.integers(1, 101, size=10)
print(numbers)

The 42 is a seed, which makes the result reproducible.

Random floating-point numbers

numbers = rng.random(5)
print(numbers)

Normal distribution

values = rng.normal(
    loc=100,
    scale=15,
    size=1000
)

Here

loc = mean
scale = standard deviation
size = number of observations

6.1.30Statistical Analysis

Consider a sales dataset

sales = np.array([
    1200, 1500, 1800, 1100,
    2200, 2500, 1700, 1900
])

Calculate statistics

print("Mean:", np.mean(sales))
print("Median:", np.median(sales))
print("Minimum:", np.min(sales))
print("Maximum:", np.max(sales))
print("Standard Deviation:", np.std(sales))

Percentiles

print(np.percentile(sales, 25))
print(np.percentile(sales, 50))
print(np.percentile(sales, 75))

These statistics are frequently used during Exploratory Data Analysis (EDA).

6.1.31Handling NaN Values

NumPy also provides functions specifically for missing numerical values represented as NaN.

data = np.array([
    10,
    20,
    np.nan,
    40,
    50
])

Regular mean

print(np.mean(data))

This produces nan.

Instead

print(np.nanmean(data))

Output

30.0

Other useful functions

np.nansum()
np.nanmean()
np.nanmedian()
np.nanmin()
np.nanmax()
np.nanstd()

6.1.32Practical Example — Sales Analysis

Suppose a company has monthly sales

sales = np.array([
    [12000, 15000, 14000, 16000],
    [18000, 21000, 19000, 22000],
\[15000, 17000, 16000, 18000\]

])

Each row represents a region and each column represents a month.

Total sales

total_sales = np.sum(sales)
print(total_sales)

Sales by region

region_sales = np.sum(sales, axis=1)
print(region_sales)

Sales by month

monthly_sales = np.sum(sales, axis=0)
print(monthly_sales)

Average monthly sales

average_sales = np.mean(sales, axis=0)
print(average_sales)

Highest-performing region

best_region = np.argmax(region_sales)
print(best_region)

This demonstrates how NumPy can perform large-scale calculations without writing explicit loops.

6.1.33NumPy vs Python Lists

Consider

numbers = [1, 2, 3, 4, 5]

Python lists are general-purpose containers.

NumPy arrays are optimized for numerical computation.

FeaturePython ListNumPy Array
Numerical operationsLimitedExcellent
VectorizationNoYes
BroadcastingNoYes
Multidimensional dataLimitedExcellent
Mathematical functionsManualBuilt-in
Memory efficiencyLowerHigher
Numerical performanceLowerHigher
Linear algebraNot built-inBuilt-in

6.1.34NumPy and SQL Comparison

Since data analysts frequently work with SQL, several NumPy concepts can be related to SQL.

SQLNumPy
WHERE sales > 500sales[sales > 500]
CASE WHENnp.where()
SUM()np.sum()
AVG()np.mean()
MIN()np.min()
MAX()np.max()
ORDER BYnp.sort()
Position of maximumnp.argmax()
Distinct valuesnp.unique()

For example, SQL

SELECT *
FROM sales

WHERE amount > 500;

Equivalent NumPy filtering

result = sales[sales > 500]

This comparison helps bridge the gap between SQL-based analytics and Python-based analytics.

6.1.35Performance: Loop vs Vectorization

Consider

numbers = np.arange(1_000_000)

A Python loop

result = []
for number in numbers:
    result.append(number * 2)

Vectorized NumPy

result = numbers * 2

The second approach is generally preferable because the operation is performed using NumPy's optimized numerical machinery.

General principle

Instead of

for row in data:
    calculate(row)

look for opportunities to perform

result = operation(data)

This is one of the most important habits in effective NumPy programming.

6.1.36Common NumPy Mistakes

Mistake 1 — Using and instead of &

Incorrect

data[(data > 10) and (data < 50)]

Correct

data[(data > 10) & (data < 50)]

Mistake 2 — Forgetting parentheses

Incorrect

data > 10 & data < 50

Correct

(data > 10) & (data < 50)

Mistake 3 — Confusing shape

a = np.array([1, 2, 3])
print(a.shape)

Output

(3,)

This is not

(1, 3)

If you need a row vector

a.reshape(1, 3)

Mistake 4 — Unexpected modification through views

subset = data[1:4]

Remember that slicing can create a view.

Use

subset = data[1:4].copy()

when an independent array is required.

6.1.37Mini Project — Regional Sales Analysis

Dataset

import numpy as np
sales = np.array([
    [12000, 15000, 14000, 16000, 18000, 19000],
    [18000, 21000, 19000, 22000, 23000, 25000],
    [10000, 12000, 13000, 14000, 15000, 16000],
\[22000, 24000, 25000, 27000, 28000, 30000\]

])

Assume

Rows → Regions

  • Columns → Months
  • Tasks
  • Task 1: Calculate total company sales.
total_sales = np.sum(sales)

Task 2: Calculate total sales for each region.

region_sales = np.sum(sales, axis=1)

Task 3: Calculate sales for each month.

monthly_sales = np.sum(sales, axis=0)

Task 4: Find the highest-performing region.

best_region = np.argmax(region_sales)

Task 5: Find the month with the highest sales.

best_month = np.argmax(monthly_sales)

Task 6: Calculate average sales.

average_sales = np.mean(sales)

Task 7: Find all sales values greater than ₹20,000.

high_sales = sales[sales > 20000]

6.1.38Interview Questions

  • Beginner/Intermediate
  • What is NumPy?
  • What is an ndarray?
  • What is the difference between shape, size, and ndim?
  • What is vectorization?
  • What is broadcasting?
  • What is Boolean indexing?
  • What is fancy indexing?
  • What is the difference between reshape() and resize()?
  • What is the difference between flatten() and ravel()?
  • What does axis=0 mean?

Advanced

  • What are NumPy views and copies?
  • How does broadcasting work?
  • Why is NumPy faster than Python loops?
  • What are ufuncs?
  • What is the difference between np.array() and Python lists?
  • What is np.where()?
  • What does np.argsort() return?
  • How do you handle NaN values in NumPy?
  • How can you reduce NumPy memory consumption?
  • What is the difference between @, np.dot(), and np.matmul()?
  • How does NumPy store multidimensional arrays?
  • What are strides?
  • How does NumPy avoid unnecessary data copies?
  • How would you optimize a NumPy calculation involving millions of records?

Explain broadcasting with an example.

6.1.39Key Takeaways

The most important concepts to master are

ndarray
Dimensions & Shape
Indexing & Slicing
Boolean/Fancy Indexing
Broadcasting
Vectorization
Aggregation & Axis
Reshaping
Stacking & Concatenation
Sorting & Searching
Views vs Copies
Statistics
Linear Algebra
Random Number Generation
Performance Optimization

For data analysis, the five concepts you should become especially strong at are Boolean indexing, broadcasting, vectorization, axis-based aggregation, and views vs copies. These form the foundation for advanced Pandas and efficient Python data-processing workflows.

Module 6 · Lesson 6.2

Pandas Advanced

Pandas is one of the most important Python libraries for data analysis. While basic Pandas teaches you how to create DataFrames, select columns, filter rows, and calculate simple statistics, Advanced Pandas focuses on complex data manipulation, aggregation, joining datasets, reshaping, hierarchical indexing, time-series operations, window functions, and performance optimization.

6.2.1Learning Objectives

By the end of this topic, you should be able to

  • Work efficiently with large DataFrames.
  • Perform advanced filtering and indexing.
  • Use groupby() for complex aggregations.
  • Merge and join multiple datasets.
  • Reshape data using pivot(), pivot_table(), melt(), and stack().
  • Work with MultiIndex DataFrames.
  • Apply transformations using apply(), map(), and transform().
  • Perform rolling and expanding calculations.
  • Work with dates and time-series data.
  • Handle duplicate and inconsistent data.
  • Optimize DataFrame memory usage.
  • Build reusable data-processing pipelines.

6.2.2Pandas Data Structures

Pandas provides two primary data structures

Series

A one-dimensional labeled structure.

import pandas as pd
sales = pd.Series(
    [100, 200, 300],
    index=["Jan", "Feb", "Mar"]
)
print(sales)

DataFrame

A two-dimensional tabular structure.

data = {
    "Product": ["Laptop", "Phone", "Tablet"],
    "Sales": [50000, 30000, 20000]
}
df = pd.DataFrame(data)
print(df)

A DataFrame can be thought of as a table containing

  • rows
  • columns
  • indexes
  • values
  • data types

6.2.3Understanding DataFrame Metadata

Before performing analysis, inspect the DataFrame.

print(df.shape)
print(df.columns)
print(df.index)
print(df.dtypes)

Useful methods

df.head()
df.tail()
df.info()
df.describe()
df.memory_usage()

For example

print(df.info())

This helps identify

  • number of records
  • column names
  • missing values
  • data types
  • memory usage

6.2.4Advanced Selection

Suppose

df = pd.DataFrame({
    "Employee": ["A", "B", "C", "D"],
    "Department": ["IT", "HR", "IT", "Finance"],
    "Salary": [60000, 50000, 75000, 65000],
    "Experience": [3, 2, 6, 5]
})

Select multiple columns

result = df[["Employee", "Salary"]]

Select rows using loc

result = df.loc[0:2, ["Employee", "Salary"]]

Select rows using iloc

result = df.iloc[0:3, [0, 2]]

Remember

loc → label-based selection

iloc → position-based selection

6.2.5Advanced Filtering

Simple condition

result = df[df["Salary"] > 60000]

Multiple conditions

result = df[
    (df["Salary"] > 60000) &
    (df["Experience"] >= 5)
]

OR condition

result = df[
    (df["Department"] == "IT") |
    (df["Department"] == "Finance")
]

Using isin()

Instead of

  • df[
  • (df["Department"] == "IT") |
  • (df["Department"] == "Finance")

]

you can write

df[

df["Department"].isin(["IT", "Finance"])

]

This is cleaner when working with multiple values.

6.2.6Using query()

Pandas provides a SQL-like filtering syntax.

result = df.query("Salary > 60000")

Multiple conditions

result = df.query(
    "Salary > 60000 and Experience >= 5"
)

String values

result = df.query(
    "Department == 'IT'"
)

This can make complex filtering easier to read.

6.2.7Sorting Data

Sort by one column

df.sort_values("Salary")

Descending

df.sort_values(
    "Salary",
    ascending=False
)

Sort by multiple columns

df.sort_values(
    ["Department", "Salary"],
    ascending=[True, False]
)

Sort by index

df.sort_index()

6.2.8groupby() — The Most Important Advanced Concept

groupby() is one of the most powerful Pandas features.

Consider

sales = pd.DataFrame({
    "Region": [
        "South", "South", "North",
        "North", "West", "West"
    ],
    "Product": [
        "Laptop", "Phone", "Laptop",
        "Phone", "Laptop", "Phone"
    ],
    "Sales": [
        50000, 30000, 45000,
        35000, 60000, 40000
    ]
})

Calculate total sales by region

result = sales.groupby("Region")["Sales"].sum()
print(result)

This is conceptually similar to

  • SELECT
  • Region,
  • SUM(Sales)
  • FROM sales
  • GROUP BY Region;

6.2.9Multiple Aggregations

result = sales.groupby("Region")["Sales"].agg(
\["sum", "mean", "min", "max", "count"\]

)

print(result)

You can also give custom names

result = sales.groupby("Region").agg(
    Total_Sales=("Sales", "sum"),
    Average_Sales=("Sales", "mean"),
    Maximum_Sales=("Sales", "max")
)

This produces a clean analytical result.

6.2.10Grouping by Multiple Columns

result = sales.groupby(
\["Region", "Product"\]

)["Sales"].sum()

print(result)

This provides sales at

Region + Product

level.

For example

South Laptop

South Phone

North Laptop

North Phone

West Laptop

West Phone

This is equivalent to grouping by multiple dimensions in SQL.

6.2.11transform()

transform() is different from groupby().agg().

Suppose

sales = pd.DataFrame({
    "Region": ["South", "South", "North", "North"],
    "Sales": [100, 300, 200, 400]
})

Calculate regional average

  • sales["Region_Avg"] = (
  • sales.groupby("Region")["Sales"]
  • .transform("mean")

)

print(sales)

Result

Region Sales Region_Avg

0 South 100 200

1 South 300 200

2 North 200 300

3 North 400 300

The important point is that transform() returns a result aligned with the original rows.

This is extremely useful for

  • percentage of group total
  • group averages
  • normalization
  • ranking
  • comparison against group-level metrics

6.2.12Percentage of Group Total

  • sales["Region_Total"] = (
  • sales.groupby("Region")["Sales"]
  • .transform("sum")

)

  • sales["Percentage"] = (
  • sales["Sales"] /
  • sales["Region_Total"] * 100

)

This is a common business-analysis calculation.

6.2.13apply()

apply() allows you to execute a custom function.

def classify_salary(salary):
if salary >= 70000:
return "High"
elif salary >= 50000:
return "Medium"
else:
return "Low"

Apply it

df["Salary_Level"] = df["Salary"].apply(

classify_salary

)

You can also use a lambda

df["Salary_Level"] = df["Salary"].apply(

lambda x: "High" if x >= 70000 else "Low"

)

Important distinction

Use

  • vectorized operations when possible
  • map() for element-wise Series mapping
  • apply() when custom logic is necessary
  • transform() when the result must align with the original DataFrame

6.2.14map()

map() is commonly used with Series.

mapping = {
    "IT": "Technology",
    "HR": "Human Resources",
    "Finance": "Finance"
}

df["Department_Name"] = (

df["Department"].map(mapping)

)

This is useful for lookup-style transformations.

6.2.15Merging DataFrames

Suppose we have

customers = pd.DataFrame({
    "CustomerID": [1, 2, 3],
    "CustomerName": ["Alice", "Bob", "Charlie"]
})
orders = pd.DataFrame({
    "OrderID": [101, 102, 103, 104],
    "CustomerID": [1, 2, 1, 3],
    "Amount": [500, 700, 300, 900]
})

Merge

result = pd.merge(
    orders,
    customers,
    on="CustomerID"
)

This is equivalent to an SQL

INNER JOIN

6.2.16Types of Joins

Pandas supports

pd.merge(
    left,
    right,
    how="inner",
    on="CustomerID"
)

Available join types

  • inner
  • left
  • right
  • outer
  • cross
  • Inner join
  • Only matching records.
pd.merge(
    orders,
    customers,
    how="inner",
    on="CustomerID"
)

Left join

Keep all records from the left DataFrame.

pd.merge(
    orders,
    customers,
    how="left",
    on="CustomerID"
)

Outer join

Keep all records from both DataFrames.

pd.merge(
    orders,
    customers,
    how="outer",
    on="CustomerID"
)

6.2.17Joining on Different Column Names

Suppose

orders = pd.DataFrame({
    "Customer_ID": [1, 2, 3],
    "Amount": [500, 700, 900]
})
customers = pd.DataFrame({
    "ID": [1, 2, 3],
    "Name": ["Alice", "Bob", "Charlie"]
})

Use

result = pd.merge(
    orders,
    customers,
    left_on="Customer_ID",
    right_on="ID"
)

6.2.18Concatenation

concat() is useful for combining DataFrames vertically or horizontally.

df1 = pd.DataFrame({
    "Name": ["A", "B"],
    "Sales": [100, 200]
})
df2 = pd.DataFrame({
    "Name": ["C", "D"],
    "Sales": [300, 400]
})

Vertical concatenation

result = pd.concat(
    [df1, df2],
    ignore_index=True
)

Result

Name Sales

0 A 100

1 B 200

2 C 300

3 D 400

6.2.19Pivot Tables

A PivotTable summarizes data across multiple dimensions.

sales = pd.DataFrame({
    "Region": [
        "South", "South",
        "North", "North"
    ],
    "Product": [
        "Laptop", "Phone",
        "Laptop", "Phone"
    ],
    "Sales": [
        50000, 30000,
        45000, 35000
    ]
})

Create a pivot table

pivot = pd.pivot_table(
    sales,
    values="Sales",
    index="Region",
    columns="Product",
    aggfunc="sum"
)
print(pivot)

This is conceptually similar to an Excel PivotTable.

6.2.20pivot() vs pivot_table()

pivot()

Used when combinations of index and columns are unique.

df.pivot(
    index="Region",
    columns="Product",
    values="Sales"
)

pivot_table()

Can aggregate duplicate combinations.

df.pivot_table(
    index="Region",
    columns="Product",
    values="Sales",
    aggfunc="sum"
)

For business analytics, pivot_table() is often more flexible.

6.2.21Melting Data

melt() converts wide data into long format.

Suppose

df = pd.DataFrame({
    "Product": ["Laptop", "Phone"],
    "Jan": [100, 200],
    "Feb": [150, 250],
    "Mar": [200, 300]
})

Use

long_df = df.melt(
    id_vars="Product",
    var_name="Month",
    value_name="Sales"
)

Result

Product Month Sales

Laptop Jan 100

Laptop Feb 150

Laptop Mar 200

Phone Jan 200

Phone Feb 250

Phone Mar 300

This structure is especially useful for visualization.

6.2.22Stack and Unstack

These methods are commonly used with hierarchical indexes.

df = pd.DataFrame({
    "A": [10, 20],
    "B": [30, 40]
})

Stack

stacked = df.stack()

Unstack

unstacked = stacked.unstack()

They are useful when changing between different dimensional representations.

6.2.23MultiIndex

A MultiIndex allows multiple levels of indexing.

data = pd.DataFrame({
    "Region": ["South", "South", "North", "North"],
    "Product": ["Laptop", "Phone", "Laptop", "Phone"],
    "Sales": [50000, 30000, 45000, 35000]
})
multi = data.set_index(
\["Region", "Product"\]

)

print(multi)

The resulting index contains

Region

Product

This is useful for hierarchical analytical data.

6.2.24Resetting Index

To convert the index back into normal columns

df = multi.reset_index()

This is frequently used after

groupby()

For example

result = (
    sales.groupby("Region")["Sales"]
    .sum()
    .reset_index()
)

This produces a regular DataFrame.

6.2.25Duplicate Handling

Find duplicate rows

df.duplicated()

Count duplicates

df.duplicated().sum()

Remove duplicates

df = df.drop_duplicates()

Duplicates based on specific columns

df = df.drop_duplicates(
    subset=["CustomerID"]
)

Keep the last occurrence

df = df.drop_duplicates(
    subset=["CustomerID"],
    keep="last"
)

6.2.26Missing Values

Check missing values

df.isna().sum()

Remove rows

df.dropna()

Fill with a value

df["Sales"] = df["Sales"].fillna(0)

Fill with mean

df["Salary"] = df["Salary"].fillna(

df["Salary"].mean()

)

Forward fill

df["Sales"] = df["Sales"].ffill()

Backward fill

df["Sales"] = df["Sales"].bfill()

6.2.27String Operations

Pandas provides vectorized string operations through .str.

df["Name"] = df["Name"].str.upper()

Other examples

  • df["Name"].str.lower()
  • df["Name"].str.title()
  • df["Name"].str.strip()
  • df["Name"].str.len()

Search for text

  • df[
  • df["Name"].str.contains(
  • "john",
case=False,
na=False

)

]

Extract text

df["Email"].str.extract(

r"@(.*)"

)

This is extremely useful for cleaning real-world datasets.

6.2.28Date and Time Operations

Convert a column to datetime

df["OrderDate"] = pd.to_datetime(

df["OrderDate"]

)

Extract year

df["Year"] = df["OrderDate"].dt.year

Month

df["Month"] = df["OrderDate"].dt.month

Month name

df["Month_Name"] = (

df["OrderDate"].dt.month_name()

)

Day

df["Day"] = df["OrderDate"].dt.day

Day of week

df["DayOfWeek"] = (

df["OrderDate"].dt.day_name()

)

6.2.29Time Difference

Suppose

df["StartDate"] = pd.to_datetime(

df["StartDate"]

)

df["EndDate"] = pd.to_datetime(

df["EndDate"]

)

Calculate duration

  • df["Duration"] = (
  • df["EndDate"] -
  • df["StartDate"]

)

Number of days

df["Duration_Days"] = (

df["Duration"].dt.days

)

6.2.30Rolling Window Calculations

Rolling calculations are extremely important in time-series analysis.

Suppose

sales = pd.Series(
\[100, 120, 150, 130, 180, 200\]

)

Three-period moving average

moving_avg = sales.rolling(3).mean()
print(moving_avg)

Conceptually

  • 100
  • 120
  • (100 + 120 + 150) / 3
  • (120 + 150 + 130) / 3

...

Rolling calculations are useful for

  • moving averages
  • smoothing
  • trend analysis
  • monitoring
  • anomaly detection

6.2.31Expanding Calculations

An expanding calculation uses all observations available up to the current row.

sales = pd.Series(
\[100, 200, 300, 400\]

)

cumulative_avg = sales.expanding().mean()
print(cumulative_avg)

Cumulative sum

cumulative_sales = sales.cumsum()

6.2.32Ranking

Suppose

df = pd.DataFrame({
    "Employee": ["A", "B", "C", "D"],
    "Sales": [500, 800, 300, 700]
})

Rank

df["Rank"] = df["Sales"].rank(

ascending=False

)

Result

Employee Sales Rank

A 500 3

B 800 1

C 300 4

D 700 2

Ranking is frequently used in business reporting.

6.2.33Top-N Analysis

Top 3 records

top3 = df.nlargest(
    3,
    "Sales"
)

Bottom 3

bottom3 = df.nsmallest(
    3,
    "Sales"
)

This is simpler than manually sorting and slicing.

6.2.34Cumulative Calculations

Cumulative sum

df["CumulativeSales"] = (

df["Sales"].cumsum()

)

Cumulative maximum

df["RunningMax"] = (

df["Sales"].cummax()

)

Cumulative minimum

df["RunningMin"] = (

df["Sales"].cummin()

)

6.2.35cut() and qcut()

These functions are useful for creating categories.

cut()

Create fixed ranges

ages = pd.Series(
\[18, 22, 27, 35, 42, 55, 67\]

)

groups = pd.cut(
    ages,
    bins=[0, 20, 40, 60, 100],
    labels=[
        "Young",
        "Adult",
        "Middle",
        "Senior"
    ]
)
print(groups)

qcut()

Create approximately equal-sized groups

groups = pd.qcut(
    ages,
    q=4,
    labels=[
        "Q1", "Q2", "Q3", "Q4"
    ]
)

This is useful for

  • customer segmentation
  • income bands
  • sales quartiles
  • risk categories

6.2.36Categorical Data

  • For columns with a small set of repeated values, the category dtype can reduce memory usage.
  • df["Department"] = (
  • df["Department"].astype("category")

)

For example

  • IT
  • IT
  • IT
  • HR
  • HR
  • Finance

Instead of storing each string independently, Pandas can represent the values using categorical codes.

This can improve memory efficiency for large datasets.

6.2.37Reading Large Datasets in Chunks

When a CSV is too large to fit comfortably into memory

for chunk in pd.read_csv(
    "large_file.csv",
    chunksize=100000

)

process(chunk)

For example

total_sales = 0
for chunk in pd.read_csv(
    "sales.csv",
    chunksize=100000

)

total_sales += chunk["Sales"].sum()

print(total_sales)

This allows large files to be processed incrementally.

6.2.38Memory Optimization

Check memory usage

df.info(memory_usage="deep")

Convert suitable columns

df["Department"] = (

df["Department"].astype("category")

)

Use smaller numerical types where appropriate

df["Quantity"] = (

df["Quantity"].astype("int32")

)

Avoid unnecessary copies

df2 = df.copy()

only when an independent DataFrame is actually required.

6.2.39Method Chaining

Instead of writing many intermediate variables

df = df.dropna()
df = df[df["Sales"] > 1000]
df = df.sort_values("Sales")

you can use method chaining

result = (
    df
    .dropna()
    .query("Sales > 1000")
    .sort_values("Sales", ascending=False)
)

This makes data-processing pipelines easier to follow.

6.2.40assign()

assign() is useful in method chains.

result = (
    df
    .assign(
        Revenue=lambda x:
        x["Quantity"] * x["Price"]
    )
)

You can create multiple columns

result = (
    df
    .assign(
        Revenue=lambda x:
        x["Quantity"] * x["Price"],
        Discount=lambda x:
        x["Revenue"] * 0.10
    )
)

6.2.41Advanced Business Example

Consider an order dataset

orders = pd.DataFrame({
    "OrderID": [1, 2, 3, 4, 5, 6],
    "Region": [
        "South", "South", "North",
        "North", "West", "West"
    ],
    "Product": [
        "Laptop", "Phone", "Laptop",
        "Phone", "Laptop", "Phone"
    ],
    "Quantity": [2, 5, 3, 4, 1, 6],
    "Price": [
        50000, 30000, 50000,
        30000, 50000, 30000
    ]
})

Calculate revenue

  • orders["Revenue"] = (
  • orders["Quantity"] *
  • orders["Price"]

)

Regional revenue

regional = (
    orders
    .groupby("Region")
\["Revenue"\]

.sum()

.reset_index()

)

Product revenue

product_sales = (
    orders
    .groupby("Product")
\["Revenue"\]

.sum()

.reset_index()

)

Find the top product

top_product = (
    orders
    .groupby("Product")["Revenue"]
    .sum()
    .nlargest(1)
)

Calculate each order's percentage of regional revenue

  • orders["Region_Total"] = (
  • orders
  • .groupby("Region")["Revenue"]
  • .transform("sum")

)

  • orders["Region_Percentage"] = (
  • orders["Revenue"] /
  • orders["Region_Total"] *
  • 100

)

This is a realistic analytical workflow.

6.2.42Pandas and SQL Comparison

If you already know SQL, the following mapping is useful

SQLPandas
SELECTdf[columns]
WHEREBoolean filtering / query()
GROUP BYgroupby()
SUM()sum()
AVG()mean()
COUNT()count() / size()
ORDER BYsort_values()
DISTINCTdrop_duplicates() / unique()
INNER JOINmerge(how="inner")
LEFT JOINmerge(how="left")
UNION ALLconcat()
CASE WHENnp.where() / apply()
Window functiontransform() / rolling()
PIVOTpivot() / pivot_table()
LIMIThead()

For example, SQL

  • SELECT
  • Region,
  • SUM(Revenue) AS TotalRevenue
  • FROM Orders
  • GROUP BY Region
  • ORDER BY TotalRevenue DESC;

Pandas

result = (
    orders
    .groupby("Region")["Revenue"]
    .sum()
    .sort_values(ascending=False)
    .reset_index(name="TotalRevenue")
)

6.2.43Advanced Window-Function Concept

SQL users often use

SUM(Revenue) OVER (

PARTITION BY Region

)

A Pandas equivalent can be

  • orders["Region_Total"] = (
  • orders
  • .groupby("Region")["Revenue"]
  • .transform("sum")

)

For ranking within groups

  • orders["Region_Rank"] = (
  • orders
  • .groupby("Region")["Revenue"]
  • .rank(ascending=False)

)

This is a very important bridge between SQL analytics and Pandas analytics.

6.2.44Practical Project — E-Commerce Sales Analysis

Create a DataFrame containing

  • OrderID
  • OrderDate
  • CustomerID
  • Region
  • Category
  • Product
  • Quantity
  • UnitPrice
  • Discount
  • Step 1 — Calculate revenue
  • df["Revenue"] = (
  • df["Quantity"] *
  • df["UnitPrice"] *
  • (1 - df["Discount"])

)

  • Step 2 — Calculate monthly sales
  • df["OrderDate"] = pd.to_datetime(
  • df["OrderDate"]

)

monthly_sales = (
    df.groupby(
        df["OrderDate"].dt.to_period("M")
    )["Revenue"]
    .sum()
)

Step 3 — Regional performance

regional_sales = (
    df.groupby("Region")["Revenue"]
    .agg(["sum", "mean", "count"])
)

Step 4 — Product performance

product_sales = (
    df.groupby("Product")["Revenue"]
    .sum()
    .sort_values(ascending=False)
)

Step 5 — Top customers

top_customers = (
    df.groupby("CustomerID")["Revenue"]
    .sum()
    .nlargest(10)
)

Step 6 — Monthly moving average

monthly = (
    df.groupby(
        df["OrderDate"].dt.to_period("M")
    )["Revenue"]
    .sum()
)
moving_average = monthly.rolling(3).mean()

This project brings together most of the advanced Pandas concepts.

6.2.45Common Pandas Mistakes

Mistake 1 — Chained assignment

Avoid patterns such as

df[df["Sales"] > 1000]["Status"] = "High"

Prefer

df.loc[
    df["Sales"] > 1000,
    "Status"
] = "High"

This is clearer and avoids problems associated with modifying an intermediate object.

Mistake 2 — Using apply() unnecessarily

Instead of

df["Revenue"] = df["Sales"].apply(

lambda x: x * 1.18

)

prefer a vectorized operation

  • df["Revenue"] = df["Sales"] * 1.18
  • Vectorized operations are generally clearer and faster.
  • Mistake 3 — Forgetting reset_index()

After

result = df.groupby("Region")["Sales"].sum()

Region becomes the index.

If you need a normal DataFrame

result = result.reset_index()

Mistake 4 — Incorrect datetime handling

Don't treat date columns as ordinary strings when you need date calculations.

Use

df["OrderDate"] = pd.to_datetime(

df["OrderDate"]

)

6.2.46Advanced Pandas Cheat Sheet

# Selection

df["Column"]

df[["A", "B"]]

df.loc[rows, columns]
df.iloc[rows, columns]

# Filtering

df[df["Sales"] > 1000]

df.query("Sales > 1000")

df[df["Region"].isin(["South", "North"])]

# Sorting

df.sort_values("Sales")
df.sort_values("Sales", ascending=False)

# Grouping

df.groupby("Region")["Sales"].sum()
df.groupby("Region")["Sales"].mean()
df.groupby("Region").agg({
    "Sales": ["sum", "mean", "max"]
})

# Transformation

df.groupby("Region")["Sales"].transform("sum")

# Joining

pd.merge(df1, df2, on="ID", how="left")

# Combining

pd.concat([df1, df2])

# Reshaping

df.pivot()
df.pivot_table()
df.melt()
df.stack()
df.unstack()

# Missing data

df.isna()
df.dropna()
df.fillna()
df.ffill()
df.bfill()

# Duplicates

df.duplicated()
df.drop_duplicates()

# Dates

pd.to_datetime(df["Date"])
  • df["Date"].dt.year
  • df["Date"].dt.month
  • df["Date"].dt.day
  • # Ranking
  • df["Rank"] = df["Sales"].rank()
  • # Rolling
  • df["Sales"].rolling(3).mean()
  • # Cumulative
  • df["Sales"].cumsum()
  • # Top N
df.nlargest(10, "Sales")
df.nsmallest(10, "Sales")

# Categorization

pd.cut()
pd.qcut()

# Memory

df.info(memory_usage="deep")

6.2.47Interview Questions

Intermediate

  • What is the difference between a Series and a DataFrame?
  • What is the difference between loc and iloc?
  • What is groupby()?
  • What is the difference between merge() and concat()?
  • What is a PivotTable?
  • What is the difference between pivot() and pivot_table()?
  • What does reset_index() do?
  • What is melt()?
  • How do you handle duplicate records?
  • How do you handle missing values?

Advanced

  • Explain groupby().agg() versus groupby().transform().
  • When would you use apply()?
  • Why should vectorized operations generally be preferred over apply()?
  • Explain Pandas MultiIndex.
  • How do you perform a left join in Pandas?
  • How do you calculate a rolling average?
  • How would you implement a SQL window function in Pandas?
  • How can you optimize Pandas memory usage?
  • How do you process a CSV file that is larger than available memory?
  • What is method chaining?
  • What is the difference between map(), apply(), and transform()?
  • How do you calculate a group's percentage of total?
  • How would you rank records within each group?
  • How would you perform time-series aggregation?
  • How would you optimize a slow Pandas transformation?

6.2.48Key Takeaways

The most important advanced Pandas concepts are

DataFrame
Advanced Selection
Filtering
groupby()
Aggregation
transform()
apply() / map()
merge() / join()
concat()
pivot_table()
melt()
MultiIndex
Date & Time Analysis
Rolling / Window Operations
Ranking
Memory Optimization
Large Dataset Processing
  • The five concepts to master first

If your goal is Data Analyst / Data Engineer / BI Developer work, prioritize

  • groupby() + agg()
  • merge() / joins
  • transform() and window-style calculations
  • pivot_table() / melt()
  • Datetime + rolling operations

Once these are strong, you can move naturally from SQL → Pandas → Visualization → Power BI, which is the core workflow for practical data analysis.

Module 6 · Lesson 6.3

Data Cleaning

Data cleaning is the process of identifying, correcting, removing, or transforming inaccurate, incomplete, inconsistent, duplicated, and improperly formatted data before analysis.

In real-world data projects, data is rarely ready for immediate analysis. It may contain

  • Missing values
  • Duplicate records
  • Incorrect data types
  • Invalid dates
  • Spelling variations
  • Extra spaces
  • Incorrect numerical values
  • Outliers
  • Inconsistent categories
  • Invalid business rules

A good data analyst spends significant time making the dataset accurate, consistent, complete, and analysis-ready.

6.3.1Learning Objectives

By the end of this topic, you should be able to

  • Understand data-quality problems.
  • Inspect a raw dataset systematically.
  • Identify duplicate records.
  • Detect missing and invalid values.
  • Standardize text and categorical data.
  • Convert incorrect data types.
  • Validate dates and numerical values.
  • Detect inconsistent business values.
  • Build reusable data-cleaning pipelines.
  • Maintain data-quality checks.
  • Produce a clean analytical dataset.

6.3.2What Is Data Cleaning?

Suppose you receive this customer dataset

Customer_IDNameCityAgeEmailSales
101RaviHyderabad32ravi@gmail.com5000
102RAVIHyderabad32ravi@gmail.com5000
103Priyahyderabad-5priya@gmail.com7000
104ArunBangalore29NULL6000
105MeenaBengaluru31meena@gmail.com8000

There are several problems

  • Ravi/RAVI may represent the same customer.
  • Hyderabad and hyderabad are inconsistent.
  • Age -5 is invalid.
  • Email is missing.
  • Bangalore and Bengaluru may represent the same location.
  • Duplicate customer information may exist.
  • Cleaning converts this raw dataset into a reliable analytical dataset.

6.3.3Data Quality Dimensions

Data quality can be evaluated using several dimensions.

1. Accuracy

Does the value represent reality?

Example

Age = -10

is not accurate.

2. Completeness

Are required values present?

Example

Email = NULL

3. Consistency

Are values represented consistently?

Example

  • Hyderabad
  • hyderabad
  • HYDERABAD

4. Validity

Does the value follow the required format?

Example

Email = "hello"

may not be a valid email address.

5. Uniqueness

Are duplicate records present?

6. Timeliness

Is the data current enough for the business purpose?

6.3.4Typical Data-Cleaning Workflow

A practical workflow is

Raw Data
Understand Dataset
Inspect Structure
Check Data Types
Check Missing Values
Check Duplicates
Standardize Values
Validate Data
Handle Invalid Records
Handle Outliers
Transform Data
Quality Checks
Clean Dataset
Analysis / Visualization

The order can vary depending on the dataset.

6.3.5Loading Data

Example CSV

import pandas as pd
df = pd.read_csv("customers.csv")
print(df.head())

Inspect the dataset

print(df.shape)
print(df.columns)
print(df.dtypes)

6.3.6Initial Data Inspection

First records

df.head()

Last records

df.tail()

Dataset information

df.info()

Statistical summary

df.describe()

For categorical columns

df.describe(include="object")

For all columns

df.describe(include="all")

6.3.7Understanding Data Types

Suppose

print(df.dtypes)

Output

Customer_ID int64

Name object

Age object

Join_Date object

Sales float64

The Age and Join_Date columns may require conversion.

6.3.8Converting Data Types

Convert age to numeric

df["Age"] = pd.to_numeric(

df["Age"],

errors="coerce"

)

errors="coerce" converts invalid values to NaN.

For example

"35" → 35

"42" → 42

"unknown" → NaN

6.3.9Date Conversion

Suppose

  • Join_Date
  • 01-01-2025
  • 15-02-2025
  • invalid

Convert

df["Join_Date"] = pd.to_datetime(

df["Join_Date"],

errors="coerce"

)

Invalid dates become missing values.

Then inspect

df[df["Join_Date"].isna()]

6.3.10Detecting Missing Values

Check whether values are missing

df.isna()

Count missing values

df.isna().sum()

Percentage of missing values

missing_percentage = (
    df.isna().mean() * 100
)
print(missing_percentage)

This is much more useful than simply knowing the number of missing records.

6.3.11Missing Value Patterns

Suppose

missing = pd.DataFrame({
    "Name": [0, 0, 1, 0],
    "Email": [0, 1, 1, 0],
    "Age": [0, 0, 0, 1]
})

You may discover

Email → 50% missing

Age → 25% missing

Name → 25% missing

The correct treatment depends on the business context.

For example

  • Missing email may be acceptable.
  • Missing customer ID may be unacceptable.
  • Missing transaction amount may require investigation.

6.3.12Detecting Duplicate Records

Check duplicates

df.duplicated()

Count them

df.duplicated().sum()

View duplicate records

df[df.duplicated()]

Remove duplicates

df = df.drop_duplicates()

6.3.13Duplicates Based on Selected Columns

Sometimes the entire row is not duplicated, but a business key is duplicated.

Example

df.duplicated(
    subset=["Customer_ID"]
)

Remove duplicate customers

df = df.drop_duplicates(
    subset=["Customer_ID"]
)

Keep the latest record

df = df.sort_values("Updated_Date")
df = df.drop_duplicates(
    subset=["Customer_ID"],
    keep="last"
)

This is common in customer master and transaction datasets.

6.3.14Standardizing Text

Real-world text often contains inconsistent capitalization.

Example

  • Hyderabad
  • hyderabad
  • HYDERABAD
  • HyDeRaBaD

Convert to lowercase

df["City"] = df["City"].str.lower()

Or uppercase

df["City"] = df["City"].str.upper()

Title case

df["City"] = df["City"].str.title()

6.3.15Removing Extra Spaces

Consider

  • " Hyderabad"
  • "Hyderabad "
  • " Hyderabad "

Use

df["City"] = df["City"].str.strip()

For multiple text columns

text_columns = ["Name", "City", "Department"]
for column in text_columns:
    df[column] = df[column].str.strip()

6.3.16Internal Whitespace

Suppose

"New Delhi"

You can normalize repeated spaces

df["City"] = (

  • df["City"]
  • .str.replace(r"\s+", " ", regex=True)
  • .str.strip()

)

Result

New Delhi

6.3.17Standardizing Categories

Suppose a department column contains

  • IT
  • it
  • Information Technology

I.T.

Technology

These may represent the same business category.

Create a mapping

department_mapping = {
    "IT": "Information Technology",
    "it": "Information Technology",
    "I.T.": "Information Technology",
    "Technology": "Information Technology"
}
  • df["Department"] = (
  • df["Department"]
  • .replace(department_mapping)

)

6.3.18Standardizing Locations

Suppose

  • Bangalore
  • Bengaluru
  • BANGALORE

Use a mapping

city_mapping = {
    "Bangalore": "Bengaluru",
    "BANGALORE": "Bengaluru"
}

df["City"] = df["City"].replace(city_mapping)

The important lesson is

Standardization should be based on a defined business rule, not arbitrary assumptions.

6.3.19Handling Invalid Numerical Values

Suppose

df["Age"]

contains

25

32

-5

150

40

A reasonable business rule might be

Age must be between 0 and 120

Find invalid values

invalid_age = df[
    (df["Age"] < 0) |
    (df["Age"] > 120)
]

Convert invalid values to missing

df.loc[
    (df["Age"] < 0) |
    (df["Age"] > 120),
    "Age"
] = pd.NA

6.3.20Business Rule Validation

Data cleaning should not only check technical correctness.

Suppose an order dataset has

  • Quantity
  • UnitPrice
  • Discount
  • Revenue

Possible business rules

  • Quantity >= 0
  • UnitPrice >= 0
  • Discount between 0 and 1
  • Revenue >= 0

Check quantity

invalid_quantity = df[
    df["Quantity"] < 0
]

Check discount

invalid_discount = df[
    (df["Discount"] < 0) |
    (df["Discount"] > 1)
]

6.3.21Cleaning Email Addresses

Suppose

" Ravi@Gmail.com "

"PRIYA@YAHOO.COM"

Normalize

df["Email"] = (

  • df["Email"]
  • .str.strip()
  • .str.lower()

)

Basic validation

email_pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$"
invalid_email = df[
    ~df["Email"].str.match(
        email_pattern,
        na=False
    )
]

This identifies values that don't follow a basic email pattern.

6.3.22Replacing Invalid Values

Suppose the dataset contains

  • N/A
  • NA
  • Unknown
  • unknown

-

These should potentially be treated as missing.

missing_values = [
    "N/A",
    "NA",
    "Unknown",
    "unknown",
    "-"
]
df = df.replace(
    missing_values,
    pd.NA
)

This is useful before performing missing-value analysis.

6.3.23Replacing Specific Values

Use replace()

df["Gender"] = df["Gender"].replace({

"M": "Male",

"F": "Female"

})

This creates consistent categories.

6.3.24Detecting Impossible Dates

Suppose an employee's joining date is

2035-01-01

but today is earlier than that.

You can validate

future_dates = df[
    df["Join_Date"] > pd.Timestamp.now()
]

Similarly, business rules may require

OrderDate >= CustomerRegistrationDate

Check

invalid = df[
    df["OrderDate"] <
    df["CustomerRegistrationDate"]
]

6.3.25Data Validation with assert

Assertions can be used for automated quality checks.

For example

assert df["Customer_ID"].notna().all()

Check that sales are non-negative

assert (df["Sales"] >= 0).all()

Check that age is within a valid range

assert df["Age"].between(

0, 120

).all()

Assertions are particularly useful in production pipelines.

6.3.26Creating a Data Quality Report

A simple quality report

quality_report = pd.DataFrame({
    "Column": df.columns,
    "Missing_Count": [
        df[column].isna().sum()
        for column in df.columns
    ],
    "Missing_Percentage": [
        df[column].isna().mean() * 100
        for column in df.columns
    ],
    "Unique_Count": [
        df[column].nunique()
        for column in df.columns
    ],
    "Data_Type": [
        df[column].dtype
        for column in df.columns
    ]
})
print(quality_report)

This gives a quick overview of dataset quality.

6.3.27Detecting Constant Columns

A column containing the same value for every row often provides little analytical value.

constant_columns = [
    column
    for column in df.columns
    if df[column].nunique(dropna=False) <= 1
]
print(constant_columns)

Such columns can potentially be removed after confirming they have no business purpose.

6.3.28Detecting High-Cardinality Columns

Check the number of unique values

df.nunique().sort_values(
    ascending=False
)

High-cardinality columns may include

  • Transaction_ID
  • Email
  • Customer_ID

These are not necessarily bad, but they should be treated differently from low-cardinality categorical columns.

6.3.29Handling Outliers

Data cleaning often includes identifying unusual numerical values.

Example

Q1 = df["Sales"].quantile(0.25)
Q3 = df["Sales"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
outliers = df[
    (df["Sales"] < lower) |
    (df["Sales"] > upper)
]

Do not automatically delete outliers.

An outlier may represent

  • data-entry error
  • fraud
  • genuine large transaction
  • exceptional business event
  • The correct action requires business understanding.

6.3.30Cleaning Numerical Formatting

Suppose sales values are stored as

  • ₹50,000
  • ₹1,20,000
  • ₹75,000

These are strings, not numbers.

Clean them

df["Sales"] = (

  • df["Sales"]
  • .str.replace("₹", "", regex=False)
  • .str.replace(",", "", regex=False)
  • .astype(float)

)

Now

₹50,000

becomes

50000.0

6.3.31Cleaning Percentage Values

Suppose

  • 10%
  • 20%
  • 15%

Clean them

  • df["Discount"] = (
  • df["Discount"]
  • .str.replace("%", "", regex=False)
  • .astype(float)
  • / 100

)

Now

10%

becomes

0.10

6.3.32Cleaning Phone Numbers

Suppose phone numbers appear as

  • +91-9876543210
  • 98765 43210
  • 9876543210

Remove unwanted characters

df["Phone"] = (

df["Phone"]

.str.replace(r"\D", "", regex=True)

)

This keeps only digits.

Further validation can check length and country-specific business rules.

6.3.33Handling Duplicate Customers

Suppose

customers = pd.DataFrame({
    "CustomerID": [101, 102, 102, 103],
    "Name": ["Ravi", "Priya", "Priya", "Arun"],
    "UpdatedDate": [
        "2025-01-01",
        "2025-01-05",
        "2025-02-01",
        "2025-01-10"
    ]
})

Convert the date

customers["UpdatedDate"] = pd.to_datetime(

customers["UpdatedDate"]

)

Sort

customers = customers.sort_values(
    "UpdatedDate"
)

Keep the latest customer record

customers = customers.drop_duplicates(
    subset=["CustomerID"],
    keep="last"
)

This is a common master-data cleaning pattern.

6.3.34Cleaning a Complete Dataset

Consider

df = pd.DataFrame({
    "Customer_ID": [101, 102, 102, 103],
    "Name": [
        " Ravi ",
        "PRIYA",
        "PRIYA",
        " Arun"
    ],
    "City": [
        "hyderabad",
        "Bangalore ",
        "Bengaluru",
        "HYDERABAD"
    ],
    "Age": [30, 25, 25, -5],
    "Sales": [
        "₹50,000",
        "₹70,000",
        "₹70,000",
        "₹30,000"
    ]
})

Step 1 — Clean text

df["Name"] = (

  • df["Name"]
  • .str.strip()
  • .str.title()

)

Step 2 — Standardize city

df["City"] = (

  • df["City"]
  • .str.strip()
  • .str.title()

)

df["City"] = df["City"].replace({

"Bangalore": "Bengaluru"

})

Step 3 — Convert age

df["Age"] = pd.to_numeric(

df["Age"],

errors="coerce"

)

Step 4 — Validate age

df.loc[
    ~df["Age"].between(0, 120),
    "Age"
] = pd.NA

Step 5 — Clean sales

df["Sales"] = (

  • df["Sales"]
  • .str.replace("₹", "", regex=False)
  • .str.replace(",", "", regex=False)
  • .astype(float)

)

Step 6 — Remove duplicate customer IDs

df = df.drop_duplicates(
    subset=["Customer_ID"]
)

The dataset is now much more consistent and suitable for analysis.

6.3.35Building a Reusable Cleaning Function

Instead of repeating cleaning logic

def clean_customer_data(df):
df = df.copy()

# Standardize names

df["Name"] = (

  • df["Name"]
  • .str.strip()
  • .str.title()

)

# Standardize city

df["City"] = (

  • df["City"]
  • .str.strip()
  • .str.title()

)

  • # Standardize city names
  • df["City"] = df["City"].replace({
  • "Bangalore": "Bengaluru"

})

# Convert age

df["Age"] = pd.to_numeric(

df["Age"],

errors="coerce"

)

# Validate age

df.loc[
    ~df["Age"].between(0, 120),
    "Age"
] = pd.NA
return df

Then

clean_df = clean_customer_data(df)

This makes the process reusable.

6.3.36Data Cleaning Pipeline

A production-style pipeline may look like

def clean_data(df):
    return (
        df
        .drop_duplicates()
        .assign(
            Name=lambda x:
            x["Name"].str.strip().str.title(),
            City=lambda x:
            x["City"].str.strip().str.title()
        )
    )

For complex projects, separate functions are often better

load_data()
standardize_columns()
clean_text()
convert_types()
handle_missing_values()
validate_ranges()
remove_duplicates()
validate_business_rules()
generate_quality_report()
save_clean_data()

6.3.37Column Name Cleaning

Raw datasets often contain

  • Customer ID
  • Customer Name
  • Order Date
  • Total Sales ($)

Standardize them

df.columns = (
    df.columns
    .str.strip()
    .str.lower()
    .str.replace(" ", "_")
    .str.replace(r"[^\w]", "", regex=True)
)

A cleaner approach when special characters need to be preserved carefully is to explicitly map important columns.

For example

df = df.rename(columns={
    "Customer ID": "customer_id",
    "Order Date": "order_date",
    "Total Sales ($)": "total_sales"
})

Explicit mappings are often safer for production pipelines.

6.3.38Data Cleaning and ETL

Data cleaning is a major part of ETL/ELT.

Source Systems
Extract
Raw Data
Data Cleaning
Transformation
Validation
Data Warehouse
BI / Analytics

For example

  • SQL Server
  • Oracle
  • Excel
  • CSV
APIs
Python / Pandas
Clean + Validate
Data Warehouse
Power BI

This is why data-cleaning knowledge is highly valuable for Data Analysts, Data Engineers, BI Developers, and ML Engineers.

6.3.39Data Cleaning vs Data Transformation

  • These concepts are related but different.
  • Data Cleaning
  • Focuses on correcting data-quality problems.

Examples

  • Remove duplicates
  • Fix invalid dates
  • Handle missing values
  • Correct spelling
  • Remove invalid records
  • Data Transformation
  • Changes the representation of valid data.

Examples

  • Create revenue
  • Calculate profit margin
  • Convert currencies
  • Create customer segments
  • Normalize numerical values

Typical workflow

Raw Data
Cleaning
Transformation
Analysis

6.3.40Data Cleaning Checklist

Before considering a dataset ready for analysis, check

  • Correct column names
  • Correct data types
  • Missing values identified
  • Missing values handled appropriately
  • Duplicate records checked
  • Duplicate business keys checked
  • Text standardized
  • Leading/trailing spaces removed
  • Categories standardized
  • Numerical values validated
  • Dates validated
  • Impossible values identified
  • Outliers investigated
  • Business rules validated
  • Data-quality report generated
  • Final dataset validated

6.3.41Real-World Data Cleaning Example

Imagine an e-commerce dataset

  • OrderID
  • CustomerID
  • OrderDate
  • CustomerName
  • City
  • Product
  • Quantity
  • UnitPrice
  • Discount
  • Revenue

A professional cleaning process might be

1. Check row count

2. Check column names

3. Check data types

4. Standardize column names

5. Identify missing values

6. Check duplicate OrderIDs

7. Validate CustomerIDs

8. Clean customer names

9. Standardize cities

10. Convert OrderDate

11. Validate Quantity

12. Validate UnitPrice

13. Validate Discount

14. Recalculate Revenue

15. Compare calculated vs source Revenue

16. Investigate anomalies

17. Generate quality report

18. Save clean dataset

This is much closer to how data cleaning works in real projects than simply calling dropna().

6.3.42Important Principle: Never Delete Data Blindly

A common beginner mistake is

df = df.dropna()

This may remove thousands of valuable records.

Similarly

df = df.drop_duplicates()

may remove legitimate repeated transactions.

And

df = df[df["Sales"] < upper_limit]

may remove genuine high-value transactions.

The correct process is

Detect
Understand
Validate
Decide
Clean
Document

Every major cleaning decision should have a business or technical reason.

6.3.43Interview Questions

  • Basic
  • What is data cleaning?
  • Why is data cleaning important?
  • What are common data-quality problems?
  • How do you identify missing values?
  • How do you identify duplicate records?
  • How do you remove duplicates?
  • How do you convert a string column to numeric?
  • How do you convert a column to datetime?
  • How do you standardize text?
  • What is data validation?

Advanced

  • How would you clean a dataset with millions of rows?
  • How do you distinguish a duplicate transaction from a legitimate repeated transaction?
  • How would you handle an invalid age such as -10?
  • How would you standardize inconsistent city names?
  • How would you handle invalid dates?
  • How would you validate business rules using Pandas?
  • How would you create an automated data-quality report?
  • How would you build a reusable data-cleaning pipeline?
  • What is the difference between data cleaning and data transformation?
  • Why shouldn't all missing values simply be removed?
  • Why shouldn't all outliers be removed?
  • How would you compare source revenue with calculated revenue?
  • How would you clean a dataset containing ₹, commas, and percentage symbols?
  • How would you process a CSV larger than available memory?
  • How would you monitor data quality in a production ETL pipeline?

6.3.44Mini Project — Customer Data Quality Pipeline

Create a project using a messy customer dataset containing

  • Customer_ID
  • Customer_Name
  • Email
  • Phone
  • City
  • Age
  • Registration_Date
  • Total_Purchases

Your pipeline should perform

Raw Customer Data
Column Name Standardization
Text Cleaning
Email Cleaning
Phone Cleaning
Date Conversion
Age Validation
Duplicate Customer Detection
Missing Value Analysis
City Standardization
Numerical Validation
Data Quality Report
Clean Customer Dataset

Expected output

Produce three datasets

1. Clean Data

Contains records suitable for analysis.

2. Rejected Data

Contains records that failed critical validation rules.

3. Data Quality Report

Contains

  • Column
  • Data Type
  • Total Records
  • Missing Count
  • Missing %
  • Unique Count
  • Invalid Count
  • Duplicate Count

This approach is much closer to a production data-quality pipeline than simply cleaning a DataFrame interactively.

6.3.45Key Takeaways

The complete data-cleaning process can be summarized as

RAW DATA

┌───────────────┐

│ PROFILE │

│ DATA │

└───────┬───────┘

┌─────────┼─────────┐

▼ ▼ ▼

Missing Duplicate Invalid

Values Data Values

│ │ │

└─────────┼─────────┘

STANDARDIZATION

TYPE CONVERSION

BUSINESS VALIDATION

QUALITY CHECKS

CLEAN DATA

ANALYSIS / BI / ML

  • Five concepts to master

For practical Data Analyst and Data Engineer work, focus especially on

  • Missing-value detection and treatment
  • Duplicate and business-key validation
  • Data-type and date conversion
  • Text/category standardization
  • Business-rule and data-quality validation

The ultimate goal of data cleaning is not simply to make a dataset look clean. The goal is to make the data trustworthy enough that the decisions made from it can also be trusted.

Module 6 · Lesson 6.4

Missing Values

Missing values are observations where a particular field does not contain a usable value. They are one of the most common data-quality problems in real-world datasets.

Missing data can occur because

  • A customer did not provide information.
  • A system failed to capture a value.
  • A field was not applicable.
  • Data was lost during ETL.
  • Different source systems use different representations.
  • A value was intentionally hidden or removed.
  • A data-entry error occurred.

Handling missing values correctly is important because careless treatment can introduce bias, incorrect statistics, and misleading business conclusions.

6.4.1Learning Objectives

By the end of this topic, you should be able to

  • Identify missing values using Pandas and NumPy.
  • Understand different representations of missing data.
  • Measure missing-value percentages.
  • Distinguish different types of missingness.
  • Remove missing records appropriately.
  • Fill missing values using statistical methods.
  • Use forward and backward filling.
  • Interpolate missing time-series values.
  • Handle missing categorical data.
  • Handle missing dates.
  • Create missing-value indicators.
  • Validate imputed data.
  • Choose an appropriate missing-value strategy.

6.4.2What Is a Missing Value?

Consider

CustomerAgeCitySalary
Ravi32Hyderabad60000
Priya29Bengaluru55000
ArunNaNHyderabad70000
Meena35NaN65000

Here

Arun's Age is missing.

Meena's City is missing.

In Pandas, missing numerical values are commonly represented using NaN, while modern Pandas also supports pd.NA.

6.4.3Common Representations of Missing Data

Missing data may appear as

NaN

None

pd.NA
  • NaT
  • NULL
  • N/A
  • NA
  • Unknown

?

-

Blank

Empty string

Not all of these are automatically recognized as missing.

For example

data = pd.Series([
    "Ravi",
    "Priya",
    "NA",
    "Arun"
])
print(data.isna())

The string "NA" may be treated as ordinary text depending on how the data was loaded.

6.4.4Creating Missing Values

Using NumPy

import numpy as np
import pandas as pd
data = pd.Series([
    100,
    200,
    np.nan,
    400
])
print(data)

Using Pandas

data = pd.Series([
    100,
    200,
    pd.NA,
    400
])

For datetime

dates = pd.Series([
    "2025-01-01",
    None,
    "2025-03-01"
])
dates = pd.to_datetime(dates)

The missing date becomes NaT (Not a Time).

6.4.5Detecting Missing Values

The most important function is

df.isna()

Example

df = pd.DataFrame({
    "Name": ["Ravi", "Priya", "Arun"],
    "Age": [30, np.nan, 35],
    "City": ["Hyderabad", "Bengaluru", np.nan]
})
print(df.isna())

6.4.6Counting Missing Values

Use

df.isna().sum()

Example result

Name 0

Age 1

City 1

This gives the number of missing values per column.

6.4.7Missing Value Percentage

Count alone isn't enough.

For example

missing_percentage = (
    df.isna().mean() * 100
)
print(missing_percentage)

You might get

Name 0.0

Age 33.3

City 33.3

This allows you to compare missingness across columns.

6.4.8Total Missing Values

total_missing = df.isna().sum().sum()
print(total_missing)

This returns the total number of missing cells in the DataFrame.

6.4.9Finding Rows Containing Missing Values

missing_rows = df[
    df.isna().any(axis=1)
]
print(missing_rows)

This returns every row containing at least one missing value.

6.4.10Finding Rows Without Missing Values

complete_rows = df[
    df.notna().all(axis=1)
]

This returns rows where every column contains a value.

6.4.11Checking a Specific Column

df[df["Age"].isna()]

This returns records where Age is missing.

For non-missing values

df[df["Age"].notna()]

6.4.12Missing Values During CSV Loading

Pandas can recognize common missing-value representations when reading files.

df = pd.read_csv(
    "customers.csv",
    na_values=[
        "NA",
        "N/A",
        "Unknown",
        "-"
    ]
)

Now those values can be treated as missing.

You can also specify multiple values

missing_values = [
    "",
    " ",
    "NA",
    "N/A",
    "NULL",
    "Unknown",
    "-"
]
df = pd.read_csv(
    "customers.csv",
    na_values=missing_values
)

6.4.13Missingness Patterns

Not all missing data occurs for the same reason.

Three important concepts are

MCAR — Missing Completely At Random

Missingness has no relationship with observed or unobserved data.

Example

  • A sensor randomly fails occasionally.
  • MAR — Missing At Random
  • Missingness is related to other observed variables.

Example

  • Income is more frequently missing for customers in a particular age group.
  • MNAR — Missing Not At Random
  • Missingness is related to the missing value itself.

Example

People with very high incomes may be less willing to report their income.

These concepts matter because the appropriate treatment can differ depending on why the data is missing.

6.4.14Removing Missing Rows

The simplest approach

df_clean = df.dropna()

This removes any row containing a missing value.

Example

Original

NameAgeCity
Ravi30Hyderabad
PriyaNaNBengaluru
Arun35NaN

After

df.dropna()

Only Ravi remains.

Problem

You may lose a large amount of useful information.

Therefore, dropna() should not automatically be the default solution.

6.4.15Removing Rows Based on Specific Columns

Suppose Customer_ID and Order_ID are mandatory.

df = df.dropna(
    subset=["Customer_ID", "Order_ID"]
)

This is usually better than removing a row because an unrelated optional field is missing.

6.4.16Removing Columns with Too Many Missing Values

Suppose

df.isna().mean() * 100

returns

Name 2%

Age 5%

Phone 10%

Comments 85%

If Comments is not important and 85% of its values are missing, you may decide to remove it:

df = df.drop(
    columns=["Comments"]
)

This should be a business decision, not an automatic rule.

6.4.17Using thresh

thresh allows you to retain rows containing a minimum number of non-missing values.

df = df.dropna(
    thresh=5
)

This keeps rows with at least 5 non-null values.

For example, if a dataset has 7 columns, a row with only 4 valid values would be removed.

6.4.18Filling Missing Values

Instead of deleting missing values, you can replace them.

The function is

df.fillna(value)

Example

df["Age"] = df["Age"].fillna(0)

But replacing age with 0 may be inappropriate.

The correct replacement depends on the meaning of the data.

6.4.19Mean Imputation

Suppose

ages = pd.Series([
    25, 30, 35, np.nan, 40
])

Calculate mean

mean_age = ages.mean()
print(mean_age)

Fill

ages = ages.fillna(mean_age)

Advantages

  • Simple
  • Fast
  • Easy to understand

Disadvantages

  • Reduces natural variation
  • Can distort distributions
  • Sensitive to outliers

Mean imputation is generally more appropriate when the distribution is reasonably symmetric and the missingness mechanism supports the assumption.

6.4.20Median Imputation

Median is often preferable when data is skewed.

median_salary = df["Salary"].median()

df["Salary"] = df["Salary"].fillna(

median_salary

)

For example, salaries may contain a few very high values.

The median is less affected by extreme values than the mean.

6.4.21Mean vs Median

Suppose salaries are

  • 30,000
  • 35,000
  • 40,000
  • 45,000
  • 500,000

The mean is heavily influenced by 500,000.

The median is much more representative of the typical salary.

Therefore

Symmetric distribution → Mean may work well

Skewed distribution → Median is often safer

6.4.22Mode Imputation

For categorical data

mode_city = df["City"].mode()[0]

df["City"] = df["City"].fillna(

mode_city

)

Example

  • Hyderabad
  • Hyderabad
  • Bengaluru
  • Hyderabad
  • NaN

The mode is

Hyderabad

So the missing city would be filled with Hyderabad.

Limitation

If missingness is not representative of the most common category, mode imputation can introduce bias.

6.4.23Filling with a Constant

  • Sometimes a meaningful category can be used.
  • df["Department"] = (
  • df["Department"]
  • .fillna("Unknown")

)

For a numeric field

  • df["Sales"] = df["Sales"].fillna(0)
  • But 0 should only be used if zero has a valid business meaning.
  • Missing ≠ zero.

This is an extremely important rule.

6.4.24Forward Fill

Forward fill uses the previous available value.

df["Sales"] = df["Sales"].ffill()

Example

  • 100
  • 120
  • NaN
  • 150
  • NaN
  • 200

After forward fill

  • 100
  • 120
  • 120
  • 150
  • 150
  • 200

This is particularly useful for

  • time-series data
  • sensor readings
  • configuration values
  • status information

when carrying the previous value forward is logically valid.

6.4.25Backward Fill

Backward fill uses the next available value.

df["Sales"] = df["Sales"].bfill()

Example

  • 100
  • NaN
  • 120
  • NaN
  • 150

After backward fill

  • 100
  • 120
  • 120
  • 150
  • 150

6.4.26Forward vs Backward Fill

MethodUses
ffill()Previous known value
bfill()Next known value

For time series

January 100

February 120

March NaN

April 160

Forward fill

March = 120

Backward fill

March = 160

Neither is automatically correct. The business meaning determines the appropriate approach.

6.4.27Filling Within Groups

Suppose sales data contains multiple stores

df = pd.DataFrame({
    "Store": [
        "A", "A", "A",
        "B", "B", "B"
    ],
    "Sales": [
        100, np.nan, 150,
        200, np.nan, 300
    ]
})

Forward fill within each store

df["Sales"] = (

df.groupby("Store")["Sales"]

.ffill()

)

This is much better than filling across unrelated stores.

6.4.28Group-Based Mean Imputation

Suppose salary varies significantly by department.

Instead of using one overall mean

df["Salary"] = df["Salary"].fillna(

df["Salary"].mean()

)

use department-specific means

df["Salary"] = (

df.groupby("Department")["Salary"]

.transform(

lambda x: x.fillna(x.mean())

)

)

This can preserve differences between groups.

6.4.29Group-Based Median Imputation

df["Salary"] = (

df.groupby("Department")["Salary"]

.transform(

lambda x: x.fillna(x.median())

)

)

This can be particularly useful when salary distributions differ across departments.

6.4.30Interpolation

Interpolation estimates missing values based on surrounding observations.

Example

sales = pd.Series([
    100,
    120,
    np.nan,
    160,
    180
])
sales = sales.interpolate()
print(sales)

The missing value may become

140

because it lies between 120 and 160.

6.4.31Linear Interpolation

Linear interpolation is the default method in many simple cases

df["Sales"] = df["Sales"].interpolate(

method="linear"

)

This is useful for continuous measurements such as

  • temperature
  • electricity consumption
  • stock-related measurements
  • sensor readings

provided interpolation is meaningful for the underlying process.

6.4.32Time-Based Interpolation

For time-series data

df["Date"] = pd.to_datetime(df["Date"])

df = df.sort_values("Date")
df = df.set_index("Date")

df["Sales"] = df["Sales"].interpolate(

method="time"

)

Time-based interpolation considers the actual spacing between dates.

6.4.33Limit Interpolation

You may not want to fill an unlimited number of consecutive missing values.

df["Sales"] = df["Sales"].interpolate(

limit=2

)

This limits interpolation to a maximum number of consecutive missing values.

6.4.34Missing Value Indicators

Sometimes the fact that a value was missing is itself meaningful.

Create an indicator

df["Age_Missing"] = (

df["Age"].isna().astype(int)

)

Result

Age Age_Missing

30 0

NaN 1

40 0

This tells a model or analyst

0 → originally present

1 → originally missing

This can be useful in machine-learning workflows.

6.4.35Missing Values in Categorical Columns

Suppose

  • Department
  • IT
  • HR
  • NaN
  • Finance

Possible strategies

  • Strategy 1 — Use "Unknown"
  • df["Department"] = (
  • df["Department"]
  • .fillna("Unknown")

)

  • Strategy 2 — Use the mode
  • df["Department"] = (
  • df["Department"]
  • .fillna(
  • df["Department"].mode()[0]

)

)

Strategy 3 — Investigate the source

If department is a mandatory field, missing values may indicate a source-system problem rather than something that should simply be imputed.

6.4.36Missing Dates

Suppose

df["OrderDate"].isna().sum()

returns 100 missing dates.

Possible options

  • Retrieve the date from the source system.
  • Use another related date.
  • Remove the record if date is mandatory.
  • Create an "Unknown" date category for reporting.
  • Impute only when there is a defensible business rule.
  • Do not automatically replace missing dates with today's date.
  • That can create completely incorrect analytics.

6.4.37Missing Values in Financial Data

Consider

Customer | Revenue

A | 10000

B | NaN

C | 15000

Should NaN become zero?

Not necessarily.

Possible interpretations

Interpretation 1

Customer had zero revenue.

Then

  • fillna(0)
  • may be valid.
  • Interpretation 2
  • Revenue was not captured.

Then

Missing ≠ Zero

You should investigate the source.

This distinction is extremely important in financial and business reporting.

6.4.38Missing Values in Time Series

Consider

df = pd.DataFrame({
    "Date": pd.date_range(
        "2025-01-01",
        periods=7
    ),
    "Power": [
        100, 110, np.nan,
        130, 140, np.nan, 160
    ]
})

A simple interpolation

df["Power"] = df["Power"].interpolate()

However, for real operational data, you should consider

  • sensor behavior
  • seasonality
  • operating hours
  • shutdown periods
  • expected business patterns
  • data-collection failures

Blind interpolation can hide a genuine operational problem.

6.4.39Missing Values and Data Leakage

In machine learning, imputation must be performed carefully.

Suppose you split your data

Training Data

Testing Data

You should calculate the training-set mean or median only from the training data.

Incorrect

Calculate mean using entire dataset
Split train/test
Impute

This allows information from the test set to influence the training process.

Better

Split data
Calculate imputation values using training data
Apply those values to training data

Apply the same values to test data

This is called avoiding data leakage.

6.4.40Using Scikit-Learn for Imputation

For machine-learning pipelines, Scikit-learn provides imputers.

Example

from sklearn.impute import SimpleImputer
imputer = SimpleImputer(
    strategy="median"
)
X = imputer.fit_transform(X)

Strategies include

  • mean
  • median
  • most_frequent
  • constant

For example

imputer = SimpleImputer(
    strategy="most_frequent"
)

is suitable for some categorical features.

6.4.41Advanced Imputation Methods

For more complex problems, you may encounter

KNN Imputation

Uses nearby observations.

from sklearn.impute import KNNImputer
imputer = KNNImputer(
    n_neighbors=5
)

Iterative Imputation

Estimates missing values using relationships among other features.

These approaches can be more sophisticated but also introduce assumptions and computational cost.

6.4.42Comparing Missing-Value Strategies

StrategySuitable ForRisk
Drop rowsVery few missing recordsData loss
Drop columnsMostly missing/unimportant columnsLoss of useful information
MeanSymmetric numerical dataDistorts distribution
MedianSkewed numerical dataCan reduce variation
ModeCategorical dataCan introduce category bias
ConstantMeaningful defaultIncorrect if default has no meaning
Forward fillTime series/state dataCan propagate stale values
Backward fillTime seriesCan use future information
InterpolationContinuous time seriesAssumes smooth change
Group mean/medianDifferent group distributionsRequires sufficient group data
KNNStructured numerical dataMore computationally expensive
IterativeComplex relationshipsMore assumptions

6.4.43Missing Data Quality Report

A professional data-quality report should include

report = pd.DataFrame({
    "Column": df.columns,
    "Total_Rows": len(df),
    "Missing_Count": [
        df[col].isna().sum()
        for col in df.columns
    ],
    "Missing_Percentage": [
        df[col].isna().mean() * 100
        for col in df.columns
    ],
    "Non_Missing_Count": [
        df[col].notna().sum()
        for col in df.columns
    ]
})
print(report)

Example output

ColumnTotal RowsMissingMissing %
Customer_ID1000000.0%
Name10000120.12%
Age100004304.30%
Email10000120012.00%
Revenue10000500.50%

This immediately tells the data team where the biggest problems are.

6.4.44Practical Example — Customer Dataset

Consider

import pandas as pd
import numpy as np
df = pd.DataFrame({
    "Customer": [
        "Ravi", "Priya", "Arun",
        "Meena", "John"
    ],
    "Age": [
        30, np.nan, 35,
        40, np.nan
    ],
    "City": [
        "Hyderabad",
        "Bengaluru",
        np.nan,
        "Hyderabad",
        "Hyderabad"
    ],
    "Salary": [
        50000,
        60000,
        np.nan,
        70000,
        80000
    ]
})

Step 1 — Profile missing values

print(df.isna().sum())

Step 2 — Calculate percentages

print(
    df.isna().mean() * 100
)
  • Step 3 — Fill Age using median
  • df["Age"] = df["Age"].fillna(
  • df["Age"].median()

)

  • Step 4 — Fill Salary using median
  • df["Salary"] = df["Salary"].fillna(
  • df["Salary"].median()

)

  • Step 5 — Fill City with "Unknown"
  • df["City"] = df["City"].fillna(
  • "Unknown"

)

Step 6 — Verify

print(df.isna().sum())

Expected

Customer 0

Age 0

City 0

Salary 0

But remember: zero missing values does not automatically mean high-quality data. The imputation strategy itself must be appropriate.

6.4.45Advanced Example — Group-Based Imputation

Suppose

df = pd.DataFrame({
    "Department": [
        "IT", "IT", "IT",
        "HR", "HR", "HR"
    ],
    "Salary": [
        80000, np.nan, 90000,
        50000, np.nan, 55000
    ]
})

Using the overall median

df["Salary"] = df["Salary"].fillna(

df["Salary"].median()

)

may not preserve the difference between IT and HR salaries.

Instead

df["Salary"] = (

df.groupby("Department")["Salary"]

.transform(

lambda x: x.fillna(x.median())

)

)

Now missing salaries are estimated using the employee's department.

This is often a more meaningful strategy.

6.4.46Advanced Example — Time Series

Consider electricity consumption

df = pd.DataFrame({
    "Date": pd.date_range(
        "2025-01-01",
        periods=10
    ),
    "Consumption": [
        100, 110, 120,
        np.nan, 140, 150,
        np.nan, 170, 180, 190
    ]
})

Convert the date

df["Date"] = pd.to_datetime(

df["Date"]

)

Sort

df = df.sort_values("Date")

Interpolate

  • df["Consumption"] = (
  • df["Consumption"]
  • .interpolate()

)

Now the missing observations are estimated based on neighboring values.

6.4.47Best Practices

1. Understand why data is missing

Don't immediately fill it.

2. Analyze missingness before cleaning

Calculate

df.isna().sum()

and

df.isna().mean() * 100

3. Use domain knowledge

A missing Revenue field may mean something very different from a missing Middle_Name.

4. Don't confuse missing with zero

NaN ≠ 0

unless the business definition explicitly says so.

5. Preserve an audit trail

Record

  • which values were missing
  • how they were handled
  • how many records were affected
  • why the chosen method was used

6. Validate after imputation

Always compare

Before
Imputation
After

Check whether the distribution changed dramatically.

6.4.48Interview Questions

  • Basic
  • What is a missing value?
  • How do you identify missing values in Pandas?
  • What is the difference between NaN, None, pd.NA, and NaT?
  • How do you count missing values?
  • How do you calculate missing-value percentages?
  • How do you remove rows containing missing values?
  • How do you fill missing values?
  • What is forward fill?
  • What is backward fill?
  • What is interpolation?

Advanced

  • What is the difference between MCAR, MAR, and MNAR?
  • When would you use mean versus median imputation?
  • Why can mean imputation be problematic?
  • When should you use group-based imputation?
  • How would you handle missing values in a time series?
  • Why shouldn't missing values always be replaced with zero?
  • How can missingness itself be used as a feature?
  • What is data leakage during imputation?
  • How does SimpleImputer work?
  • What is KNN imputation?
  • How would you handle a column with 90% missing values?
  • How would you validate an imputation strategy?
  • How would you handle missing values differently for numerical and categorical columns?
  • How would you handle missing values in a production ETL pipeline?
  • How would you determine whether missing data indicates a source-system defect?

6.4.49Mini Project — Missing Value Analysis

Create a customer dataset containing

  • Customer_ID
  • Age
  • Gender
  • City
  • Income
  • Order_Count
  • Last_Order_Date
  • Intentionally introduce missing values.

Your project should

Step 1

Generate a missing-value report.

Step 2

Calculate missing percentage for every column.

Step 3

Identify columns with more than 30% missing values.

Step 4

Remove records where Customer_ID is missing.

Step 5

Use median imputation for Income.

Step 6

Use group-based median imputation for Income by City.

Step 7

Use mode or "Unknown" for Gender.

Step 8

Use a business-appropriate strategy for missing Last_Order_Date.

Step 9

Create missing-value indicator columns.

Step 10

Compare the dataset before and after treatment.

6.4.50Key Takeaways

The missing-value workflow is

RAW DATA

IDENTIFY MISSING VALUES

MEASURE MISSINGNESS

UNDERSTAND THE CAUSE

┌─────────┼─────────┐

▼ ▼ ▼

REMOVE IMPUTE RETAIN

│ │ │

└─────────┼─────────┘

VALIDATE RESULT

CLEAN DATA

  • Most important concepts

Focus especially on

  • isna() / notna()
  • Missing-value percentages
  • dropna() vs fillna()
  • Mean, median, and mode imputation
  • ffill() / bfill()
  • Interpolation
  • Group-based imputation
  • Missing-value indicators
  • MCAR, MAR, and MNAR
  • Avoiding data leakage during imputation

The most important principle is

There is no universally correct way to handle missing values. The right approach depends on why the value is missing, what the field represents, and how the cleaned data will be used.

Module 6 · Lesson 6.5

Outlier Detection

Outlier detection is the process of identifying observations that are unusually different from the majority of the data.

For example, suppose monthly customer purchases are

  • ₹1,200
  • ₹1,500
  • ₹1,800
  • ₹1,700
  • ₹1,600
  • ₹95,000

₹95,000 is an unusual observation compared with the other values.

However, an outlier is not automatically an error. It could represent

  • Data-entry error
  • Fraud
  • System malfunction
  • Exceptional customer
  • Large business transaction
  • Seasonal event
  • Genuine extreme observation

Therefore, the objective is not simply to remove outliers, but to detect, investigate, and decide how they should be treated.

6.5.1Learning Objectives

By the end of this topic, you should be able to

  • Understand what an outlier is.
  • Distinguish outliers from normal variation.
  • Detect outliers using statistical methods.
  • Use the IQR method.
  • Use Z-score detection.
  • Use percentile-based detection.
  • Use Boolean filtering in Pandas.
  • Visualize outliers with box plots and scatter plots.
  • Detect outliers within groups.
  • Understand the effect of outliers on statistics.
  • Decide whether to remove, cap, transform, or retain outliers.
  • Build an outlier-detection workflow.

6.5.2What Is an Outlier?

An outlier is an observation that lies unusually far from the typical pattern of a dataset.

Example

  • 10
  • 12
  • 11
  • 13
  • 15
  • 14
  • 12
  • 100

Here

100

is substantially different from the other values.

But consider

  • 10
  • 12
  • 11
  • 13
  • 15
  • 14
  • 12
  • 20

20 is larger, but it may not be sufficiently unusual to be considered an outlier.

Therefore, outlier detection requires a defined method.

6.5.3Types of Outliers

There are several ways to classify outliers.

1. Global Outlier

An observation that is unusual compared with the entire dataset.

Example

Sales = ₹10,000,000

when almost all other sales are below ₹100,000.

2. Local Outlier

An observation may be normal globally but unusual within a specific group.

Example

RegionSales
South100
South110
South105
North10,000
North11,000
North10,500
North100

100 may not look extreme globally, but it is unusual within the North region.

3. Contextual Outlier

An observation is unusual only in a particular context.

Example

Electricity consumption

Monday 2 PM → 500 KW

Monday 3 PM → 510 KW

Monday 4 PM → 505 KW

But

Sunday 2 AM → 500 KW

may be unusual because the expected consumption at that time is much lower.

4. Multivariate Outlier

A value may look normal individually but be unusual when multiple variables are considered together.

Example

Age = 25
Income = ₹20 lakh

Neither value may be extreme individually, but their combination may be unusual for the population being analyzed.

6.5.4Why Outliers Matter

Outliers can strongly influence

  • Mean
  • Standard deviation
  • Regression models
  • Correlation
  • Machine-learning algorithms
  • Forecasting
  • Business KPIs

Consider

  • 10
  • 20
  • 30
  • 40
  • 50
  • 1000

The mean becomes much larger because of 1000.

The median is much less affected.

This is why analysts often compare

df["Sales"].mean()

df["Sales"].median()

6.5.5Basic Data Inspection

Start with

import pandas as pd
import numpy as np
df = pd.DataFrame({
    "Sales": [
        100, 120, 130, 110,
        125, 140, 135, 5000
    ]
})
print(df.describe())

Look at

  • minimum
  • maximum
  • mean
  • median
  • standard deviation
  • quartiles

A very large difference between the median and maximum can indicate potential outliers.

6.5.6Visual Detection

Visualization is one of the easiest ways to understand outliers.

Important charts include

  • Box plot
  • Histogram
  • Scatter plot
  • Time-series plot

Using Matplotlib

import matplotlib.pyplot as plt
plt.boxplot(df["Sales"])
plt.title("Sales Distribution")
plt.show()

A box plot displays

Minimum

├── Q1

├── Median

├── Q3

└── Maximum

Values far outside the whiskers are potential outliers.

6.5.7Box Plot and IQR

The Interquartile Range (IQR) is one of the most widely used methods for outlier detection.

Define

IQR = Q3 - Q1

where

Q1 = 25th percentile
Q3 = 75th percentile

The standard boundaries are

  • Lower Bound = Q1 - 1.5 × IQR
  • Upper Bound = Q3 + 1.5 × IQR
  • Values outside these boundaries are considered potential outliers.

6.5.8IQR Method in Python

Suppose

sales = pd.Series([
    100, 120, 130, 110,
    125, 140, 135, 5000
])

Calculate Q1

Q1 = sales.quantile(0.25)

Calculate Q3

Q3 = sales.quantile(0.75)

Calculate IQR

IQR = Q3 - Q1

Calculate boundaries

lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR

Find outliers

outliers = sales[
    (sales < lower_bound) |
    (sales > upper_bound)
]
print(outliers)

6.5.9Complete IQR Function

Instead of repeating the calculation, create a function

def detect_iqr_outliers(series):
    Q1 = series.quantile(0.25)
    Q3 = series.quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    return series[
        (series < lower) |
        (series > upper)
    ]

Use it

outliers = detect_iqr_outliers(
    df["Sales"]
)
print(outliers)

6.5.10Understanding Quartiles

Suppose sorted data is

  • 10
  • 20
  • 30
  • 40
  • 50
  • 60
  • 70
  • 80
  • 90
  • 100

Quartiles divide the data approximately into four sections

Q1 Q2 Q3

│ │ │

--------|-----------|-----------|--------

25% 50% 75%

Where

Q1 = 25th percentile
Q2 = Median
Q3 = 75th percentile

The IQR covers the middle 50% of the observations.

6.5.11Why IQR Is Useful

IQR is popular because it is relatively robust against extreme values.

For example

  • 10
  • 11
  • 12
  • 13
  • 14
  • 10000

The extreme value 10000 has a strong impact on the mean and standard deviation, but the quartiles are much less affected.

Therefore, IQR is often a good first method for skewed business data.

6.5.12Z-Score Method

Another common method is the Z-score.

The Z-score measures how many standard deviations an observation is from the mean.

Formula

Z = (X - μ) / σ

where

X = observation

μ = mean

σ = standard deviation

For example

Z = 3

means the observation is approximately 3 standard deviations above the mean.

6.5.13Z-Score in Python

Using SciPy

from scipy.stats import zscore

df["Z_Score"] = zscore(

df["Sales"]

)

Find potential outliers

outliers = df[
    df["Z_Score"].abs() > 3
]

A common rule is

|Z| > 3 → potential outlier

But the threshold is not universal.

6.5.14Manual Z-Score Calculation

You can calculate it without SciPy

mean = df["Sales"].mean()
std = df["Sales"].std()

df["Z_Score"] = (

(df["Sales"] - mean) / std

)

Then

outliers = df[
    df["Z_Score"].abs() > 3
]

6.5.15IQR vs Z-Score

FeatureIQRZ-Score
Based onQuartilesMean & standard deviation
Sensitive to extreme valuesLowerHigher
Distribution assumptionLess restrictiveMore useful for approximately normal data
Good for skewed dataYesLess suitable
Easy to interpretYesYes
Common threshold1.5 × IQR`

General guideline

  • Skewed / non-normal data → IQR
  • Approximately normal data → Z-score
  • But always consider the business context.

6.5.16Percentile-Based Detection

Another simple method is to define acceptable percentiles.

For example

lower = df["Sales"].quantile(0.01)
upper = df["Sales"].quantile(0.99)

Find observations outside the range

outliers = df[
    (df["Sales"] < lower) |
    (df["Sales"] > upper)
]

This identifies approximately the lowest 1% and highest 1%.

This approach is useful when business rules naturally focus on extreme tails.

6.5.17Winsorization

Instead of removing extreme values, you can cap them.

Suppose

lower = df["Sales"].quantile(0.01)
upper = df["Sales"].quantile(0.99)

Then

  • df["Sales_Capped"] = (
  • df["Sales"]
  • .clip(lower, upper)

)

Values below the lower limit become the lower limit, and values above the upper limit become the upper limit.

This is called winsorization/capping.

6.5.18Removing Outliers

If an outlier is confirmed to be erroneous

df_clean = df[
    (df["Sales"] >= lower_bound) &
    (df["Sales"] <= upper_bound)
]

But don't do this automatically.

For example

  • ₹10,000
  • ₹12,000
  • ₹11,000
  • ₹15,000
  • ₹1,000,000

The ₹1,000,000 transaction could be a genuine enterprise customer order.

Removing it would distort the business analysis.

6.5.19Outlier Treatment Strategies

  • Once an outlier is detected, you have several options.
  • Option 1 — Keep it
  • Use this when it represents a genuine observation.
  • Option 2 — Remove it
  • Use this when it is confirmed to be invalid.
  • Option 3 — Correct it
  • If the source value is incorrect and the correct value can be established.
  • Option 4 — Cap it
  • Replace extreme values with percentile limits.
  • Option 5 — Transform it

For example, use

np.log1p(df["Sales"])

Option 6 — Analyze separately

For example

Normal customers

VIP customers

Instead of treating them as one population.

6.5.20Log Transformation

Highly skewed data can sometimes be transformed using logarithms.

Example

df["Log_Sales"] = np.log1p(

df["Sales"]

)

log1p(x) calculates

log(1 + x)

and is useful when zero values are present.

Log transformation can reduce the influence of extremely large values.

6.5.21Outlier Detection by Group

This is an important advanced concept.

Suppose

df = pd.DataFrame({
    "Region": [
        "South", "South", "South",
        "North", "North", "North"
    ],
    "Sales": [
        100, 110, 105,
        10000, 11000, 50
    ]
})
  • 50 might be a local outlier in the North region.
  • A global analysis might not identify it correctly.
  • Use group-specific IQR rules.

6.5.22Group-Based IQR

def mark_outliers(group):
Q1 = group["Sales"].quantile(0.25)
Q3 = group["Sales"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
  • group["Outlier"] = (
  • (group["Sales"] < lower) |
  • (group["Sales"] > upper)

)

return group

Apply

df = (
    df.groupby("Region", group_keys=False)
    .apply(mark_outliers)
)

This detects outliers within each region.

6.5.23Outliers in Time Series

Time-series outliers require special attention.

Consider

  • Jan → 100
  • Feb → 105
  • Mar → 110
  • Apr → 108
  • May → 500
  • Jun → 115
  • May could be an outlier.

But perhaps May represents

  • Festival demand
  • Marketing campaign
  • Product launch
  • System issue
  • Seasonal event

A simple IQR calculation may detect May but cannot explain why it occurred.

Time-series analysis should consider

  • trend
  • seasonality
  • calendar events
  • business events
  • historical patterns

6.5.24Rolling Outlier Detection

  • For time-series data, rolling statistics can be useful.
  • df["RollingMean"] = (
  • df["Sales"]
  • .rolling(7)
  • .mean()

)

  • df["RollingStd"] = (
  • df["Sales"]
  • .rolling(7)

.std()

)

Calculate a rolling upper threshold

df["Upper"] = (

df["RollingMean"] +

3 * df["RollingStd"]

)

Then

df["Outlier"] = (

df["Sales"] > df["Upper"]

)

This adapts the threshold based on recent observations.

6.5.25Scatter Plot for Multivariate Outliers

Suppose

plt.scatter(
    df["Income"],
    df["Spending"]
)
plt.xlabel("Income")
plt.ylabel("Spending")
plt.title("Income vs Spending")
plt.show()

An observation far away from the main cluster may be a potential multivariate outlier.

6.5.26Correlation and Outliers

Outliers can dramatically affect correlation.

Suppose

  • X: 10, 20, 30, 40, 50
  • Y: 12, 22, 32, 42, 52
  • The relationship is strongly positive.

But one extreme point

X = 1000
Y = 10

can dramatically change the correlation.

Therefore, always inspect scatter plots when analyzing relationships between variables.

6.5.27Effect on Mean and Median

Consider

data = pd.Series([
    10, 12, 14, 15, 16, 1000
])
print("Mean:", data.mean())
print("Median:", data.median())
  • The mean is pulled toward the extreme value.
  • The median remains much more stable.
  • This demonstrates why median is often preferred for highly skewed data.

6.5.28Effect on Standard Deviation

Standard deviation is also highly affected by extreme observations.

Without outlier

  • 10
  • 12
  • 14
  • 15
  • 16

With outlier

  • 10
  • 12
  • 14
  • 15
  • 16
  • 1000
  • The standard deviation increases substantially.

This is one reason why the Z-score method can be affected by extreme observations.

6.5.29Robust Statistics

Robust statistics are less sensitive to extreme observations.

Examples

  • Median
  • IQR
  • MAD
  • MAD means Median Absolute Deviation.

Conceptually

MAD = median(
    |x - median(x)|
)

A robust z-score can be based on MAD rather than the standard deviation.

This can be useful when the dataset itself contains strong outliers.

6.5.30MAD Example

median = df["Sales"].median()
mad = (
    df["Sales"] - median
).abs().median()
robust_z = (
    0.6745 *
    (df["Sales"] - median) /
    mad
)

Potential outliers can then be identified using a threshold such as

outliers = df[
    robust_z.abs() > 3.5
]

The exact threshold should be chosen based on the analytical context.

6.5.31Outlier Detection with Isolation Forest

For more advanced machine-learning workflows, Isolation Forest can detect unusual observations.

from sklearn.ensemble import IsolationForest
model = IsolationForest(
    contamination=0.05,
    random_state=42
)

df["Outlier"] = model.fit_predict(

df[["Sales"]]

)

The result is typically

1 → normal

-1 → potential outlier

For multiple variables

features = [
    "Income",
    "Spending",
    "Purchase_Count"
]

df["Outlier"] = model.fit_predict(

df[features]

)

This can identify multivariate anomalies that simple one-variable rules may miss.

6.5.32IQR vs Z-Score vs Isolation Forest

MethodBest Use
IQRUnivariate, skewed data
Z-scoreApproximately normal data
PercentilesExtreme-tail detection
MADRobust statistical detection
Rolling statisticsTime-series anomalies
Isolation ForestMultivariate anomaly detection

No single method is universally best.

6.5.33Practical Example — Employee Salary

Suppose

df = pd.DataFrame({
    "Employee": [
        "A", "B", "C", "D",
        "E", "F", "G"
    ],
    "Salary": [
        50000, 55000, 60000,
        58000, 62000, 65000,
        500000
    ]
})

Step 1 — Calculate quartiles

Q1 = df["Salary"].quantile(0.25)
Q3 = df["Salary"].quantile(0.75)

Step 2 — Calculate IQR

IQR = Q3 - Q1

Step 3 — Calculate bounds

lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
  • Step 4 — Mark outliers
  • df["Outlier"] = (
  • (df["Salary"] < lower) |
  • (df["Salary"] > upper)

)

Step 5 — Investigate

print(
    df[df["Outlier"]]
)

If employee G is actually a CEO, removing the record would be incorrect.

6.5.34Practical Example — Sales Transactions

Suppose a company has

df = pd.DataFrame({
    "OrderID": range(1, 11),
    "Sales": [
        1000, 1200, 900,
        1500, 1300, 1100,
        1250, 1400, 10000,
        1150
    ]
})

Detect IQR outliers

Q1 = df["Sales"].quantile(0.25)
Q3 = df["Sales"].quantile(0.75)
IQR = Q3 - Q1
lower = Q1 - 1.5 * IQR
upper = Q3 + 1.5 * IQR
  • df["Outlier"] = (
  • (df["Sales"] < lower) |
  • (df["Sales"] > upper)

)

Then investigate

print(
    df[df["Outlier"]]
)

Instead of immediately deleting the transaction, check the source system.

6.5.35Outlier Investigation Workflow

A professional process looks like

RAW DATA

Detect Potential

Outliers

Statistical Method

Business Validation

┌──────────┼──────────┐

▼ ▼ ▼

Genuine Error Unknown

│ │ │

▼ ▼ ▼

Keep Correct Investigate

Validate

Final Dataset

6.5.36Never Automatically Delete Outliers

This is perhaps the most important lesson.

Suppose your sales data contains

  • ₹1,000
  • ₹1,200
  • ₹900
  • ₹1,100
  • ₹1,300
  • ₹500,000

The last value may be flagged as an outlier.

But it could be

  • a bulk corporate order
  • an annual contract
  • a genuine high-value customer

If you remove it, you could significantly understate total revenue.

Therefore

Outlier detection identifies observations for investigation; it does not automatically prove that they are wrong.

6.5.37Data Quality Rules for Outliers

A production pipeline can define rules such as

Sales < 0 → Invalid

Quantity < 0 → Invalid

Discount > 100% → Invalid

Age < 0 → Invalid

Age > 120 → Suspicious

Transaction > P99 → Investigate

Then classify records

  • VALID
  • SUSPICIOUS
  • INVALID

This is often better than simply deleting all extreme values.

6.5.38Outlier Report

A useful outlier report can contain

ColumnOutlier CountOutlier %Lower BoundUpper Bound
Sales250.25%1008,500
Quantity120.12%1150
Discount50.05%00.80

You can create this programmatically

def iqr_summary(df, column):
    Q1 = df[column].quantile(0.25)
    Q3 = df[column].quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - 1.5 * IQR
    upper = Q3 + 1.5 * IQR
    mask = (
        (df[column] < lower) |
        (df[column] > upper)
    )
return {
    "Column": column,
    "Outlier_Count": mask.sum(),
    "Outlier_Percentage": mask.mean() * 100,
    "Lower_Bound": lower,
    "Upper_Bound": upper
}

6.5.39Mini Project — Customer Transaction Outlier Detection

Create a dataset

  • Transaction_ID
  • Customer_ID
  • Transaction_Date
  • Region
  • Product
  • Quantity
  • Unit_Price
  • Total_Amount
  • Perform the following analysis.
  • Step 1 — Data profiling

Inspect

df.info()
df.describe()
  • Step 2 — Detect negative values
  • df[
  • df["Total_Amount"] < 0

]

Step 3 — Detect IQR outliers

Apply the IQR method to

  • Quantity
  • Unit_Price
  • Total_Amount
  • Step 4 — Detect Z-score outliers

Calculate

  • Z-score > 3
  • Z-score < -3
  • Step 5 — Compare methods

Determine which transactions are identified by

  • IQR only
  • Z-score only
  • Both
  • Step 6 — Visualize

Create

  • Box plot
  • Histogram
  • Scatter plot
  • Step 7 — Investigate

Classify each potential outlier

  • Genuine
  • Data Error
  • Business Exception
  • Unknown
  • Step 8 — Treatment

Apply the appropriate action

  • Keep
  • Correct
  • Remove
  • Cap
  • Transform
  • Investigate
  • Step 9 — Generate a final report

Include

  • Total Records
  • Potential Outliers
  • Confirmed Errors
  • Genuine Extreme Values
  • Removed Records
  • Corrected Records
  • Remaining Outliers

6.5.40Interview Questions

  • Basic
  • What is an outlier?
  • Why are outliers important?
  • What is the difference between an outlier and an error?
  • What is the IQR?
  • What are Q1 and Q3?
  • How is the IQR calculated?
  • What is a box plot?
  • What is a Z-score?
  • What is the common Z-score threshold for outlier detection?
  • How do you find outliers using Pandas?

Advanced

  • Why is IQR often preferred for skewed data?
  • Why can Z-score detection be affected by outliers?
  • What is the difference between global and local outliers?
  • What is a contextual outlier?
  • What is a multivariate outlier?
  • How would you detect outliers within groups?
  • How would you detect outliers in a time series?
  • What is winsorization?
  • When should you remove an outlier?
  • When should you keep an outlier?
  • What is MAD?
  • How does Isolation Forest detect anomalies?
  • How can outliers affect correlation?
  • How can outliers affect machine-learning models?
  • How would you design an automated outlier-monitoring process?

6.5.41Key Takeaways

The complete outlier-detection workflow is

DATA

PROFILE

VISUALIZE DATA

DETECT POTENTIAL

OUTLIERS

┌─────────────┼─────────────┐

▼ ▼ ▼

IQR Z-SCORE ML METHODS

│ │ │

└─────────────┼─────────────┘

BUSINESS REVIEW

┌────────┼────────┐

▼ ▼ ▼

Keep Correct Remove

│ │ │

└────────┼────────┘

VALIDATE

CLEAN DATA

  • Concepts to master

Focus especially on

  • IQR method
  • Z-score method
  • Box plots
  • Percentile-based detection
  • Outlier treatment strategies
  • Group-level outlier detection
  • Time-series outliers
  • Multivariate outliers
  • Robust statistics
  • Business validation

The central principle is

An outlier is a signal to investigate, not a command to delete.

In professional data analysis, the strongest workflow is detect → investigate → classify → treat → validate.

Module 6 · Lesson 6.6

Feature Engineering

Feature engineering is the process of creating, modifying, selecting, and transforming variables so that data contains useful information for analysis, visualization, statistical modeling, and machine learning.

A feature is simply an input variable.

For example, from

  • Order_Date
  • Quantity
  • Unit_Price
  • Discount

we can create

  • Year
  • Month
  • Day_of_Week
  • Revenue
  • Discount_Amount
  • Net_Revenue

These derived features can make patterns much easier to analyze.

6.6.1Learning Objectives

By the end of this topic, you should be able to

  • Understand what features are.
  • Create new numerical features.
  • Create categorical features.
  • Extract features from dates.
  • Create ratio and percentage features.
  • Create aggregation-based features.
  • Encode categorical variables.
  • Create bins and segments.
  • Create interaction features.
  • Handle skewed variables.
  • Select useful features.
  • Avoid data leakage.
  • Build reusable feature-engineering pipelines.

6.6.2What Is a Feature?

Consider a customer dataset

Customer_IDAgeIncomeOrders
10125500005
102409000012
10332700008

Existing features are

  • Age
  • Income
  • Orders

We can create

  • Income_Per_Order
  • Customer_Segment
  • Age_Group

For example

  • df["Income_Per_Order"] = (
  • df["Income"] /
  • df["Orders"]

)

This new feature may provide more analytical value than either variable alone.

6.6.3Why Feature Engineering Matters

Raw data is not always in the most useful form.

Consider

OrderDate = 2026-08-23

The date itself may not directly tell us whether the order occurred on

  • a weekend
  • a holiday
  • a month-end
  • a quarter-end

We can create

Year = 2026
Month = 8
Quarter = 3
DayOfWeek = Sunday
IsWeekend = True
IsMonthEnd = False

These features can reveal patterns that aren't obvious in the raw data.

6.6.4Types of Feature Engineering

Common categories include

Numerical Features
Categorical Features
Date/Time Features
Aggregation Features
Ratio Features
Text Features
Interaction Features
Binning
Encoding
Transformation

6.6.5Creating Simple Numerical Features

Suppose

df = pd.DataFrame({
    "Quantity": [2, 5, 3, 10],
    "Unit_Price": [100, 200, 150, 50]
})

Create revenue

  • df["Revenue"] = (
  • df["Quantity"] *
  • df["Unit_Price"]

)

Result

QuantityUnit_PriceRevenue
2100200
52001000
3150450
1050500

This is one of the simplest forms of feature engineering.

6.6.6Business Features

Suppose an order contains

Revenue

Cost

Create profit

  • df["Profit"] = (
  • df["Revenue"] -
  • df["Cost"]

)

Profit margin

  • df["Profit_Margin"] = (
  • df["Profit"] /
  • df["Revenue"] * 100

)

This converts raw financial data into a meaningful business KPI.

6.6.7Ratio Features

Ratios can reveal relationships between variables.

Example

  • df["Revenue_Per_Order"] = (
  • df["Revenue"] /
  • df["Order_Count"]

)

Customer value

  • df["Revenue_Per_Customer"] = (
  • df["Revenue"] /
  • df["Customer_Count"]

)

Conversion rate

  • df["Conversion_Rate"] = (
  • df["Conversions"] /
  • df["Visitors"] * 100

)

Always handle division by zero

  • df["Conversion_Rate"] = np.where(
  • df["Visitors"] > 0,
  • df["Conversions"] /
  • df["Visitors"] * 100,
  • 0

)

6.6.8Percentage Change Features

Suppose monthly revenue is

  • df["Revenue_Growth"] = (
  • df["Revenue"]
  • .pct_change() * 100

)

Example

January → 10000

February → 12000

Growth

20%

This is useful for

  • revenue growth
  • customer growth
  • traffic growth
  • operational metrics

6.6.9Difference Features

Create absolute change

  • df["Revenue_Change"] = (
  • df["Revenue"]
  • .diff()

)

For example

  • 10000
  • 12000
  • 15000

becomes

  • NaN
  • 2000
  • 3000

6.6.10Date-Based Feature Engineering

Date features are extremely important.

Suppose

df["OrderDate"] = pd.to_datetime(

df["OrderDate"]

)

Create year

df["Year"] = (

df["OrderDate"].dt.year

)

Month

df["Month"] = (

df["OrderDate"].dt.month

)

Quarter

df["Quarter"] = (

df["OrderDate"].dt.quarter

)

Day

df["Day"] = (

df["OrderDate"].dt.day

)

6.6.11Day-of-Week Features

df["DayOfWeek"] = (

df["OrderDate"].dt.dayofweek

)

Values

Monday → 0

Tuesday → 1

Wednesday → 2

Thursday → 3

Friday → 4

Saturday → 5

Sunday → 6

Get the name

df["DayName"] = (

df["OrderDate"].dt.day_name()

)

6.6.12Weekend Feature

Create a Boolean feature

df["IsWeekend"] = (

df["OrderDate"].dt.dayofweek >= 5

)

Or numeric

  • df["IsWeekend"] = (
  • df["OrderDate"].dt.dayofweek >= 5
  • ).astype(int)

Now

0 → Weekday

1 → Weekend

6.6.13Month-End and Month-Start Features

df["IsMonthEnd"] = (

df["OrderDate"].dt.is_month_end

)

df["IsMonthStart"] = (

df["OrderDate"].dt.is_month_start

)

These can be useful for financial and operational analysis.

6.6.14Customer Age from Date of Birth

Suppose

df["DOB"] = pd.to_datetime(

df["DOB"]

)

Calculate approximate age

today = pd.Timestamp.today()

df["Age"] = (

today.year -

df["DOB"].dt.year

)

For more accurate age calculation

df["Age"] = (

today.year -

df["DOB"].dt.year -

(

(today.month, today.day) <

(df["DOB"].dt.month, df["DOB"].dt.day)

)

)

In production code, date handling should be carefully validated around birthdays and missing dates.

6.6.15Customer Tenure

Suppose

Registration_Date

Create tenure in days

df["Tenure_Days"] = (

pd.Timestamp.today() -
df["Registration_Date"]

).dt.days

Tenure in years

df["Tenure_Years"] = (

df["Tenure_Days"] / 365.25

)

This can be useful for customer segmentation and retention analysis.

6.6.16Age Grouping

Suppose

df["Age"]

contains individual ages.

Create groups

bins = [
    0, 18, 30, 45, 60, 100
]
labels = [
    "Under 18",
    "18-30",
    "31-45",
    "46-60",
    "60+"
]

df["Age_Group"] = pd.cut(

df["Age"],

bins=bins,
labels=labels

)

This transforms continuous age into a categorical feature.

6.6.17Income Segmentation

df["Income_Group"] = pd.cut(

df["Income"],

bins=[
    0,
    30000,
    60000,
    100000,
    float("inf")
],
labels=[
    "Low",
    "Medium",
    "High",
    "Very High"
]

)

This can make business reports easier to understand.

6.6.18Quantile-Based Segmentation

Instead of fixed ranges, use qcut()

df["Income_Quartile"] = pd.qcut(

df["Income"],

q=4,
labels=[
    "Q1",
    "Q2",
    "Q3",
    "Q4"
]

)

This creates groups containing approximately equal numbers of records.

6.6.19Categorical Encoding

Machine-learning algorithms often require numerical representations of categories.

Suppose

  • df["Gender"] = [
  • "Male",
  • "Female",
  • "Female",
  • "Male"

]

Label encoding

from sklearn.preprocessing import LabelEncoder
encoder = LabelEncoder()
  • df["Gender_Encoded"] = (
  • encoder.fit_transform(
  • df["Gender"]

)

)

Potential result

Female → 0

Male → 1

However, label encoding can incorrectly imply an ordering when categories are nominal.

6.6.20One-Hot Encoding

For nominal categories

df = pd.get_dummies(
    df,
    columns=["Gender"]
)

Example

  • Gender
  • Male
  • Female
  • Female

becomes something like

  • Gender_Female
  • Gender_Male
  • with binary values.

6.6.21Avoiding the Dummy Variable Trap

For some modeling algorithms, you may drop one category

df = pd.get_dummies(
    df,
    columns=["Gender"],
    drop_first=True
)

This reduces redundant information when using models where perfect multicollinearity is a concern.

6.6.22Frequency Encoding

For high-cardinality categorical variables, frequency encoding can be useful.

Suppose

  • City
  • Hyderabad
  • Hyderabad
  • Bengaluru
  • Chennai

Calculate frequency

frequency = (
    df["City"]
    .value_counts(normalize=True)
)

df["City_Frequency"] = (

df["City"].map(frequency)

)

Example

Hyderabad → 0.50

Bengaluru → 0.25

Chennai → 0.25

6.6.23Target Encoding

Target encoding replaces categories with statistics based on the target variable.

Example

City Average Revenue

Hyderabad 50000

Bengaluru 65000

Chennai 45000

This can be powerful but has a major risk

Target encoding can cause data leakage if calculated using information from validation/test records.

For machine learning, target encoding must be performed carefully, usually using training-only information or cross-validation schemes.

6.6.24Aggregation Features

Aggregation is one of the most powerful feature-engineering techniques.

Suppose transaction data contains

  • Customer_ID
  • Order_ID
  • Revenue

Calculate total customer revenue

customer_revenue = (
    df.groupby("Customer_ID")["Revenue"]
    .transform("sum")
)

df["Customer_Total_Revenue"] = (

customer_revenue

)

6.6.25Customer-Level Features

From transaction data, create

  • Total Revenue
  • Number of Orders
  • Average Order Value
  • Maximum Order Value
  • Minimum Order Value
  • Last Purchase Date
  • Days Since Last Purchase

Example

customer_features = (
    df.groupby("Customer_ID")
    .agg(
        Total_Revenue=("Revenue", "sum"),
        Order_Count=("Order_ID", "nunique"),
        Average_Order_Value=("Revenue", "mean"),
        Max_Order_Value=("Revenue", "max")
    )
    .reset_index()
)

These are highly valuable for customer analytics.

6.6.26RFM Features

A classic customer feature-engineering approach is RFM

  • R → Recency
  • F → Frequency
  • M → Monetary Value
  • Recency
  • How recently did the customer purchase?
reference_date = df["OrderDate"].max()
recency = (
    df.groupby("Customer_ID")["OrderDate"]
    .max()
    .apply(

lambda x

(reference_date - x).days

)

)

Frequency

Number of orders

frequency = (
df.groupby("Customer_ID")
\["Order_ID"\]

.nunique()

)

Monetary

Total revenue

monetary = (
df.groupby("Customer_ID")
\["Revenue"\]

.sum()

)

RFM is widely used in

  • customer segmentation
  • marketing
  • churn analysis
  • customer-value analysis

6.6.27Rolling Features

For time-series data, rolling statistics can become features.

Suppose

  • df["Sales_7D_Avg"] = (
  • df["Sales"]
  • .rolling(7)
  • .mean()

)

Other features

  • df["Sales_7D_Max"] = (
  • df["Sales"]
  • .rolling(7)

.max()

)

  • df["Sales_7D_Min"] = (
  • df["Sales"]
  • .rolling(7)

.min()

)

These features capture recent behavior.

6.6.28Lag Features

  • Lag features use previous observations.
  • df["Sales_Lag_1"] = (
  • df["Sales"].shift(1)

)

Two-period lag

df["Sales_Lag_2"] = (

df["Sales"].shift(2)

)

These are extremely useful for forecasting.

6.6.29Lead Features

A lead feature looks into the next observation

df["Sales_Next"] = (

df["Sales"].shift(-1)

)

Be careful with lead features in predictive models because future information can easily cause data leakage.

6.6.30Interaction Features

Sometimes the combination of two features is more useful than either alone.

Suppose

df["Age_Income"] = (

df["Age"] *

df["Income"]

)

Or

  • df["Price_Quantity"] = (
  • df["Price"] *
  • df["Quantity"]

)

For categorical variables, interaction terms can represent combinations such as

Region × Product

These can capture relationships that individual features cannot.

6.6.31Polynomial Features

For numerical variables, you can create powers

df["Income_Squared"] = (

df["Income"] ** 2

)

Using Scikit-learn

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(
    degree=2,
    include_bias=False
)
X_poly = poly.fit_transform(X)

Polynomial features can help models capture nonlinear relationships.

However, they can dramatically increase the number of features.

6.6.32Log Transformation

For highly skewed variables

df["Log_Income"] = np.log1p(

df["Income"]

)

This can reduce the influence of extremely large values.

Common candidates

  • Income
  • Revenue
  • Population
  • Transaction amount
  • Customer lifetime value

6.6.33Binning

Binning converts continuous variables into groups.

Example

df["Sales_Band"] = pd.cut(

df["Sales"],

bins=[
    0, 1000, 5000,
    10000, float("inf")
],
labels=[
    "Low",
    "Medium",
    "High",
    "Very High"
]

)

This is useful for

  • customer segmentation
  • reporting
  • visualization
  • rule-based classification

6.6.34Text Feature Engineering

Text can also be converted into useful numerical features.

Suppose

  • df["Review"] = [
  • "Excellent product",
  • "Good product",
  • "Very poor service"

]

Create text length

df["Review_Length"] = (

df["Review"].str.len()

)

Word count

  • df["Word_Count"] = (
  • df["Review"]
  • .str.split()
  • .str.len()

)

Contains a keyword

  • df["Contains_Poor"] = (
  • df["Review"]
  • .str.contains(
  • "poor",
case=False,
na=False

)

)

More advanced NLP feature engineering can use

  • TF-IDF
  • word embeddings
  • sentence embeddings
  • sentiment scores

6.6.35Feature Engineering from File or System Metadata

Sometimes useful features don't come directly from business columns.

Examples

  • Source_System
  • Load_Date
  • File_Name
  • Batch_ID
  • Data_Source
  • Record_Created_Date

For example

df["Load_Date"] = pd.Timestamp.today()

These features are useful for

  • data lineage
  • data-quality monitoring
  • ETL troubleshooting
  • auditability

6.6.36Handling Division by Zero

Suppose

  • df["Revenue_Per_Order"] = (
  • df["Revenue"] /
  • df["Order_Count"]

)

If Order_Count = 0, you may get infinity or invalid results.

Better

  • df["Revenue_Per_Order"] = np.where(
  • df["Order_Count"] > 0,
  • df["Revenue"] /
  • df["Order_Count"],
np.nan

)

Then handle the resulting missing values appropriately.

6.6.37Feature Selection

Creating hundreds of features does not necessarily improve a model.

You may need to remove

  • irrelevant features
  • duplicate features
  • constant features
  • highly correlated features
  • leakage features
  • unstable features

Check correlation

correlation = (
    df.select_dtypes(
        include="number"
    ).corr()
)

Highly correlated features may provide redundant information depending on the model and objective.

6.6.38Removing Constant Features

constant_features = [
    column
    for column in df.columns
    if df[column].nunique() <= 1
]

These features contain no variation and generally provide little predictive value.

6.6.39Feature Leakage

Data leakage occurs when a feature contains information that would not actually be available at prediction time.

Example

Suppose you're predicting whether an order will be refunded.

This would be dangerous

  • Order_Date
  • Customer_ID
  • Payment_Method
  • Refund_Date

Refund_Date is only known after the refund occurs.

Using it to predict the refund would leak future information.

6.6.40Another Leakage Example

Suppose you want to predict customer churn.

You create

Last_30_Days_Orders

If the prediction is made at the beginning of the month, but your feature includes orders from the future, you've introduced leakage.

Correct approach

Prediction Date
Features available before prediction
Future outcome

Not

Prediction Date
Future information
Prediction

6.6.41Train/Test Feature Engineering

Correct machine-learning workflow

Raw Data
Train / Validation / Test Split
Fit transformations on Training Data
Apply transformations to Validation/Test
Train Model

For example, don't calculate a global mean using the entire dataset before splitting if that mean will be used to construct a predictive feature.

6.6.42Feature Engineering Pipeline with Scikit-Learn

For reproducible ML workflows, use pipelines.

from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
numeric_pipeline = Pipeline([
    (
        "imputer",
        SimpleImputer(strategy="median")
    ),
    (
        "scaler",
        StandardScaler()
    )
])

Categorical pipeline

from sklearn.preprocessing import OneHotEncoder
categorical_pipeline = Pipeline([
    (
        "imputer",
        SimpleImputer(
            strategy="most_frequent"
        )
    ),
    (
        "encoder",
        OneHotEncoder(
            handle_unknown="ignore"
        )
    )
])

These pipelines help prevent inconsistent preprocessing and data leakage.

6.6.43ColumnTransformer

For datasets containing both numerical and categorical variables

from sklearn.compose import ColumnTransformer
numeric_features = [
    "Age",
    "Income",
    "Order_Count"
]
categorical_features = [
    "Gender",
    "City"
]
preprocessor = ColumnTransformer([
    (
        "num",
        numeric_pipeline,
        numeric_features
    ),
    (
        "cat",
        categorical_pipeline,
        categorical_features
    )
])

This allows different feature-engineering strategies for different data types.

6.6.44Complete Business Example

Consider an e-commerce dataset

df = pd.DataFrame({
    "Customer_ID": [1, 1, 2, 2, 3],
    "Order_Date": [
        "2026-01-10",
        "2026-02-15",
        "2026-02-20",
        "2026-03-10",
        "2026-03-15"
    ],
    "Quantity": [2, 3, 1, 5, 2],
    "Unit_Price": [
        500, 400, 1000, 200, 800
    ]
})

Convert date

df["Order_Date"] = pd.to_datetime(

df["Order_Date"]

)

Create revenue

  • df["Revenue"] = (
  • df["Quantity"] *
  • df["Unit_Price"]

)

Create date features

  • df["Year"] = df["Order_Date"].dt.year
  • df["Month"] = df["Order_Date"].dt.month
  • df["DayOfWeek"] = (
  • df["Order_Date"].dt.dayofweek

)

df["IsWeekend"] = (

df["Order_Date"].dt.dayofweek >= 5

)

Customer-level features

df["Customer_Total_Revenue"] = (

df.groupby("Customer_ID")
\["Revenue"\]

.transform("sum")

)

Order count

df["Customer_Order_Count"] = (

df.groupby("Customer_ID")
\["Customer_ID"\]

.transform("count")

)

Average order value

  • df["Customer_AOV"] = (
  • df["Customer_Total_Revenue"] /
  • df["Customer_Order_Count"]

)

This transforms a basic transaction table into a much richer analytical dataset.

6.6.45Feature Engineering Workflow

A professional workflow is

RAW DATA

DATA CLEANING

UNDERSTAND BUSINESS

OBJECTIVE

CREATE FEATURES

┌────────────┼────────────┐

▼ ▼ ▼

Numerical Date Categorical

│ │ │

└────────────┼────────────┘

AGGREGATION

TRANSFORMATION

FEATURE SELECTION

LEAKAGE VALIDATION

FINAL FEATURES

MODEL / EDA / BI

6.6.46Feature Engineering vs Data Cleaning

  • These concepts are closely related but different.
  • Data Cleaning
  • Fixes problems.
  • Invalid age → missing
  • "hyderabad" → "Hyderabad"
  • Duplicate → remove
  • "₹50,000" → 50000
  • Feature Engineering
  • Creates useful information.
  • Age → Age_Group
  • Quantity × Price → Revenue
  • OrderDate → Month
  • Revenue / Orders → AOV
  • Last_Order_Date → Recency

A typical workflow is

Raw Data
Cleaning
Feature Engineering
Analysis / Modeling

6.6.47Common Feature Engineering Mistakes

  • Mistake 1 — Creating features without a business purpose
  • More features do not automatically mean better analysis.
  • Mistake 2 — Data leakage
  • Using future information creates unrealistic model performance.
  • Mistake 3 — Ignoring missing values
  • Feature calculations can propagate missing values.

For example

  • df["Revenue"] = (
  • df["Quantity"] *
  • df["Price"]

)

  • If either input is missing, revenue may also become missing.
  • Mistake 4 — Division by zero
  • Revenue / Orders
  • must handle zero orders.
  • Mistake 5 — Creating features before sorting time-series data

For lag and rolling calculations

df = df.sort_values(
\["Customer_ID", "Order_Date"\]

)

should generally happen before applying temporal calculations.

Mistake 6 — Using future information in lag/rolling features

Always define the exact prediction time and ensure every feature was available at that time.

6.6.48Feature Engineering Cheat Sheet

  • # Arithmetic
  • df["Revenue"] = df["Quantity"] * df["Price"]
  • # Ratios
  • df["AOV"] = df["Revenue"] / df["Orders"]
  • # Difference
  • df["Change"] = df["Sales"].diff()
  • # Percentage change
  • df["Growth"] = df["Sales"].pct_change()
  • # Date
  • df["Year"] = df["Date"].dt.year
  • df["Month"] = df["Date"].dt.month
  • df["Quarter"] = df["Date"].dt.quarter
  • df["Day"] = df["Date"].dt.day
  • df["DayOfWeek"] = df["Date"].dt.dayofweek
  • # Weekend
  • df["IsWeekend"] = (
  • df["Date"].dt.dayofweek >= 5

)

# Binning

df["Age_Group"] = pd.cut(

df["Age"],

bins=[0, 18, 30, 45, 60, 100]

)

# One-hot encoding

pd.get_dummies(df, columns=["City"])

# Aggregation

df["Customer_Revenue"] = (

df.groupby("Customer_ID")
\["Revenue"\]

.transform("sum")

)

# Ranking

df["Rank"] = df["Revenue"].rank(

ascending=False

)

  • # Lag
  • df["Previous_Sales"] = (
  • df["Sales"].shift(1)

)

  • # Rolling
  • df["Rolling_Avg"] = (
  • df["Sales"]
  • .rolling(7)
  • .mean()

)

  • # Log transformation
  • df["Log_Revenue"] = np.log1p(
  • df["Revenue"]

)

6.6.49Interview Questions

  • Basic
  • What is feature engineering?
  • What is a feature?
  • Why is feature engineering important?
  • Give examples of derived features.
  • How can you create features from dates?
  • How do you create categorical bins?
  • What is one-hot encoding?
  • What is label encoding?
  • What is a ratio feature?
  • What is an interaction feature?

Advanced

  • What is feature leakage?
  • How do you create customer-level aggregation features?
  • What are RFM features?
  • How do you create lag features?
  • How do rolling features work?
  • What is target encoding?
  • What problems can high-cardinality categorical variables cause?
  • When would you use frequency encoding?
  • How do you handle division by zero during feature creation?
  • Why should feature engineering be performed carefully with time-series data?
  • How do you prevent leakage when creating target-based features?
  • What is the difference between feature selection and feature engineering?
  • How can feature engineering improve machine-learning performance?
  • How would you design a reusable feature-engineering pipeline?
  • How would you decide whether a newly engineered feature is actually useful?

6.6.50Mini Project — E-Commerce Customer Feature Engineering

Build a feature-engineering pipeline using

  • Customer_ID
  • Order_ID
  • Order_Date
  • Product
  • Category
  • Quantity
  • Unit_Price
  • Discount

Create the following features

  • Transaction Features
  • Revenue
  • Discount_Amount
  • Net_Revenue
  • Date Features
  • Year
  • Month
  • Quarter
  • DayOfWeek
  • IsWeekend
  • IsMonthEnd
  • Customer Features
  • Total_Revenue
  • Order_Count
  • Average_Order_Value
  • Maximum_Order_Value
  • Minimum_Order_Value
  • RFM Features
  • Recency
  • Frequency
  • Monetary
  • Behavioral Features
  • Average_Quantity
  • Total_Quantity
  • Unique_Product_Count
  • Unique_Category_Count
  • Time-Series Features
  • Previous_Order_Revenue
  • Revenue_7_Day_Average
  • Revenue_30_Day_Average

Finally, create a customer-level feature table

  • Customer_ID
  • Recency
  • Frequency
  • Monetary
  • Average_Order_Value
  • Total_Quantity
  • Unique_Product_Count
  • Unique_Category_Count

This becomes the foundation for

  • Customer segmentation
  • Churn prediction
  • Customer lifetime value
  • Recommendation systems
  • Marketing analytics
  • Power BI dashboards

6.6.51Key Takeaways

The feature-engineering workflow is

RAW DATA

CLEAN DATA

UNDERSTAND BUSINESS

OBJECTIVE

CREATE FEATURES

┌──────────────┼──────────────┐

▼ ▼ ▼

Numerical Date/Time Categorical

│ │ │

└──────────────┼──────────────┘

Aggregation

Ratios / KPIs

Lag / Rolling

Encoding / Binning

Feature Selection

Leakage Validation

FINAL FEATURES

  • The most important concepts to master
  • Date/time feature extraction
  • Ratio and KPI features
  • Group-based aggregation
  • RFM/customer features
  • Categorical encoding
  • Binning and segmentation
  • Lag and rolling features
  • Interaction features
  • Feature selection
  • Data-leakage prevention

The central principle is

Good feature engineering converts raw data into information that directly represents the business problem.

For example, instead of giving a model only Order_Date, Quantity, and Price, feature engineering can turn them into Revenue, Month, Weekend Flag, Customer Frequency, Average Order Value, Recency, and Growth, making the dataset far more useful for analytics and predictive modeling.

Module 6 · Lesson 6.7

Data Transformation

Data transformation is the process of converting data from one structure, format, scale, or representation into another form that is more suitable for analysis, visualization, reporting, statistical modeling, or machine learning.

For example, raw sales data might contain

Quantity = 5
Unit_Price = ₹2,000
Discount = 10%

After transformation

Quantity        = 5
Unit_Price      = 2000
Discount        = 0.10
Gross_Revenue   = 10000
Discount_Value  = 1000
Net_Revenue     = 9000

Data transformation is closely related to feature engineering, but transformation generally focuses on changing the representation or structure of existing data, whereas feature engineering emphasizes creating useful new variables/features.

6.7.1Learning Objectives

By the end of this topic, you should be able to

  • Understand data transformation.
  • Transform numerical data.
  • Transform categorical data.
  • Change data types.
  • Normalize and standardize values.
  • Apply logarithmic transformations.
  • Transform dates and times.
  • Reshape DataFrames.
  • Aggregate and summarize data.
  • Merge and join datasets.
  • Pivot and melt data.
  • Apply custom transformations.
  • Build transformation pipelines.
  • Understand transformation leakage.

6.7.2Why Data Transformation Is Important

Raw data often isn't directly usable.

For example

  • Sales
  • ₹10,000
  • ₹20,000
  • ₹50,000

This is text, not numeric data.

After transformation

  • 10000
  • 20000
  • 50000

we can calculate

  • Mean
  • Median
  • Total
  • Growth
  • Correlation

Similarly, machine-learning algorithms often perform better when numerical variables are transformed to comparable scales.

6.7.3Data Transformation Workflow

A typical workflow is

Raw Data
Understand Data
Clean Data
Convert Data Types
Transform Values
Reshape Data
Aggregate / Join
Validate
Analysis / ML / BI

6.7.4Common Types of Data Transformation

Important transformation categories include

  • 1. Data Type Conversion
  • 2. Numerical Transformation
  • 3. Scaling
  • 4. Normalization
  • 5. Standardization
  • 6. Log Transformation
  • 7. Categorical Transformation
  • 8. Date Transformation
  • 9. Aggregation
  • 10. Reshaping
  • 11. Pivoting
  • 12. Melting
  • 13. Merging
  • 14. Joining
  • 15. Mapping
  • 16. Custom Transformation
  • 6.7.5Data Type Conversion

Suppose

df["Age"].dtype

returns

object

Convert to numeric

df["Age"] = pd.to_numeric(

df["Age"],

errors="coerce"

)

6.7.6String to Numeric

Suppose

  • "100"
  • "200"
  • "300"

Convert

df["Sales"] = pd.to_numeric(

df["Sales"]

)

For invalid values

df["Sales"] = pd.to_numeric(

df["Sales"],

errors="coerce"

)

Invalid values become missing.

6.7.7Currency Transformation

Suppose

  • ₹10,000
  • ₹25,000
  • ₹50,000

Transform

df["Sales"] = (

  • df["Sales"]
  • .str.replace("₹", "", regex=False)
  • .str.replace(",", "", regex=False)

)

df["Sales"] = pd.to_numeric(

df["Sales"],

errors="coerce"

)

Now

  • 10000
  • 25000
  • 50000

6.7.8Percentage Transformation

Suppose

  • 10%
  • 20%
  • 35%

Transform

  • df["Discount"] = (
  • df["Discount"]
  • .str.replace("%", "", regex=False)
  • .astype(float)
  • / 100

)

Now

  • 0.10
  • 0.20
  • 0.35
  • This representation is easier for calculations.

6.7.9Date Transformation

Suppose

df["Order_Date"] = pd.to_datetime(

df["Order_Date"]

)

Extract year

df["Year"] = (

df["Order_Date"].dt.year

)

Month

df["Month"] = (

df["Order_Date"].dt.month

)

Quarter

df["Quarter"] = (

df["Order_Date"].dt.quarter

)

Day

df["Day"] = (

df["Order_Date"].dt.day

)

6.7.10Date Formatting

Suppose

df["Order_Date"]

contains datetime values.

Convert to a formatted string

df["Order_Date_String"] = (

df["Order_Date"]

.dt.strftime("%Y-%m-%d")

)

Example

2026-08-23

Other formats

df["Order_Date"].dt.strftime(

"%d-%m-%Y"

)

or

df["Order_Date"].dt.strftime(

"%B %Y"

)

6.7.11Numerical Transformation

Numerical transformation changes the mathematical representation of a variable.

Common techniques

  • Min-Max Scaling
  • Standardization
  • Robust Scaling
  • Log Transformation
  • Power Transformation
  • Quantile Transformation

6.7.12Min-Max Scaling

Min-Max scaling transforms values to a fixed range, usually

0 to 1

Formula

X_scaled =

(X - X_min) /

(X_max - X_min)

Example

from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
  • df["Income_Scaled"] = (
  • scaler.fit_transform(
  • df[["Income"]]

)

)

If the original values are

  • 100
  • 200
  • 300

they become approximately

  • 0
  • 0.5
  • 1

6.7.13When to Use Min-Max Scaling

Min-Max scaling is useful when

  • A fixed range is desirable.
  • Features have different units.
  • Algorithms are sensitive to feature magnitude.

Common applications include

  • Neural networks
  • Distance-based algorithms
  • KNN
  • Some optimization-based models

6.7.14Standardization

Standardization transforms data so that it has approximately

Mean = 0

Standard deviation = 1

Formula

Z = (X - Mean) / Standard Deviation

Using Scikit-learn

from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
  • df["Income_Standardized"] = (
  • scaler.fit_transform(
  • df[["Income"]]

)

)

6.7.15Min-Max vs Standardization

FeatureMin-MaxStandardization
RangeUsually 0–1No fixed range
MeanNot necessarily 0Approximately 0
Std DevNot necessarily 1Approximately 1
Sensitive to outliersYesYes
Common useDistance/scale-sensitive modelsMany statistical/ML models

Neither method is universally better.

6.7.16Robust Scaling

When data contains significant outliers, robust scaling can be useful.

It uses

Median

Interquartile range

from sklearn.preprocessing import RobustScaler
scaler = RobustScaler()
  • df["Income_Robust"] = (
  • scaler.fit_transform(
  • df[["Income"]]

)

)

This is less affected by extreme observations than mean/std-based scaling.

6.7.17Log Transformation

Consider highly skewed revenue

  • 100
  • 200
  • 300
  • 500
  • 10000
  • 50000
  • A log transformation compresses large values.
import numpy as np

df["Log_Revenue"] = np.log1p(

df["Revenue"]

)

log1p(x) means

log(1 + x)

It is useful when zero values are possible.

6.7.18Why Log Transformation Helps

Without transformation

  • 100
  • 200
  • 300
  • 500
  • 10000
  • 50000

After log transformation, the difference between large values is compressed.

This can

  • Reduce skewness
  • Reduce influence of extreme values
  • Improve visualization
  • Help some statistical models
  • Make relationships more linear

6.7.19Power Transformation

Power transformations can help make skewed distributions more suitable for analysis.

Scikit-learn provides

from sklearn.preprocessing import PowerTransformer
transformer = PowerTransformer()
X_transformed = (
    transformer.fit_transform(X)
)

Common methods include

Yeo-Johnson

Box-Cox

Yeo-Johnson can handle zero and negative values, while Box-Cox has stricter input requirements.

6.7.20Quantile Transformation

Quantile transformation maps values according to their rank distribution.

from sklearn.preprocessing import QuantileTransformer
transformer = QuantileTransformer(
    output_distribution="normal"
)
X_transformed = (
    transformer.fit_transform(X)
)

This can be useful for heavily skewed data, but it changes the original distribution substantially.

6.7.21Categorical Transformation

Categorical values need to be represented appropriately.

Example

  • Gender
  • Male
  • Female
  • Male
  • Female

One-hot encoding

df = pd.get_dummies(
    df,
    columns=["Gender"]
)

Result

Gender_Female

Gender_Male

6.7.22Mapping Categories

Suppose

  • Low
  • Medium
  • High

You can map them

mapping = {
    "Low": 1,
    "Medium": 2,
    "High": 3
}

df["Priority_Code"] = (

df["Priority"].map(mapping)

)

This is appropriate when the categories have a genuine ordinal relationship.

6.7.23Ordinal vs Nominal Categories

Ordinal

Categories have an order

Low < Medium < High

Numeric mapping can make sense

Low → 1

Medium → 2

High → 3

Nominal

Categories have no natural order

  • Hyderabad
  • Bengaluru
  • Chennai

Using

Hyderabad = 1
Bengaluru = 2
Chennai = 3

would incorrectly imply an ordering.

One-hot encoding is usually more appropriate.

6.7.24String Transformation

Convert text to lowercase

df["City"] = (

df["City"]

.str.lower()

)

Remove spaces

df["City"] = (

df["City"]

.str.strip()

)

Replace values

df["City"] = df["City"].replace({

"bangalore": "bengaluru"

})

6.7.25Conditional Transformation

Suppose

df["Sales"]

Create a sales category

  • df["Sales_Category"] = np.where(
  • df["Sales"] >= 100000,
  • "High",
  • "Normal"

)

For multiple conditions, use np.select()

conditions = [
    df["Sales"] < 10000,
    df["Sales"].between(
        10000, 50000
    ),
    df["Sales"] > 50000
]
choices = [
    "Low",
    "Medium",
    "High"
]
  • df["Sales_Category"] = np.select(
  • conditions,
  • choices,
default="Unknown"

)

6.7.26Aggregation Transformation

Suppose transaction-level data

CustomerOrderRevenue
A1100
A2200
B3500

Aggregate by customer

customer_sales = (
    df.groupby("Customer")["Revenue"]
    .sum()
    .reset_index()
)

Result

CustomerRevenue
A300
B500

Aggregation converts transaction-level data into customer-level data.

6.7.27Multiple Aggregations

summary = (
    df.groupby("Customer")
    .agg(
        Total_Revenue=("Revenue", "sum"),
        Average_Revenue=("Revenue", "mean"),
        Max_Revenue=("Revenue", "max"),
        Order_Count=("Order", "nunique")
    )
    .reset_index()
)

This is extremely common in data analysis.

6.7.28Transformation Using apply()

You can apply a custom function.

def categorize_sales(value):
if value < 10000:
return "Low"
elif value < 50000:
return "Medium"
else:
return "High"
  • df["Sales_Category"] = (
  • df["Sales"].apply(
  • categorize_sales

)

)

For simple conditions, vectorized operations are generally preferable because they are usually faster.

6.7.29Lambda Transformation

  • df["Sales_Thousands"] = (
  • df["Sales"]
  • .apply(lambda x: x / 1000)

)

However, prefer vectorized Pandas operations where possible

df["Sales_Thousands"] = (

df["Sales"] / 1000

)

This is usually faster and more idiomatic.

6.7.30Reshaping Data

Data can exist in

Wide format

Month Product_A Product_B Product_C

Jan 100 200 300

Feb 120 220 320

Long format

Month Product Sales

Jan Product_A 100

Jan Product_B 200

Jan Product_C 300

Feb Product_A 120

...

Pandas provides

  • melt()
  • pivot()
  • pivot_table()
  • stack()
  • unstack()

6.7.31Melting Data

Example

df_long = df.melt(
    id_vars=["Month"],
    var_name="Product",
    value_name="Sales"
)

melt() converts wide data into long format.

This is particularly useful for

  • visualization
  • statistical analysis
  • BI workflows

6.7.32Pivoting Data

Suppose long data

Month Product Sales

Jan A 100

Jan B 200

Feb A 120

Feb B 220

Pivot

pivoted = df.pivot(
    index="Month",
    columns="Product",
    values="Sales"
)

Result

Product A B

Month

Jan 100 200

Feb 120 220

6.7.33Pivot Tables

For duplicate combinations, use pivot_table()

pivot = pd.pivot_table(
    df,
    index="Region",
    columns="Product",
    values="Sales",
    aggfunc="sum"
)

This is extremely useful for business analysis.

6.7.34Transposing Data

df.T

This swaps rows and columns.

Useful for quick inspection, but usually not a primary analytical transformation.

6.7.35Merging DataFrames

Suppose

customers

contains

  • Customer_ID
  • Customer_Name
  • City

and

orders

contains

  • Order_ID
  • Customer_ID
  • Revenue

Merge

result = pd.merge(
    orders,
    customers,
    on="Customer_ID",
    how="left"
)

6.7.36Types of Joins

Common joins

  • inner
  • left
  • right
  • outer
  • cross
  • Inner Join
  • Returns matching records.
pd.merge(
    orders,
    customers,
    on="Customer_ID",
    how="inner"
)

Left Join

Keeps all orders

pd.merge(
    orders,
    customers,
    on="Customer_ID",
    how="left"
)

This is often useful when enriching transaction data with master data.

6.7.37Concatenating Data

Suppose you have January and February datasets

jan = pd.read_csv("jan.csv")
feb = pd.read_csv("feb.csv")

Combine rows

df = pd.concat(
    [jan, feb],
    ignore_index=True
)

This is useful when datasets have the same structure.

6.7.38Combining Columns

You can also concatenate along columns

result = pd.concat(
    [df1, df2],
    axis=1
)

Use this carefully because rows must be aligned correctly.

6.7.39Data Transformation for Power BI

A typical analytics workflow might be

SQL Server
Extract
Python / Pandas
Clean
Transform
Aggregate
Data Warehouse
Power BI

For example, transaction data

  • Customer_ID
  • Order_Date
  • Quantity
  • Unit_Price

can be transformed into

  • Customer_ID
  • Year
  • Month
  • Revenue
  • Order_Count
  • Average_Order_Value

The resulting dataset is much easier to use for dashboard development.

6.7.40Transformation Order Matters

A common pipeline is

Raw Data
Data Type Conversion
Missing Value Handling
Outlier Handling
Feature Creation
Scaling / Encoding
Reshaping
Aggregation
Validation

But the exact order depends on the use case.

For example, scaling should generally happen after splitting train/test data in a predictive modeling workflow.

6.7.41Avoiding Data Leakage

Suppose you want to standardize

Income

Incorrect approach

Entire Dataset
Fit Scaler
Train/Test Split

This allows test-set information to influence the transformation.

Correct

Raw Data
Train/Test Split
Fit scaler on Training Data
Transform Training Data
Transform Test Data using same scaler

In Scikit-learn

scaler.fit(X_train)

X_train_scaled = (
    scaler.transform(X_train)
)
X_test_scaled = (
    scaler.transform(X_test)
)

6.7.42Pipeline-Based Transformation

A better approach is

from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
pipeline = Pipeline([
    (
        "imputer",
        SimpleImputer(strategy="median")
    ),
    (
        "scaler",
        StandardScaler()
    )
])

Then

X_train_transformed = (
    pipeline.fit_transform(X_train)
)
X_test_transformed = (
    pipeline.transform(X_test)
)

This helps keep preprocessing consistent.

6.7.43Transformation of Skewed Data

Suppose revenue is

  • 100
  • 200
  • 300
  • 400
  • 10000
  • 50000

Check skewness

df["Revenue"].skew()

If strongly positive

df["Log_Revenue"] = np.log1p(

df["Revenue"]

)

Check again

df["Log_Revenue"].skew()

This allows you to compare the distribution before and after transformation.

6.7.44Transformation Validation

After transformation, always verify

print(df.info())
print(df.describe())

For numerical transformation

print(
    df["Revenue"].describe()
)
print(
    df["Log_Revenue"].describe()
)

For categorical transformation

print(
    df["Category"].value_counts()
)

For joins

print(
    len(before),
    len(after)
)

Unexpected row-count changes can reveal join problems.

6.7.45Common Data Transformation Mistakes

  • Mistake 1 — Transforming without understanding the data
  • Always inspect the data first.
  • Mistake 2 — Treating missing values as zero
  • Missing ≠ Zero
  • Mistake 3 — Scaling before train/test split
  • This can cause leakage.
  • Mistake 4 — Incorrect join keys
  • A many-to-many join can unexpectedly multiply records.

Always validate

  • Before rows
  • After rows
  • Expected relationship
  • Mistake 5 — Applying log to negative values

This is invalid for the ordinary logarithm.

For non-negative values

np.log1p(x)

can handle zero, but not values below -1.

Mistake 6 — Encoding nominal variables as arbitrary numbers

Don't assume

Hyderabad = 1
Chennai = 2
Bengaluru = 3

has meaningful order.

Use one-hot or another appropriate encoding.

6.7.46Complete Example

Consider an e-commerce dataset

df = pd.DataFrame({
    "Order_ID": [1, 2, 3, 4],
    "Order_Date": [
        "2026-01-10",
        "2026-02-15",
        "2026-02-20",
        "2026-03-10"
    ],
    "Quantity": [2, 5, 1, 10],
    "Unit_Price": [
        "₹500",
        "₹1,000",
        "₹750",
        "₹200"
    ],
    "Discount": [
        "10%",
        "5%",
        "0%",
        "20%"
    ]
})
  • Step 1 — Convert date
  • df["Order_Date"] = pd.to_datetime(
  • df["Order_Date"]

)

  • Step 2 — Clean price
  • df["Unit_Price"] = (
  • df["Unit_Price"]
  • .str.replace("₹", "", regex=False)
  • .str.replace(",", "", regex=False)
  • .astype(float)

)

  • Step 3 — Convert discount
  • df["Discount"] = (
  • df["Discount"]
  • .str.replace("%", "", regex=False)
  • .astype(float)
  • / 100

)

  • Step 4 — Create gross revenue
  • df["Gross_Revenue"] = (
  • df["Quantity"] *
  • df["Unit_Price"]

)

  • Step 5 — Calculate discount amount
  • df["Discount_Amount"] = (
  • df["Gross_Revenue"] *
  • df["Discount"]

)

  • Step 6 — Calculate net revenue
  • df["Net_Revenue"] = (
  • df["Gross_Revenue"] -
  • df["Discount_Amount"]

)

Step 7 — Extract month

df["Month"] = (

df["Order_Date"].dt.month

)

Step 8 — Validate

print(df.describe())
print(df.info())

This converts messy business data into analysis-ready data.

6.7.47Real-World Transformation Architecture

A production analytics environment may look like

SOURCE SYSTEMS

┌───────────┼───────────┐

▼ ▼ ▼

SQL Server Oracle CSV

│ │ │

└───────────┼───────────┘

RAW DATA

DATA CLEANING

DATA TRANSFORMATION

┌───────────┼───────────┐

▼ ▼ ▼

Types Scaling Business

Conversion Calculations

│ │ │

└───────────┼───────────┘

AGGREGATION

DATA MODEL

┌────────┴────────┐

▼ ▼

Power BI ML / Python

6.7.48Mini Project — Sales Data Transformation

Create a dataset containing

  • Order_ID
  • Customer_ID
  • Order_Date
  • Region
  • Product
  • Quantity
  • Unit_Price
  • Discount
  • Perform the following transformations.
  • Step 1 — Clean types

Convert

Order_Date → datetime

Quantity → numeric

Unit_Price → numeric

Discount → numeric

  • Step 2 — Create financial measures
  • Gross_Revenue
  • Discount_Amount
  • Net_Revenue
  • Step 3 — Create date features
  • Year
  • Month
  • Quarter
  • DayOfWeek
  • IsWeekend
  • Step 4 — Create customer-level aggregation
  • Total_Revenue
  • Order_Count
  • Average_Order_Value
  • Step 5 — Create regional summary
  • Region
  • Total_Revenue
  • Average_Revenue
  • Order_Count
  • Step 6 — Create a pivot table

Rows

Region

Columns

Month

Values

Net_Revenue

Step 7 — Scale numerical variables

Apply

  • Min-Max Scaling
  • Standardization
  • Robust Scaling
  • Compare the results.
  • Step 8 — Apply log transformation

Transform

Net_Revenue

if it is heavily skewed.

Step 9 — Validate

Check

  • Row count
  • Null count
  • Data types
  • Minimum values
  • Maximum values
  • Duplicate IDs
  • Unexpected join multiplication

6.7.49Interview Questions

  • Basic
  • What is data transformation?
  • Why is data transformation important?
  • What is the difference between data cleaning and transformation?
  • How do you convert strings to numeric values in Pandas?
  • How do you convert strings to datetime?
  • What is Min-Max scaling?
  • What is standardization?
  • What is log transformation?
  • What is one-hot encoding?
  • What is data reshaping?

Advanced

  • What is the difference between normalization and standardization?
  • When would you use RobustScaler?
  • When would you use a log transformation?
  • What is the difference between pivot() and pivot_table()?
  • What is the difference between merge() and concat()?
  • How can a join unexpectedly increase row count?
  • How do you transform categorical variables?
  • How do you handle skewed numerical variables?
  • How do you prevent transformation-related data leakage?
  • Why should scalers be fitted only on training data?
  • What is a PowerTransformer?
  • What is QuantileTransformer?
  • How would you build a reusable transformation pipeline?
  • How would you validate transformations in production?
  • How would you transform a raw transactional dataset into a Power BI-ready dataset?

6.7.50Key Takeaways

The complete transformation process is

RAW DATA

DATA CLEANING

TYPE CONVERSION

VALUE TRANSFORMATION

┌────────────────┼────────────────┐

▼ ▼ ▼

Numerical Categorical Date/Time

Scaling Encoding Extraction

│ │ │

└────────────────┼────────────────┘

AGGREGATION

RESHAPING DATA

MERGE / JOIN DATA

VALIDATION

ANALYSIS / BI / ML

  • Most important concepts

Focus especially on

  • Data-type conversion
  • Min-Max scaling
  • Standardization
  • Robust scaling
  • Log transformation
  • Categorical encoding
  • Date transformation
  • Aggregation and grouping
  • Pivot / melt / reshape
  • Merge / join / concatenate
  • Transformation pipelines
  • Data-leakage prevention

Data transformation converts raw data into a representation that is technically correct, analytically useful, and appropriate for the intended business or machine-learning task.

A useful way to remember the distinction is

Data Cleaning → Fix the data. Feature Engineering → Create useful information. Data Transformation → Change the representation or structure of the data.

Module 6 · Lesson 6.8

Matplotlib

Matplotlib is one of the most widely used Python libraries for creating static data visualizations.

It allows analysts and developers to create

  • Line charts
  • Bar charts
  • Histograms
  • Scatter plots
  • Pie charts
  • Box plots
  • Area charts
  • Heatmaps
  • Subplots
  • Customized dashboards and reports

Matplotlib is particularly important because libraries such as Seaborn and many other Python visualization tools build on concepts from Matplotlib.

6.8.1Learning Objectives

By the end of this topic, you should be able to

  • Understand Matplotlib architecture.
  • Create basic charts.
  • Customize titles, labels, and legends.
  • Control axes and ticks.
  • Create multiple plots.
  • Create subplots.
  • Visualize Pandas DataFrames.
  • Create statistical charts.
  • Annotate charts.
  • Save charts to files.
  • Customize chart appearance.
  • Build business-oriented visualizations.

6.8.2Installing Matplotlib

Install using

pip install matplotlib

Import

import matplotlib.pyplot as plt

The common alias is

plt

6.8.3Basic Matplotlib Structure

A typical chart follows

Import
Prepare Data
Create Figure
Create Plot
Customize
Display / Save

Basic example

import matplotlib.pyplot as plt
x = [1, 2, 3, 4, 5]
y = [10, 20, 15, 30, 25]
plt.plot(x, y)
plt.show()

6.8.4Figure and Axes

Matplotlib has two important concepts

  • Figure
  • The entire drawing area.
  • Axes

The actual plotting area inside the figure.

Conceptually

Figure

┌─────────────────────────────┐

│ │

│ Axes │

│ ┌───────────────┐ │

│ │ │ │

│ │ Chart │ │

│ │ │ │

│ └───────────────┘ │

│ │

└─────────────────────────────┘

The object-oriented approach is

fig, ax = plt.subplots()

ax.plot(x, y)

plt.show()

For larger applications, this approach is usually preferable.

6.8.5Line Chart

A line chart is useful for showing trends over an ordered variable such as time.

import matplotlib.pyplot as plt
months = [
    "Jan", "Feb", "Mar",
    "Apr", "May", "Jun"
]
sales = [
    10000, 12000, 15000,
    13000, 18000, 20000
]
plt.plot(
    months,
    sales
)
plt.title("Monthly Sales")
plt.xlabel("Month")
plt.ylabel("Sales")
plt.show()

6.8.6Adding Markers

plt.plot(
    months,
    sales,
    marker="o"
)

Markers make individual observations easier to see.

Common markers

  • o → Circle
  • s → Square
  • ^ → Triangle
  • D → Diamond
  • x → X
  • * → Star

6.8.7Line Styles

Matplotlib supports several line styles

plt.plot(
    months,
    sales,
    linestyle="--"
)

Common options

- Solid

-- Dashed

-. Dash-dot

: Dotted

6.8.8Multiple Lines

Suppose you want to compare two products

product_a = [100, 120, 140, 160, 180]
product_b = [90, 130, 125, 170, 190]
plt.plot(
    months[:5],
    product_a,
    label="Product A"
)
plt.plot(
    months[:5],
    product_b,
    label="Product B"
)
plt.title("Product Sales")
plt.xlabel("Month")
plt.ylabel("Sales")
plt.legend()
plt.show()

The legend identifies each series.

6.8.9Grid

Add a grid

plt.grid()

Or

plt.grid(
    axis="y"
)

A grid can help users compare values more easily.

Avoid excessive gridlines in polished business charts.

6.8.10Figure Size

Set the chart size

plt.figure(
    figsize=(10, 5)
)

For example

plt.figure(
    figsize=(12, 6)
)
plt.plot(months, sales)
plt.show()

6.8.11Bar Chart

Bar charts are useful for comparing categories.

products = [
    "Laptop",
    "Phone",
    "Tablet",
    "Monitor"
]
sales = [
    50000,
    80000,
    30000,
    45000
]
plt.bar(
    products,
    sales
)
plt.title("Product Sales")
plt.xlabel("Product")
plt.ylabel("Sales")
plt.show()

6.8.12Horizontal Bar Chart

Use barh()

plt.barh(
    products,
    sales
)
plt.title("Product Sales")
plt.show()

Horizontal bars are often better when category names are long.

6.8.13Grouped Bar Chart

Suppose

months = ["Jan", "Feb", "Mar"]
product_a = [100, 120, 140]
product_b = [90, 130, 150]

Use positions

import numpy as np
x = np.arange(len(months))
width = 0.35
plt.bar(
    x - width/2,
    product_a,
    width,
    label="Product A"
)
plt.bar(
    x + width/2,
    product_b,
    width,
    label="Product B"
)
plt.xticks(
    x,
    months
)
plt.legend()
plt.show()

6.8.14Stacked Bar Chart

plt.bar(
    months,
    product_a,
    label="Product A"
)
plt.bar(
    months,
    product_b,
    bottom=product_a,
    label="Product B"
)
plt.legend()
plt.show()

This is useful for showing composition.

6.8.15Histogram

A histogram shows the distribution of a numerical variable.

ages = [
    21, 22, 25, 28,
    30, 32, 35, 36,
    40, 42, 45, 50
]
plt.hist(
    ages,
    bins=5
)
plt.title("Age Distribution")
plt.xlabel("Age")
plt.ylabel("Frequency")
plt.show()

6.8.16Understanding Bins

Suppose

plt.hist(
    ages,
    bins=10
)

More bins

Show more detail.

May make the chart noisy.

Fewer bins

Simplify the distribution.

May hide useful patterns.

Choosing bins is partly a visualization judgment.

6.8.17Density-Style Histogram

You can normalize the histogram

plt.hist(
    ages,
    bins=10,
    density=True
)

This represents density rather than raw count.

6.8.18Scatter Plot

Scatter plots show relationships between two numerical variables.

Example

income = [
    30000, 40000, 50000,
    60000, 70000, 80000
]
spending = [
    2000, 2500, 3000,
    4000, 5000, 6500
]
plt.scatter(
    income,
    spending
)
plt.title(
    "Income vs Spending"
)
plt.xlabel("Income")
plt.ylabel("Spending")
plt.show()

Scatter plots help identify

  • Correlation
  • Clusters
  • Outliers
  • Nonlinear patterns

6.8.19Scatter Plot with Categories

Suppose customers belong to different segments.

plt.scatter(
    income,
    spending,
    label="Customers"
)
plt.legend()
plt.show()

For more advanced categorical visualization, Seaborn is often more convenient.

6.8.20Pie Chart

Pie charts show proportions.

categories = [
    "Electronics",
    "Clothing",
    "Food",
    "Books"
]
sales = [
    40,
    25,
    20,
    15
]
plt.pie(
    sales,
    labels=categories,
    autopct="%1.1f%%"
)
plt.title("Sales Distribution")
plt.show()

Pie charts work best with a small number of categories.

6.8.21Exploded Pie Chart

explode = [
    0.1,
    0,
    0,
    0
]
plt.pie(
    sales,
    labels=categories,
    explode=explode,
    autopct="%1.1f%%"
)
plt.show()

Use this sparingly.

6.8.22Box Plot

Box plots are useful for understanding

  • Median
  • Quartiles
  • IQR
  • Potential outliers
sales = [
    100, 120, 130,
    140, 150, 160,
    500
]
plt.boxplot(
    sales
)
plt.title(
    "Sales Distribution"
)
plt.ylabel("Sales")
plt.show()

6.8.23Understanding a Box Plot

Conceptually

Maximum

┌─────────────┐

│ │

│ Q3 │

│─────────────│

│ Median │

│─────────────│

│ Q1 │

└─────────────┘

Minimum

Potential outliers appear separately from the whiskers.

6.8.24Multiple Box Plots

Compare several categories

sales_data = [
    [100, 120, 130, 140],
    [200, 220, 250, 270],
\[50, 60, 70, 80\]

]

plt.boxplot(
    sales_data,
    labels=[
        "Region A",
        "Region B",
        "Region C"
    ]
)
plt.title(
    "Regional Sales"
)
plt.show()

6.8.25Area Chart

plt.fill_between(
    months,
    sales
)
plt.title(
    "Sales Trend"
)
plt.show()

Area charts emphasize magnitude over time.

6.8.26Step Chart

plt.step(
    months,
    sales
)
plt.title(
    "Sales Progression"
)
plt.show()

Useful when values change in discrete steps.

6.8.27Stem Plot

plt.stem(
    [1, 2, 3, 4],
\[10, 20, 15, 30\]

)

plt.show()

Useful for discrete observations.

6.8.28Titles and Labels

A professional chart should usually contain

plt.title(
    "Monthly Revenue"
)
plt.xlabel(
    "Month"
)
plt.ylabel(
    "Revenue (₹)"
)

Labels make charts understandable without requiring additional explanation.

6.8.29Legend

Multiple series require a legend

plt.plot(
    months,
    product_a,
    label="Product A"
)
plt.plot(
    months,
    product_b,
    label="Product B"
)
plt.legend()

You can control location

plt.legend(
    loc="upper left"
)

6.8.30Axis Limits

Set y-axis limits

plt.ylim(
    0,
    100000
)

Set x-axis limits

plt.xlim(
    0,
    12
)

Be careful not to manipulate axis limits in a way that exaggerates or hides important differences.

6.8.31Tick Rotation

Long category names may overlap.

plt.xticks(
    rotation=45
)

For 90 degrees

plt.xticks(
    rotation=90
)

6.8.32Custom Tick Labels

plt.xticks(
    [0, 1, 2],
\["Jan", "Feb", "Mar"\]

)

This is useful when x-values are numerical positions but the desired labels are text.

6.8.33Formatting Large Numbers

For business charts, values like

1000000

may be harder to read.

You can use a formatter

from matplotlib.ticker import FuncFormatter
def millions(x, pos):
    return f"{x/1_000_000:.1f}M"
    plt.gca().yaxis.set_major_formatter(
        FuncFormatter(millions)
    )

The axis can then display values such as

  • 1.0M
  • 2.0M
  • 3.0M

6.8.34Adding Annotations

You can highlight an important point

plt.plot(
    months,
    sales,
    marker="o"
)
plt.annotate(
    "Highest Sales",
    xy=("Jun", 20000),
    xytext=("Apr", 22000),
    arrowprops=dict(
        arrowstyle="->"
    )
)
plt.show()

Annotations are useful for highlighting

  • Peaks
  • Drops
  • Business events
  • Outliers
  • Important milestones

6.8.35Reference Lines

Add a target

plt.axhline(
    y=15000,
    linestyle="--"
)

For example

Target = ₹15,000

This allows users to compare actual performance against a target.

6.8.36Vertical Reference Line

plt.axvline(
    x=3,
    linestyle="--"
)

Useful for marking

  • Launch date
  • Policy change
  • Incident
  • Promotion
  • Business event

6.8.37Multiple Subplots

Create multiple charts in one figure

fig, axes = plt.subplots(

2,

2,

figsize=(10, 8)

)

Then

  • axes[0, 0].plot(
  • months,
  • sales

)

  • axes[0, 1].bar(
  • products,
  • product_sales

)

axes[1, 0].hist(

ages

)

  • axes[1, 1].scatter(
  • income,
  • spending

)

plt.tight_layout()
plt.show()

This creates a 2 × 2 layout.

6.8.38Understanding subplots()

  • fig, ax = plt.subplots()
  • One plot.
  • fig, axes = plt.subplots(

2,

2

)

Four plots.

┌────────────┬────────────┐

│ Plot 1 │ Plot 2 │

├────────────┼────────────┤

│ Plot 3 │ Plot 4 │

└────────────┴────────────┘

6.8.39Object-Oriented Matplotlib

For professional code, prefer

  • fig, ax = plt.subplots()
  • ax.plot(
  • months,
  • sales

)

ax.set_title(

"Monthly Sales"

)

ax.set_xlabel(

"Month"

)

ax.set_ylabel(

"Sales"

)

plt.show()

Instead of relying entirely on global plt state.

This becomes especially useful when working with multiple axes.

6.8.40Pandas + Matplotlib

Pandas integrates directly with Matplotlib.

Suppose

df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar"
    ],
    "Sales": [
        10000, 12000, 15000
    ]
})

Plot

df.plot(
    x="Month",
    y="Sales",
    kind="line"
)
plt.show()

Bar

df.plot(
    x="Month",
    y="Sales",
    kind="bar"
)
plt.show()

Histogram

df["Sales"].plot(

kind="hist"

)

plt.show()

6.8.41Common Pandas Plot Types

  • line
  • bar
  • barh
  • hist
  • box
  • area
  • scatter
  • pie

Example

df.plot(
    kind="box"
)

6.8.42Plotting Multiple Columns

df.plot(
    x="Month",
    y=[
        "Product_A",
        "Product_B"
    ]
)
plt.show()

This creates multiple series in one chart.

6.8.43Saving a Chart

Use

plt.savefig(
    "sales_chart.png"
)

High-resolution output

plt.savefig(
    "sales_chart.png",
    dpi=300,
    bbox_inches="tight"
)

Common formats

  • PNG
  • JPG
  • SVG
  • PDF

For example

plt.savefig(
    "sales_chart.pdf"
)

6.8.44Saving Before show()

Prefer

plt.savefig(
    "chart.png"
)
plt.show()

rather than depending on state after show().

For reusable plotting functions, it is often cleaner to explicitly save from the Figure object:

  • fig, ax = plt.subplots()
  • ax.plot(months, sales)
  • fig.savefig(
  • "chart.png",
dpi=300,
bbox_inches="tight"

)

6.8.45Handling Missing Values

Matplotlib generally skips missing numerical observations in line plots.

Example

sales = [
    100,
    120,
    np.nan,
    160
]
plt.plot(
    sales,
    marker="o"
)
plt.show()

The missing point creates a break rather than automatically inventing a value.

This is often preferable because the visualization doesn't silently fabricate data.

6.8.46Plotting Dates

dates = pd.date_range(
    "2026-01-01",
    periods=10
)
sales = [
    100, 120, 130,
    110, 150, 160,
    170, 165, 180, 200
]
  • fig, ax = plt.subplots()
  • ax.plot(
  • dates,
  • sales

)

ax.set_title(

"Daily Sales"

)

fig.autofmt_xdate()

plt.show()

autofmt_xdate() helps format date labels.

6.8.47Business KPI Visualization

Suppose a company tracks

  • Revenue
  • Profit
  • Orders
  • Customers
  • Conversion Rate

A Matplotlib dashboard might include

┌─────────────────────────────────────┐

│ Revenue Trend │

│ Line Chart │

├───────────────────┬─────────────────┤

│ Regional Revenue │ Order Volume │

│ Bar Chart │ Line Chart │

├───────────────────┼─────────────────┤

│ Profit Margin │ Customer Growth │

│ Line Chart │ Area Chart │

└───────────────────┴─────────────────┘

This combines multiple visualizations into one analytical view.

6.8.48Choosing the Right Chart

Business QuestionRecommended Chart
How did sales change over time?Line
Which product sold the most?Bar
What is the distribution of ages?Histogram
Are income and spending related?Scatter
Are there outliers?Box plot
What is the composition?Stacked bar / Pie
How does a metric compare with target?Line + reference line
Compare many categoriesBar
Show continuous distributionHistogram
Show multiple KPIsSubplots

6.8.49Matplotlib Styling Principles

A good business visualization should have

  • Clear title
  • ax.set_title(
  • "Monthly Revenue"

)

  • Meaningful labels
  • ax.set_xlabel("Month")
  • ax.set_ylabel("Revenue (₹)")
  • Appropriate scale
  • Avoid misleading axes.
  • Limited visual complexity
  • Don't add unnecessary elements.
  • Readable text
  • Make sure labels are large enough.
  • Appropriate chart type

Use a chart that matches the analytical question.

6.8.50Color and Styling

Matplotlib allows extensive customization

plt.plot(
    months,
    sales,
    color="blue",
    linewidth=2,
    linestyle="--",
    marker="o"
)

You can also define styles, fonts, markers, and transparency.

For example

plt.plot(
    months,
    sales,
    alpha=0.7
)

However, customization should support readability rather than become decoration.

6.8.51Transparency

Transparency is controlled using alpha

plt.scatter(
    income,
    spending,
    alpha=0.5
)

This is useful when many points overlap.

6.8.52Chart Annotations for Business Events

  • Suppose revenue increased after a marketing campaign.
  • fig, ax = plt.subplots()
  • ax.plot(
  • months,
  • sales,
marker="o"

)

ax.annotate(

"Marketing Campaign",

xy=("Apr", 18000),
xytext=("Feb", 21000),
arrowprops=dict(
    arrowstyle="->"
)

)

plt.show()

This turns a chart into a simple business story.

6.8.53Advanced Example — Sales Dashboard

import pandas as pd
import matplotlib.pyplot as plt
df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar",
        "Apr", "May", "Jun"
    ],
    "Revenue": [
        100000,
        120000,
        115000,
        150000,
        165000,
        180000
    ],
    "Orders": [
        1000,
        1100,
        1050,
        1300,
        1400,
        1500
    ],
    "Profit": [
        20000,
        24000,
        23000,
        30000,
        35000,
        40000
    ]
})

Create dashboard

fig, axes = plt.subplots(

2,

2,

figsize=(12, 8)

)

  • # Revenue
  • axes[0, 0].plot(
  • df["Month"],
  • df["Revenue"],
marker="o"

)

axes[0, 0].set_title(

"Revenue Trend"

)

  • # Orders
  • axes[0, 1].bar(
  • df["Month"],
  • df["Orders"]

)

axes[0, 1].set_title(

"Order Volume"

)

  • # Profit
  • axes[1, 0].plot(
  • df["Month"],
  • df["Profit"],
marker="o"

)

axes[1, 0].set_title(

"Profit Trend"

)

  • # Revenue vs Orders
  • axes[1, 1].scatter(
  • df["Orders"],
  • df["Revenue"]

)

axes[1, 1].set_title(

"Revenue vs Orders"

)

plt.tight_layout()
plt.show()

This demonstrates how Matplotlib can be used to build a small analytical dashboard.

6.8.54Matplotlib with SQL Data

In real-world data analysis, data often comes from databases.

For example

import pandas as pd
import matplotlib.pyplot as plt
import sqlalchemy
engine = sqlalchemy.create_engine(
    "your_connection_string"
)
query = """
  • SELECT
  • OrderDate,
  • SUM(Revenue) AS Revenue
  • FROM Sales
  • GROUP BY OrderDate
  • ORDER BY OrderDate

"""

df = pd.read_sql(
    query,
    engine
)

Then

df["OrderDate"] = pd.to_datetime(

df["OrderDate"]

)

  • fig, ax = plt.subplots()
  • ax.plot(
  • df["OrderDate"],
  • df["Revenue"]

)

ax.set_title(

"Daily Revenue"

)

plt.show()

This is a common SQL → Pandas → Matplotlib workflow.

6.8.55Matplotlib with Power BI-Oriented Analysis

Even if the final dashboard is built in Power BI, Matplotlib is useful for

  • Exploratory data analysis
  • Validating KPIs
  • Checking distributions
  • Finding outliers
  • Comparing source data with dashboard data
  • Creating custom analytical images
  • Investigating trends before building the semantic model

Example workflow

SQL / CSV
Pandas
Data Cleaning
EDA
Matplotlib
Find Insights
Power BI Dashboard

6.8.56Common Matplotlib Mistakes

  • Mistake 1 — Missing labels
  • A chart without labels is difficult to interpret.
  • Mistake 2 — Wrong chart type
  • Don't use a pie chart for 20 categories.
  • Mistake 3 — Too many colors
  • Visual complexity can reduce readability.
  • Mistake 4 — Overlapping labels

Use

plt.xticks(
    rotation=45
)

when necessary.

  • Mistake 5 — Misleading axis limits
  • Always ensure the scale represents the data honestly.
  • Mistake 6 — Forgetting tight_layout()

For multiple charts

plt.tight_layout()
  • helps prevent overlapping elements.
  • Mistake 7 — Mixing plt state and object-oriented code carelessly
  • For simple charts, plt is convenient.

For complex visualizations, prefer

fig, ax = plt.subplots()

6.8.57Matplotlib Cheat Sheet

import matplotlib.pyplot as plt

# Line

plt.plot(x, y)

# Bar

plt.bar(x, y)

# Horizontal bar

plt.barh(x, y)

# Scatter

plt.scatter(x, y)

# Histogram

plt.hist(x)

# Box plot

plt.boxplot(x)

# Pie

plt.pie(x)

# Area

plt.fill_between(x, y)

# Title

plt.title("Title")

# X label

plt.xlabel("X")

# Y label

plt.ylabel("Y")

# Legend

plt.legend()

# Grid

plt.grid()

# Axis limits

plt.xlim(0, 10)
plt.ylim(0, 100)

# Rotate labels

plt.xticks(rotation=45)

# Save

plt.savefig("chart.png")

# Display

plt.show()

6.8.58Mini Project — Sales Visualization Dashboard

Create a dataset

  • Order_Date
  • Region
  • Product
  • Quantity
  • Revenue
  • Profit
  • Create the following charts.
  • Chart 1 — Monthly Revenue

Use

Line chart

Question

How is revenue changing over time?

Chart 2 — Regional Revenue

Use

Bar chart

Question

Which region generates the most revenue?

Chart 3 — Revenue Distribution

Use

Histogram

Question

How are transaction values distributed?

Chart 4 — Revenue vs Profit

Use

Scatter plot

Question

Is higher revenue associated with higher profit?

Chart 5 — Profit by Region

Use

Box plot

Question

Which regions have greater variation and potential outliers in profit?

Chart 6 — KPI Dashboard

Create a 2 × 2 dashboard containing

  • Revenue Trend
  • Order Volume
  • Profit Trend
  • Revenue vs Profit

Save it as

sales_dashboard.png

6.8.59Interview Questions

  • Basic
  • What is Matplotlib?
  • Why is Matplotlib used in data analysis?
  • What is pyplot?
  • What is the difference between Figure and Axes?
  • How do you create a line chart?
  • How do you create a bar chart?
  • How do you create a histogram?
  • How do you create a scatter plot?
  • How do you create a box plot?
  • How do you add titles and labels?

Intermediate

  • How do you create multiple charts in one figure?
  • What is plt.subplots()?
  • What is the difference between plt.plot() and ax.plot()?
  • How do you rotate x-axis labels?
  • How do you add a legend?
  • How do you add a reference line?
  • How do you annotate a chart?
  • How do you save a chart?
  • How do you visualize missing values?
  • How do you visualize outliers?

Advanced

  • What is the object-oriented approach in Matplotlib?
  • Why is it preferred for complex visualizations?
  • How do you create a dashboard using Matplotlib?
  • How do you visualize time-series data?
  • How do you format large numbers on an axis?
  • How do you handle overlapping labels?
  • How would you visualize millions of data points?
  • How would you create reusable plotting functions?
  • How would you connect SQL data to Matplotlib?
  • How would you use Matplotlib as part of an EDA workflow?

6.8.60Key Takeaways

The Matplotlib workflow is

DATA

Prepare Data

Create Figure

Create Axes

Plot Data

┌───────────┼───────────┐

▼ ▼ ▼

Title Labels Legend

│ │ │

└───────────┼───────────┘

Customize Axes

Add Context

/ Annotations

Save / Display

  • Most important charts to master
ChartMain Purpose
LineTrends over time
BarCategory comparison
HistogramDistribution
ScatterRelationship
Box PlotDistribution + outliers
PieSimple composition
AreaTrend + magnitude
SubplotsMultiple analytical views

The key principle is

A good visualization is not simply a chart that looks attractive; it is a chart that makes the underlying data and business insight easy to understand.

For a Data Analyst, the essential Matplotlib progression is

Line → Bar → Histogram → Scatter → Box Plot → Subplots → Business Dashboard → Advanced customization.

Module 6 · Lesson 6.9

Seaborn

Seaborn is a Python data-visualization library built on top of Matplotlib. It is designed specifically for statistical data visualization and works especially well with Pandas DataFrames.

Seaborn makes it easier to create informative visualizations with less code than raw Matplotlib.

It is particularly useful for

  • Exploratory Data Analysis (EDA)
  • Statistical distributions
  • Correlation analysis
  • Categorical comparisons
  • Regression analysis
  • Multivariate analysis
  • Heatmaps
  • Business analytics

6.9.1Learning Objectives

By the end of this topic, you should be able to

  • Understand Seaborn and its relationship with Matplotlib.
  • Create statistical visualizations.
  • Create distribution plots.
  • Create categorical plots.
  • Create relationship plots.
  • Create regression plots.
  • Create heatmaps.
  • Visualize correlations.
  • Work with Pandas DataFrames.
  • Customize Seaborn charts.
  • Build EDA visualizations.
  • Create multi-chart analytical views.

6.9.2Installing Seaborn

Install

pip install seaborn

Import

import seaborn as sns

It is commonly imported as

sns

You will normally also import Matplotlib

import matplotlib.pyplot as plt

6.9.3Seaborn vs Matplotlib

FeatureMatplotlibSeaborn
General plottingExcellentExcellent
Statistical chartsGoodExcellent
DataFrame integrationGoodExcellent
Default aestheticsBasicMore polished
Complex customizationExcellentExcellent
EDAGoodExcellent
HeatmapsPossibleVery convenient
Regression visualizationManualBuilt-in
Categorical visualizationMore codeVery convenient

A useful way to think about them

Matplotlib
Low-level plotting control
Seaborn
High-level statistical visualization
Built on Matplotlib

You can use both together.

6.9.4Basic Seaborn Example

import seaborn as sns
import matplotlib.pyplot as plt
tips = sns.load_dataset(
    "tips"
)

sns.scatterplot(

data=tips,
x="total_bill",
y="tip"

)

plt.show()

Seaborn automatically works with the DataFrame columns.

6.9.5Seaborn Dataset

Seaborn provides example datasets.

For example

tips = sns.load_dataset(
    "tips"
)
print(tips.head())

Other commonly used example datasets include

  • tips
  • iris
  • penguins
  • titanic
  • flights
  • car_crashes
  • diamonds

These datasets are useful for learning visualization techniques.

6.9.6Understanding the data, x, and y Pattern

One of Seaborn's most useful features is

sns.scatterplot(

data=df,
x="Income",
y="Spending"

)

Instead of manually extracting

  • df["Income"]
  • df["Spending"]
  • Seaborn understands the DataFrame structure directly.

6.9.7Scatter Plot

A scatter plot shows the relationship between two numerical variables.

sns.scatterplot(

data=tips,
x="total_bill",
y="tip"

)

plt.show()

Useful for identifying

  • Correlation
  • Clusters
  • Outliers
  • Nonlinear relationships

6.9.8Adding a Categorical Variable

Suppose we want to compare lunch and dinner.

sns.scatterplot(

data=tips,
x="total_bill",
y="tip",
hue="time"

)

plt.show()

hue separates observations by category.

6.9.9Using Size

You can encode another variable through point size

sns.scatterplot(

data=tips,
x="total_bill",
y="tip",
hue="time",
size="size"

)

plt.show()

This allows multiple dimensions to be represented in one chart.

6.9.10Using Style

Another variable can control marker style

sns.scatterplot(

data=tips,
x="total_bill",
y="tip",
hue="time",
style="sex"

)

plt.show()

Be careful not to encode too many variables at once because the chart can become difficult to read.

6.9.11Line Plot

Line charts are useful for trends.

sns.lineplot(

data=tips,
x="size",
y="tip"

)

plt.show()

For time-series data

sns.lineplot(

data=df,
x="Date",
y="Sales"

)

plt.show()

6.9.12Aggregation in Line Plots

Suppose multiple observations exist for each date.

Seaborn can aggregate values automatically in many cases

sns.lineplot(

data=df,
x="Month",
y="Sales"

)

Depending on the estimator and version/configuration, Seaborn can display an estimated central tendency and uncertainty.

For exact business KPI charts, however, it is often better to explicitly aggregate the data first:

monthly = (
df.groupby("Month", as_index=False)
\["Sales"\]

.sum()

)

Then plot

sns.lineplot(

data=monthly,
x="Month",
y="Sales"

)

This makes the business metric definition explicit.

6.9.13Bar Plot

Bar plots compare categories.

sns.barplot(

data=tips,
x="day",
y="total_bill"

)

plt.show()

Seaborn can calculate an aggregate statistic for each category.

For business reporting, if you already have a summarized dataset, plot that directly to avoid confusion about what is being aggregated.

6.9.14Bar Plot with Hue

sns.barplot(

data=tips,
x="day",
y="total_bill",
hue="sex"

)

plt.show()

This compares groups within each category.

6.9.15Count Plot

A count plot shows how many observations belong to each category.

sns.countplot(

data=tips,
x="day"

)

plt.show()

Useful for questions such as

How many transactions occurred on each day?

6.9.16Count Plot with Hue

sns.countplot(

data=tips,
x="day",
hue="sex"

)

plt.show()

This allows you to compare category counts.

6.9.17Box Plot

Seaborn makes box plots very convenient.

sns.boxplot(

data=tips,
x="day",
y="total_bill"

)

plt.show()

Box plots show

  • Median
  • Q1
  • Q3
  • IQR
  • Potential outliers

This connects directly to the Outlier Detection topic.

6.9.18Box Plot with Hue

sns.boxplot(

data=tips,
x="day",
y="total_bill",
hue="sex"

)

plt.show()

This allows distribution comparisons across multiple groups.

6.9.19Violin Plot

A violin plot combines ideas from a box plot and a density plot.

sns.violinplot(

data=tips,
x="day",
y="total_bill"

)

plt.show()

It shows

  • Distribution shape
  • Density
  • Median
  • Spread
  • Useful when comparing distributions across groups.

6.9.20Strip Plot

A strip plot displays individual observations.

sns.stripplot(

data=tips,
x="day",
y="total_bill"

)

plt.show()

This is useful when you want to see the actual data points rather than only summary statistics.

6.9.21Swarm Plot

A swarm plot arranges individual observations to reduce overlap.

sns.swarmplot(

data=tips,
x="day",
y="total_bill"

)

plt.show()

It can become crowded for very large datasets.

6.9.22Combining Box and Strip Plots

A useful EDA technique

sns.boxplot(

data=tips,
x="day",
y="total_bill"

)

sns.stripplot(

data=tips,
x="day",
y="total_bill",
alpha=0.5

)

plt.show()

The box plot shows the summary while the strip plot shows individual observations.

6.9.23Histogram

Seaborn provides histplot().

sns.histplot(

data=tips,
x="total_bill"

)

plt.show()

Control bins

sns.histplot(

data=tips,
x="total_bill",
bins=20

)

6.9.24Histogram by Category

sns.histplot(

data=tips,
x="total_bill",
hue="sex",
bins=20

)

plt.show()

This allows distributions to be compared.

6.9.25KDE Plot

  • KDE stands for Kernel Density Estimate.
  • It provides a smooth representation of a distribution.
  • sns.kdeplot(
data=tips,
x="total_bill"

)

plt.show()

KDE can be useful for comparing distribution shapes.

6.9.26KDE by Category

sns.kdeplot(

data=tips,
x="total_bill",
hue="sex"

)

plt.show()

This shows separate distributions for the categories.

6.9.27Histogram + KDE

sns.histplot(

data=tips,
x="total_bill",
kde=True

)

plt.show()

This provides both

Histogram

Smooth density estimate

6.9.28ECDF Plot

ECDF means Empirical Cumulative Distribution Function.

sns.ecdfplot(

data=tips,
x="total_bill"

)

plt.show()

It shows the proportion of observations less than or equal to each value.

6.9.29Regression Plot

Seaborn can easily display a regression relationship.

sns.regplot(

data=tips,
x="total_bill",
y="tip"

)

plt.show()

The chart contains

  • Scatter points
  • Regression line
  • Confidence interval

This is useful for exploratory relationship analysis.

6.9.30Regression by Category

For categorical comparisons

sns.lmplot(

data=tips,
x="total_bill",
y="tip",
hue="sex"

)

plt.show()

This creates separate regression relationships by category.

6.9.31Heatmap

Heatmaps are one of Seaborn's most useful features.

Suppose

corr = df.corr(
    numeric_only=True
)

Create

sns.heatmap(

corr,

annot=True

)

plt.show()

This visualizes correlations between numerical variables.

6.9.32Understanding Correlation Heatmaps

Example

Revenue Profit Orders

Revenue 1.00 0.85 0.72

Profit 0.85 1.00 0.65

Orders 0.72 0.65 1.00

Interpretation

  • +1 → Strong positive relationship
  • 0 → Little linear relationship
  • -1 → Strong negative relationship

Remember

Correlation does not prove causation.

6.9.33Heatmap with Formatting

sns.heatmap(

corr,

annot=True,
fmt=".2f"

)

plt.show()
fmt=".2f" displays values with two decimal places.

6.9.34Masking the Upper Triangle

Correlation matrices are symmetrical, so you can display only half.

import numpy as np
mask = np.triu(
    np.ones_like(
        corr,
        dtype=bool
    )
)

sns.heatmap(

corr,

mask=mask,
annot=True,
fmt=".2f"

)

plt.show()

This produces a cleaner correlation matrix.

6.9.35Pair Plot

  • A pair plot displays relationships among several numerical variables.
  • sns.pairplot(
  • tips

)

plt.show()

It creates

  • Scatter plots between variables
  • Distribution plots along the diagonal
  • Very useful during EDA.

6.9.36Pair Plot with Hue

sns.pairplot(

tips,

hue="sex"

)

plt.show()

This helps identify differences between groups.

6.9.37Joint Plot

A joint plot combines

  • Relationship plot
  • Marginal distributions
  • sns.jointplot(
data=tips,
x="total_bill",
y="tip"

)

plt.show()

Regression

sns.jointplot(

data=tips,
x="total_bill",
y="tip",
kind="reg"

)

6.9.38FacetGrid

FacetGrid allows the same chart to be repeated across categories.

g = sns.FacetGrid(
    tips,
    col="time"
)

g.map_dataframe(

sns.scatterplot,

x="total_bill",
y="tip"

)

plt.show()

This is useful for comparing patterns across groups.

6.9.39FacetGrid by Rows and Columns

g = sns.FacetGrid(
    tips,
    row="sex",
    col="time"
)

g.map_dataframe(

sns.scatterplot,

x="total_bill",
y="tip"

)

plt.show()

This can create a grid such as

Lunch Dinner

Male [Chart] [Chart]

Female [Chart] [Chart]

6.9.40Catplot

catplot() provides a figure-level interface for categorical plots.

sns.catplot(

data=tips,
x="day",
y="total_bill",
kind="box"

)

plt.show()

Change the chart type

sns.catplot(

data=tips,
x="day",
y="total_bill",
kind="violin"

)

or

sns.catplot(

data=tips,
x="day",
kind="count"

)

6.9.41Relplot

relplot() provides a figure-level interface for relational charts.

sns.relplot(

data=tips,
x="total_bill",
y="tip",
hue="sex"

)

plt.show()

You can create multiple panels

sns.relplot(

data=tips,
x="total_bill",
y="tip",
col="time"

)

6.9.42Seaborn Themes

Seaborn provides high-level styling options.

For example

sns.set_theme(

style="whitegrid"

)

Common styles include

  • white
  • dark
  • whitegrid
  • darkgrid
  • ticks

You can also set a context

sns.set_context(

"talk"

)

Common contexts include

  • paper
  • notebook
  • talk
  • poster

6.9.43Color Palettes

Seaborn provides many built-in palettes.

Example

sns.color_palette(

"deep"

)

For categorical plots

sns.barplot(

data=tips,
x="day",
y="total_bill",
palette="deep"

)

Use color intentionally. The goal should be clear communication rather than decoration.

6.9.44Sequential Palettes

Sequential palettes are useful when values represent magnitude.

Examples include

  • Blues
  • Greens
  • Oranges
  • Purples

Useful for

  • Heatmaps
  • Intensity
  • Geographic values
  • Ranking

6.9.45Diverging Palettes

Diverging palettes are useful when values have a meaningful midpoint, such as zero.

Example

Negative ← 0 → Positive

They are particularly useful for

  • Correlation matrices
  • Variance from target
  • Positive/negative change

6.9.46Categorical Palettes

Categorical palettes distinguish discrete groups.

Examples

  • Region
  • Product
  • Customer Segment
  • Department

Each category receives a distinct visual representation.

6.9.47Seaborn with Pandas

Suppose

df = pd.DataFrame({
    "Region": [
        "South",
        "South",
        "North",
        "North"
    ],
    "Revenue": [
        10000,
        12000,
        15000,
        14000
    ]
})

Plot

sns.barplot(

data=df,
x="Region",
y="Revenue"

)

plt.show()

Seaborn understands the DataFrame structure directly.

6.9.48Statistical Estimation

Seaborn's categorical plots can calculate summary statistics.

For example

sns.barplot(

data=tips,
x="day",
y="total_bill"

)

The bar represents an aggregate estimate, and Seaborn can display uncertainty around the estimate.

For business dashboards, however, it is important to know exactly what the aggregation represents.

For example, if you want total revenue, calculate total revenue first

daily_sales = (
    df.groupby(
        "day",
        as_index=False
    )["Revenue"]
    .sum()
)

Then

sns.barplot(

data=daily_sales,
x="day",
y="Revenue"

)

6.9.49Seaborn for Outlier Detection

Seaborn is excellent for visualizing outliers.

sns.boxplot(

data=df,
x="Region",
y="Revenue"

)

plt.show()

Potential outliers appear beyond the whiskers.

You can combine this with the IQR calculations from the previous topic.

6.9.50Seaborn for Missing-Value Analysis

A simple visualization

missing = (
    df.isnull()
    .sum()
    .sort_values(
        ascending=False
    )
)

sns.barplot(

x=missing.values,
y=missing.index

)

plt.show()

This provides a quick view of which columns contain the most missing values.

6.9.51Seaborn for Feature Analysis

Suppose a machine-learning dataset contains

  • Age
  • Income
  • Orders
  • Revenue
  • Churn

You can examine

sns.boxplot(

data=df,
x="Churn",
y="Income"

)

This helps answer

Do churned and non-churned customers have different income distributions?

6.9.52Seaborn for Business Analysis

Suppose an e-commerce company has

  • Region
  • Product
  • Revenue
  • Profit
  • Customer_Segment
  • Order_Date

Useful visualizations include

Revenue by Region

sns.barplot(

data=df,
x="Region",
y="Revenue"

)

Revenue Distribution

sns.histplot(

data=df,
x="Revenue",
kde=True

)

Profit by Customer Segment

sns.boxplot(

data=df,
x="Customer_Segment",
y="Profit"

)

Revenue vs Profit

sns.scatterplot(

data=df,
x="Revenue",
y="Profit",
hue="Region"

)

Correlation

sns.heatmap(

df.corr(numeric_only=True),
annot=True

)

6.9.53Seaborn EDA Workflow

A typical EDA workflow

DATASET

df.info()

df.describe()

Missing Value Analysis

Distribution Analysis

┌─────────┼─────────┐

▼ ▼ ▼

Hist Box KDE

│ │ │

└─────────┼─────────┘

Relationship Analysis

┌─────────┼─────────┐

▼ ▼ ▼

Scatter Regplot Pairplot

Categorical Analysis

┌─────────┼─────────┐

▼ ▼ ▼

Bar Box Violin

Correlation

Heatmap

INSIGHTS

6.9.54Complete EDA Example

Let's use the Seaborn tips dataset

import seaborn as sns
import matplotlib.pyplot as plt
df = sns.load_dataset(
    "tips"
)

Step 1 — Inspect

print(df.head())
print(df.info())
print(df.describe())

Step 2 — Check missing values

print(
    df.isnull().sum()
)

Step 3 — Distribution

sns.histplot(

data=df,
x="total_bill",
kde=True

)

plt.show()

Step 4 — Outliers

sns.boxplot(

data=df,
y="total_bill"

)

plt.show()

Step 5 — Relationship

sns.scatterplot(

data=df,
x="total_bill",
y="tip",
hue="time"

)

plt.show()

Step 6 — Category comparison

sns.boxplot(

data=df,
x="day",
y="total_bill"

)

plt.show()

Step 7 — Correlation

corr = df.corr(
    numeric_only=True
)

sns.heatmap(

corr,

annot=True,
fmt=".2f"

)

plt.show()

This is a basic but complete EDA workflow.

6.9.55Seaborn and Matplotlib Together

Seaborn doesn't replace Matplotlib.

For example

fig, ax = plt.subplots(

figsize=(10, 6)

)

sns.boxplot(

data=df,
x="day",
y="total_bill",
ax=ax

)

ax.set_title(

"Daily Transaction Distribution"

)

ax.set_xlabel(

"Day"

)

ax.set_ylabel(

"Transaction Amount"

)

plt.show()

This gives you

Seaborn
Statistical plot
Matplotlib
Fine-grained customization

6.9.56Figure-Level vs Axes-Level Functions

This is an important Seaborn concept.

Axes-level

Examples

  • scatterplot()
  • lineplot()
  • barplot()
  • boxplot()
  • histplot()

They typically draw onto a Matplotlib Axes.

Example

fig, ax = plt.subplots()

sns.scatterplot(

data=df,
x="Revenue",
y="Profit",
ax=ax

)

Figure-level

Examples

  • relplot()
  • catplot()
  • lmplot()
  • pairplot()
  • displot()

They manage the overall figure and can create multiple axes.

6.9.57displot()

displot() is a figure-level distribution interface.

sns.displot(

data=df,
x="total_bill",
kde=True

)

plt.show()

Facet by category

sns.displot(

data=df,
x="total_bill",
col="time"

)

6.9.58relplot()

sns.relplot(

data=df,
x="total_bill",
y="tip",
hue="sex",
col="time"

)

This is useful for creating small multiples.

6.9.59catplot()

sns.catplot(

data=df,
x="day",
y="total_bill",
hue="sex",
kind="box"

)

Change kind

  • strip
  • swarm
  • box
  • violin
  • boxen
  • point
  • bar
  • count

6.9.60pairplot()

For quick multivariate EDA

sns.pairplot(

df,

hue="sex"

)

plt.show()

Use it on datasets with a manageable number of variables. A pair plot becomes difficult to interpret when there are many columns.

6.9.61Business Dashboard Example

Suppose

df = pd.DataFrame({
    "Region": [
        "North", "South",
        "East", "West"
    ],
    "Revenue": [
        120000,
        180000,
        100000,
        150000
    ],
    "Profit": [
        25000,
        40000,
        18000,
        32000
    ],
    "Orders": [
        1000,
        1400,
        800,
        1200
    ]
})

Create a dashboard

fig, axes = plt.subplots(

2,

2,

figsize=(12, 8)

)

sns.barplot(

data=df,
x="Region",
y="Revenue",
ax=axes[0, 0]

)

axes[0, 0].set_title(

"Revenue by Region"

)

sns.barplot(

data=df,
x="Region",
y="Profit",
ax=axes[0, 1]

)

axes[0, 1].set_title(

"Profit by Region"

)

sns.scatterplot(

data=df,
x="Orders",
y="Revenue",
hue="Region",
ax=axes[1, 0]

)

axes[1, 0].set_title(

"Orders vs Revenue"

)

sns.heatmap(

df[

\["Revenue", "Profit", "Orders"\]

].corr(),

annot=True,
fmt=".2f",
ax=axes[1, 1]

)

axes[1, 1].set_title(

"KPI Correlation"

)

plt.tight_layout()
plt.show()

This combines Seaborn's statistical visualization with Matplotlib's layout management.

6.9.62Saving Seaborn Charts

Because Seaborn uses Matplotlib underneath

plt.savefig(
    "analysis.png",
    dpi=300,
    bbox_inches="tight"
)

Or using the figure

fig.savefig(

"analysis.png",

dpi=300,
bbox_inches="tight"

)

6.9.63Common Seaborn Mistakes

Mistake 1 — Too many variables

Avoid putting

  • hue
  • size
  • style
  • row
  • col
  • all into one chart unless there is a clear reason.
  • Mistake 2 — Misinterpreting bar plots
  • A Seaborn bar plot represents an aggregate statistic, not necessarily a total.
  • If you need total revenue, aggregate explicitly first.
  • Mistake 3 — Treating correlation as causation
  • A strong correlation doesn't prove one variable causes another.
  • Mistake 4 — Ignoring sample size
  • A visually strong difference may not be meaningful when based on very few observations.
  • Mistake 5 — Using pie charts for many categories
  • Use bars instead when categories become numerous.
  • Mistake 6 — Overloading pair plots
  • Pair plots are powerful but can become unreadable with too many features.

6.9.64Matplotlib vs Seaborn — Practical Decision

Use Matplotlib when

You need maximum customization
Precise axes control
Complex layouts
Custom annotations
Publication-quality figures

Use Seaborn when

You need statistical visualization
Quick EDA
Categorical comparisons
Distribution analysis
Correlation heatmaps
Regression visualization

Use both when

Seaborn creates the statistical chart

+

Matplotlib controls the final figure

6.9.65Seaborn Cheat Sheet

import seaborn as sns
import matplotlib.pyplot as plt

# Scatter

sns.scatterplot(

data=df,
x="x",
y="y"

)

# Line

sns.lineplot(

data=df,
x="Date",
y="Sales"

)

# Bar

sns.barplot(

data=df,
x="Category",
y="Sales"

)

# Count

sns.countplot(

data=df,
x="Category"

)

# Histogram

sns.histplot(

data=df,
x="Sales"

)

# KDE

sns.kdeplot(

data=df,
x="Sales"

)

# Box

sns.boxplot(

data=df,
x="Category",
y="Sales"

)

# Violin

sns.violinplot(

data=df,
x="Category",
y="Sales"

)

# Regression

sns.regplot(

data=df,
x="Sales",
y="Profit"

)

# Heatmap

sns.heatmap(

df.corr(numeric_only=True),
annot=True

)

  • # Pair plot
  • sns.pairplot(df)
  • # Joint plot
  • sns.jointplot(
data=df,
x="Sales",
y="Profit"

)

# Theme

sns.set_theme(

style="whitegrid"

)

plt.show()

6.9.66Mini Project — E-Commerce EDA with Seaborn

Create a dataset containing

  • Customer_ID
  • Order_Date
  • Region
  • Category
  • Quantity
  • Revenue
  • Profit
  • Customer_Segment
  • Perform the following analysis.
  • Step 1 — Dataset overview
df.info()
df.describe()
df.isnull().sum()

Step 2 — Revenue distribution

Create

Histogram + KDE

Question

Is revenue normally distributed or skewed?

Step 3 — Regional comparison

Create

Box plot

Question

Which region has the greatest revenue variation?

Step 4 — Category comparison

Create

Bar chart

Question

Which product category generates the most revenue?

Step 5 — Revenue vs Profit

Create

Scatter plot

Use

hue = Region

Question

Is revenue positively related to profit?

Step 6 — Correlation analysis

Create

Correlation heatmap

Analyze

  • Quantity
  • Revenue
  • Profit
  • Discount
  • Step 7 — Customer segmentation

Create

Box plot

Compare

  • Customer_Segment
  • vs
  • Revenue
  • Step 8 — Multivariate analysis

Create

sns.pairplot(

df[

[

  • "Quantity",
  • "Revenue",
  • "Profit"

]

]

)

6.9.67Interview Questions

  • Basic
  • What is Seaborn?
  • How is Seaborn related to Matplotlib?
  • Why is Seaborn useful for EDA?
  • How do you create a scatter plot?
  • What is the hue parameter?
  • What is a count plot?
  • What is a box plot?
  • What is a violin plot?
  • What is a histogram?
  • What is a heatmap?

Intermediate

  • What is the difference between barplot() and countplot()?
  • What is the difference between boxplot() and violinplot()?
  • What is pairplot()?
  • What is jointplot()?
  • What is FacetGrid?
  • What is catplot()?
  • What is relplot()?
  • How do you visualize correlations?
  • How do you add categories to a scatter plot?
  • How do you customize Seaborn charts using Matplotlib?

Advanced

  • What is the difference between axes-level and figure-level functions?
  • How does Seaborn perform statistical aggregation?
  • Why might you aggregate data before using barplot()?
  • How would you visualize outliers using Seaborn?
  • How would you analyze a highly skewed variable?
  • How would you visualize relationships among 10 numerical variables?
  • How would you create a reusable Seaborn visualization function?
  • How would you combine Seaborn and Matplotlib for a dashboard?
  • How would you visualize missing values using Seaborn?
  • How would you design a complete EDA workflow using Seaborn?

6.9.68Key Takeaways

The Seaborn workflow is

DATA

Pandas DF

Data Profiling

┌──────────┼──────────┐

▼ ▼ ▼

Distribution Category Relationship

│ │ │

▼ ▼ ▼

Hist/KDE Bar/Box Scatter/Reg

│ │ │

└──────────┼──────────┘

Heatmap

Pair Plot

EDA

BUSINESS

INSIGHTS

  • Most important Seaborn functions
FunctionPurpose
scatterplot()Relationship
lineplot()Trend
barplot()Aggregate comparison
countplot()Category counts
histplot()Distribution
kdeplot()Density
boxplot()Distribution + outliers
violinplot()Distribution shape
regplot()Regression relationship
heatmap()Correlation/matrix
pairplot()Multivariate EDA
jointplot()Relationship + distributions
catplot()Categorical visualization
relplot()Relational visualization

Seaborn is especially powerful for EDA because it combines Pandas-friendly syntax with statistical visualization capabilities.

The key progression to master is

Scatter → Line → Bar → Count → Histogram/KDE → Box/Violin → Heatmap → Regression → Pair Plot → Facets → EDA Dashboard.

Module 6 · Lesson 6.10

Plotly

Plotly is a Python visualization library used to create interactive, web-based, and highly customizable visualizations.

Unlike traditional static charts created with Matplotlib, Plotly charts allow users to

  • Hover over data points
  • Zoom in and out
  • Pan across a chart
  • Select data
  • Toggle series on/off
  • View detailed tooltips
  • Interact with maps
  • Build interactive dashboards

Plotly is therefore particularly useful for business analytics, data exploration, dashboards, and web applications.

6.10.1Learning Objectives

By the end of this topic, you should be able to

Understand Plotly and its architecture.

  • Create interactive charts.
  • Create line, bar, scatter, and pie charts.
  • Create interactive histograms and box plots.
  • Build heatmaps.
  • Create financial charts.
  • Create geographic visualizations.
  • Use Plotly Express.
  • Use Plotly Graph Objects.
  • Add hover information.
  • Add filters and dropdowns.
  • Create subplots.
  • Build interactive dashboards.
  • Export Plotly charts.

6.10.2Installing Plotly

Install

pip install plotly

Import Plotly Express

import plotly.express as px

For advanced control

import plotly.graph_objects as go

6.10.3Plotly Express vs Graph Objects

Plotly provides two major interfaces.

Plotly Express

High-level API

px.line(...)

px.bar(...)

px.scatter(...)

px.histogram(...)

Best for

  • Quick charts
  • EDA
  • Business analysis
  • DataFrames
  • Graph Objects

Lower-level API

go.Figure()

go.Scatter(...)

go.Bar(...)

Best for

  • Complex charts
  • Multiple traces
  • Custom interactions
  • Advanced dashboards

A useful progression is

Plotly Express
Graph Objects
Advanced interactive dashboards

6.10.4Basic Plotly Example

import plotly.express as px
df = px.data.iris()
fig = px.scatter(
    df,
    x="sepal_width",
    y="sepal_length"
)

fig.show()

Unlike a static Matplotlib chart, the resulting chart is interactive.

6.10.5Why Plotly Is Useful

Imagine a sales chart with 100,000 transactions.

A static chart may show

Revenue Trend

──────────────

/\

/\ / \__

/ \/ \

Plotly allows the user to

  • Hover → See exact value
  • Zoom → Inspect specific period
  • Pan → Move through data
  • Legend → Hide/show series
  • Select → Explore subset

This makes Plotly very useful for interactive analytics.

6.10.6Line Chart

import pandas as pd
import plotly.express as px
df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar",
        "Apr", "May", "Jun"
    ],
    "Revenue": [
        10000, 12000, 15000,
        13000, 18000, 20000
    ]
})
fig = px.line(
    df,
    x="Month",
    y="Revenue",
    title="Monthly Revenue"
)

fig.show()

The user can hover over each point to see the underlying value.

6.10.7Adding Markers

fig = px.line(
    df,
    x="Month",
    y="Revenue",
    markers=True,
    title="Monthly Revenue"
)

fig.show()

6.10.8Multiple Lines

Suppose

df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar",
        "Jan", "Feb", "Mar"
    ],
    "Product": [
        "A", "A", "A",
        "B", "B", "B"
    ],
    "Revenue": [
        100, 120, 150,
        90, 130, 160
    ]
})

Create

fig = px.line(
    df,
    x="Month",
    y="Revenue",
    color="Product",
    markers=True
)

fig.show()

Plotly automatically creates separate series.

6.10.9Bar Chart

df = pd.DataFrame({
    "Product": [
        "Laptop",
        "Phone",
        "Tablet",
        "Monitor"
    ],
    "Sales": [
        50000,
        80000,
        30000,
        45000
    ]
})
fig = px.bar(
    df,
    x="Product",
    y="Sales",
    title="Product Sales"
)

fig.show()

6.10.10Horizontal Bar Chart

fig = px.bar(
    df,
    x="Sales",
    y="Product",
    orientation="h",
    title="Product Sales"
)

fig.show()

Horizontal charts work well when category names are long.

6.10.11Grouped Bar Chart

df = pd.DataFrame({
    "Region": [
        "North", "North",
        "South", "South"
    ],
    "Product": [
        "Laptop", "Phone",
        "Laptop", "Phone"
    ],
    "Sales": [
        50000, 70000,
        60000, 80000
    ]
})
fig = px.bar(
    df,
    x="Region",
    y="Sales",
    color="Product",
    barmode="group"
)

fig.show()

6.10.12Stacked Bar Chart

fig = px.bar(
    df,
    x="Region",
    y="Sales",
    color="Product",
    barmode="stack"
)

fig.show()

This is useful for showing composition.

6.10.13Scatter Plot

Scatter plots are excellent for interactive relationship analysis.

df = pd.DataFrame({
    "Income": [
        30000, 40000,
        50000, 60000,
        70000, 80000
    ],
    "Spending": [
        2000, 2500,
        3000, 4000,
        5000, 6500
    ]
})
fig = px.scatter(
    df,
    x="Income",
    y="Spending",
    title="Income vs Spending"
)

fig.show()

Hovering over points reveals values.

6.10.14Scatter Plot with Color

fig = px.scatter(
    df,
    x="Income",
    y="Spending",
    color="Region"
)

If Region exists, each region can be visually distinguished.

6.10.15Scatter Plot with Size

You can encode another variable using marker size

fig = px.scatter(
    df,
    x="Income",
    y="Spending",
    color="Region",
    size="Order_Count"
)

Now the chart can represent

  • X → Income
  • Y → Spending
  • Color → Region
  • Size → Orders

This is a multivariate visualization.

6.10.16Hover Information

Plotly automatically creates hover information.

You can customize it

fig = px.scatter(
    df,
    x="Income",
    y="Spending",
    hover_data=[
        "Customer_ID",
        "Region"
    ]
)

Hovering over a point can then display additional fields.

6.10.17Custom Hover Templates

For advanced control

fig.update_traces(

hovertemplate=

"Revenue: %{y}<extra></extra>"

)

Graph Objects provides even more detailed control.

6.10.18Histogram

fig = px.histogram(
    df,
    x="Revenue",
    nbins=20,
    title="Revenue Distribution"
)

fig.show()

Users can zoom into the distribution interactively.

6.10.19Histogram with Color

fig = px.histogram(
    df,
    x="Revenue",
    color="Region",
    nbins=20
)

fig.show()

6.10.20Box Plot

fig = px.box(
    df,
    x="Region",
    y="Revenue",
    title="Revenue Distribution by Region"
)

fig.show()

This is particularly useful for interactive outlier analysis.

6.10.21Box Plot with Points

You can show individual observations

fig = px.box(
    df,
    x="Region",
    y="Revenue",
    points="all"
)

fig.show()

This combines

Box plot

Individual observations

6.10.22Violin Plot

fig = px.violin(
    df,
    x="Region",
    y="Revenue",
    box=True,
    points="all"
)

fig.show()

This provides an interactive view of distribution shape.

6.10.23Pie Chart

fig = px.pie(
    df,
    names="Product",
    values="Sales",
    title="Sales Distribution"
)

fig.show()

Plotly makes pie charts interactive.

Users can click legend items to hide/show categories.

6.10.24Donut Chart

A donut chart can be created by using

fig = px.pie(
    df,
    names="Product",
    values="Sales",
    hole=0.4
)

fig.show()

hole creates the center opening.

6.10.25Area Chart

fig = px.area(
    df,
    x="Month",
    y="Revenue",
    title="Revenue Trend"
)

fig.show()

For multiple categories

fig = px.area(
    df,
    x="Month",
    y="Revenue",
    color="Product"
)

6.10.26Treemap

Treemaps are useful for hierarchical composition.

Example

fig = px.treemap(
    df,
    path=[
        "Region",
        "Category",
        "Product"
    ],
    values="Revenue"
)

fig.show()

This can provide an interactive hierarchy such as

Region

├── Category

│ ├── Product A

│ └── Product B

  • └── Category
  • ├── Product C
  • └── Product D

6.10.27Sunburst Chart

Another hierarchical visualization

fig = px.sunburst(
    df,
    path=[
        "Region",
        "Category",
        "Product"
    ],
    values="Revenue"
)

fig.show()

Users can click into levels of the hierarchy.

6.10.28Funnel Chart

Useful for business conversion analysis.

Suppose

df = pd.DataFrame({
    "Stage": [
        "Visitors",
        "Leads",
        "Qualified Leads",
        "Customers"
    ],
    "Count": [
        10000,
        5000,
        2000,
        800
    ]
})

Create

fig = px.funnel(
    df,
    y="Stage",
    x="Count"
)

fig.show()

This is excellent for

  • Sales pipelines
  • Marketing funnels
  • Conversion analysis
  • Recruitment pipelines

6.10.29Heatmap

Plotly can create interactive heatmaps

fig = px.imshow(
    correlation_matrix,
    text_auto=True
)

fig.show()

Users can hover over cells to inspect values.

6.10.30Geographic Maps

Plotly is particularly strong for geographic visualization.

Example

fig = px.scatter_geo(
    df,
    locations="Country",
    locationmode="country names",
    size="Sales"
)

fig.show()

This can create an interactive world map.

6.10.31Scatter Map

For latitude and longitude

fig = px.scatter_map(
    df,
    lat="Latitude",
    lon="Longitude",
    size="Sales",
    hover_name="City",
    zoom=4
)

fig.show()

The exact map API available depends on the Plotly version, and modern Plotly versions favor MapLibre-based functions for many map use cases.

6.10.32Time-Series Visualization

Suppose

df["Date"] = pd.to_datetime(

df["Date"]

)

Create

fig = px.line(
    df,
    x="Date",
    y="Revenue",
    title="Revenue Over Time"
)

fig.show()

Users can

  • Zoom
  • Select date ranges
  • Pan
  • Hover

This is extremely useful for business monitoring.

6.10.33Range Slider

For time-series charts

fig = px.line(
    df,
    x="Date",
    y="Revenue"
)

fig.update_xaxes(

rangeslider_visible=True

)

fig.show()

A range slider appears beneath the chart.

6.10.34Range Selector

You can provide predefined periods

fig.update_xaxes(

rangeselector=dict(
    buttons=[
        dict(
            count=1,
            label="1m",
            step="month",
            stepmode="backward"
        ),
        dict(
            count=6,
            label="6m",
            step="month",
            stepmode="backward"
        ),
        dict(
            step="all"
        )
    ]
)

)

Users can quickly switch between periods.

6.10.35Trendline

Plotly Express can add regression trendlines

fig = px.scatter(
    df,
    x="Revenue",
    y="Profit",
    trendline="ols"
)

fig.show()

This can help explore linear relationships.

You may need the statsmodels dependency for some trendline functionality

pip install statsmodels

6.10.36Faceting

Plotly can create multiple panels.

fig = px.scatter(
    df,
    x="Revenue",
    y="Profit",
    facet_col="Region"
)

fig.show()

This is useful for comparing relationships across regions.

6.10.37Animation

Plotly supports animated visualizations.

Suppose

fig = px.scatter(
    df,
    x="Income",
    y="Spending",
    animation_frame="Year",
    animation_group="Customer_ID",
    size="Revenue",
    color="Region"
)

fig.show()

This allows the user to move through time interactively.

6.10.38Animation Use Cases

Animations can be useful for

  • Population changes
  • Sales growth
  • Geographic changes
  • Market trends
  • Customer behavior

However, don't use animation simply because it looks impressive. It should reveal a meaningful temporal pattern.

6.10.39Graph Objects

For more control

import plotly.graph_objects as go
fig = go.Figure()

fig.add_trace(

go.Scatter(

x=["Jan", "Feb", "Mar"],
y=[100, 120, 150],
mode="lines+markers",
name="Revenue"

)

)

fig.show()

This gives you direct control over chart traces.

6.10.40Understanding a Trace

A trace is an individual data series in a Plotly figure.

For example

  • Figure
  • ├── Trace 1 → Product A
  • ├── Trace 2 → Product B
  • └── Trace 3 → Product C

You can add multiple traces

fig.add_trace(

go.Scatter(

x=months,
y=product_a,
name="Product A"

)

)

fig.add_trace(

go.Scatter(

x=months,
y=product_b,
name="Product B"

)

)

6.10.41Updating Layout

Plotly provides extensive layout control

fig.update_layout(

title="Monthly Revenue",
xaxis_title="Month",
yaxis_title="Revenue"

)

fig.show()

6.10.42Updating Traces

fig.update_traces(

mode="lines+markers"

)

This can modify all relevant traces.

6.10.43Updating Axes

fig.update_xaxes(

title="Month"

)

fig.update_yaxes(

title="Revenue"

)

You can also configure

  • tick formatting
  • ranges
  • gridlines
  • logarithmic scales
  • date formatting

6.10.44Logarithmic Axis

For highly skewed data

fig.update_yaxes(

type="log"

)

fig.show()

This can make wide-ranging values easier to inspect.

Be sure to clearly communicate when an axis is logarithmic.

6.10.45Custom Hover Templates

With Graph Objects

fig = go.Figure(
    go.Scatter(
        x=df["Month"],
        y=df["Revenue"],
        mode="lines+markers",
        hovertemplate=
        "<b>%{x}</b><br>" +
        "Revenue: ₹%{y:,.0f}" +
        "<extra></extra>"
    )
)

fig.show()

This creates business-friendly tooltips.

6.10.46Subplots

Plotly can create multiple charts in one figure.

from plotly.subplots import make_subplots
fig = make_subplots(
    rows=2,
    cols=2
)

Add charts

fig.add_trace(

go.Bar(

x=months,
y=revenue,
name="Revenue"

),

row=1,
col=1

)

fig.add_trace(

go.Scatter(

x=months,
y=profit,
name="Profit"

),

row=1,
col=2

)

Then

fig.update_layout(

title="Business Dashboard"

)

fig.show()

6.10.47Interactive Dropdowns

Plotly can provide dropdown controls.

Example

fig.update_layout(

updatemenus=[
    dict(
        buttons=[
            dict(
                label="Revenue",
                method="update",
                args=[
                    {"visible": [True, False]}
                ]
            ),
            dict(
                label="Profit",
                method="update",
                args=[
                    {"visible": [False, True]}
                ]
            )
        ]
    )
]

)

This lets the user switch between metrics.

6.10.48Interactive Dashboards

Plotly charts can be used with

  • Dash
  • Streamlit
  • Jupyter
  • Flask applications
  • Django applications
  • HTML files
  • Web applications

A common architecture is

Database
Python
Pandas
Plotly
Interactive Chart
Web Application

6.10.49Plotly + Dash

Plotly Dash is a framework for building analytical web applications.

Conceptually

Dashboard

┌─────────┼─────────┐

▼ ▼ ▼

KPI Chart Table

│ │ │

└─────────┼─────────┘

Filters

Python Logic

Database

For example, a monitoring portal could provide

  • Pipeline Health
  • Pipeline Success Rate
  • Execution Trend
  • Failed Pipelines
  • Average Duration
  • with interactive Plotly charts.

6.10.50Plotly with Pandas

Plotly works naturally with DataFrames.

fig = px.bar(
    df,
    x="Region",
    y="Revenue",
    color="Product"
)

No need to manually extract every column.

This makes Plotly particularly convenient for data analysts.

6.10.51Plotly with SQL

A common workflow

import pandas as pd
import plotly.express as px
import sqlalchemy
engine = sqlalchemy.create_engine(
    "your_connection_string"
)
query = """
  • SELECT
  • OrderDate,
  • SUM(Revenue) AS Revenue
  • FROM Sales
  • GROUP BY OrderDate
  • ORDER BY OrderDate

"""

df = pd.read_sql(
    query,
    engine
)

Then

df["OrderDate"] = pd.to_datetime(

df["OrderDate"]

)

fig = px.line(
    df,
    x="OrderDate",
    y="Revenue",
    title="Daily Revenue"
)

fig.show()

This is a practical

SQL → Pandas → Plotly

workflow.

6.10.52Plotly for Data Quality Monitoring

Plotly is useful for monitoring data pipelines.

Suppose

  • Pipeline
  • Run_Date
  • Status
  • Duration
  • Records

You can create

Pipeline Success Trend

fig = px.line(
    df,
    x="Run_Date",
    y="Success_Rate",
    color="Pipeline"
)

Pipeline Duration

fig = px.box(
    df,
    x="Pipeline",
    y="Duration"
)

Record Count

fig = px.bar(
    df,
    x="Pipeline",
    y="Records"
)

This makes Plotly highly suitable for monitoring dashboards.

6.10.53Exporting Plotly Charts to HTML

  • One of Plotly's major strengths is standalone HTML export.
  • fig.write_html(
  • "sales_dashboard.html"

)

The generated HTML can be opened in a browser.

This makes Plotly charts easy to share.

6.10.54Exporting Static Images

Plotly can also export images.

For example

fig.write_image(

"chart.png"

)

Static image export commonly uses Kaleido.

Install if needed

pip install -U kaleido

6.10.55Plotly in Jupyter Notebook

Plotly works well in Jupyter

fig.show()

The chart appears directly inside the notebook.

This makes Plotly useful for

  • Data exploration
  • Academic work
  • Analytics reports
  • Data science experiments

6.10.56Plotly in Web Applications

Plotly can be integrated into web applications using frameworks such as

  • Dash
  • Streamlit
  • Django
  • Flask

For example

Django Application
Pandas DataFrame
Plotly Figure
HTML / JavaScript
Browser

This makes Plotly a strong option for custom analytics portals.

6.10.57Plotly vs Matplotlib vs Seaborn

FeatureMatplotlibSeabornPlotly
Static chartsExcellentExcellentYes
Interactive chartsLimitedLimitedExcellent
EDAGoodExcellentExcellent
Statistical chartsGoodExcellentExcellent
Web dashboardsPossiblePossibleExcellent
DataFrame supportGoodExcellentExcellent
CustomizationExcellentExcellentExcellent
HoverLimitedLimitedExcellent
Zoom/PanLimitedLimitedExcellent
MapsPossibleLimitedExcellent
Business dashboardsGoodGoodExcellent

A useful mental model

  • Matplotlib
  • → Maximum static control
  • Seaborn
  • → Statistical EDA
  • Plotly
  • → Interactive analytics

6.10.58When Should You Use Plotly?

Use Plotly when users need to

  • Explore data interactively.
  • Hover over points.
  • Zoom into trends.
  • Filter or inspect categories.
  • Explore large time-series datasets.
  • Navigate geographic data.
  • Use interactive dashboards.
  • Share charts through HTML.
  • Build web-based analytics.

Use Matplotlib when you mainly need

  • Static reports
  • Publication-style charts
  • Fine-grained static layout control

Use Seaborn when you mainly need

  • Statistical EDA
  • Distributions
  • Correlations
  • Categorical comparisons

6.10.59Common Plotly Mistakes

Mistake 1 — Too much interactivity

Not every chart needs

  • Dropdowns
  • Animation
  • Multiple filters
  • Complex hover templates

Use interaction only when it improves analysis.

Mistake 2 — Too many dimensions

A chart with

  • X
  • Y
  • Color
  • Size
  • Symbol
  • Facet
  • Animation
  • may become difficult to understand.
  • Mistake 3 — Incorrect aggregation
  • Always understand what your metric represents.

For example

Revenue

could mean

  • AVG(Revenue)
  • COUNT(Revenue)
  • These answer very different business questions.
  • Mistake 4 — Poor hover information
  • Hover data should provide useful context, not every column in the DataFrame.
  • Mistake 5 — Misleading visual scales
  • Interactive charts should still use honest scales.

6.10.60Complete Sales Dashboard Example

Suppose

df = pd.DataFrame({
    "Month": [
        "Jan", "Feb", "Mar",
        "Apr", "May", "Jun"
    ],
    "Revenue": [
        100000,
        120000,
        115000,
        150000,
        165000,
        180000
    ],
    "Profit": [
        20000,
        24000,
        23000,
        30000,
        35000,
        40000
    ],
    "Orders": [
        1000,
        1100,
        1050,
        1300,
        1400,
        1500
    ]
})

Revenue chart

fig = px.line(
    df,
    x="Month",
    y="Revenue",
    markers=True,
    title="Revenue Trend"
)

fig.show()

Profit chart

fig = px.bar(
    df,
    x="Month",
    y="Profit",
    title="Monthly Profit"
)

fig.show()

Orders chart

fig = px.line(
    df,
    x="Month",
    y="Orders",
    markers=True,
    title="Order Volume"
)

fig.show()

Revenue vs Orders

fig = px.scatter(
    df,
    x="Orders",
    y="Revenue",
    size="Profit",
    hover_data=["Month"],
    title="Orders vs Revenue"
)

fig.show()

These four charts provide an interactive analytical view of the business.

6.10.61Advanced Business Dashboard Concept

A complete Plotly dashboard could look like

┌─────────────────────────────────────────────┐

│ SALES PERFORMANCE │

├────────────┬────────────┬───────────────────┤

│ Revenue │ Profit │ Orders │

│ ₹18.0L │ ₹4.0L │ 1,500 │

├────────────┴────────────┴───────────────────┤

│ │

│ Revenue Trend │

│ Interactive │

│ │

├──────────────────────┬──────────────────────┤

│ Revenue by Region │ Profit Distribution │

│ Interactive Bar │ Box / Violin │

├──────────────────────┼──────────────────────┤

│ Orders vs Revenue │ Product Mix │

│ Scatter │ Treemap │

└──────────────────────┴──────────────────────┘

Users can

  • Hover
  • Zoom
  • Filter
  • Select
  • Toggle
  • Drill into hierarchy

6.10.62Plotly Mini Project

Project: Interactive E-Commerce Dashboard

Create a dataset containing

  • Order_ID
  • Order_Date
  • Customer_ID
  • Region
  • Category
  • Product
  • Quantity
  • Revenue
  • Profit
  • Customer_Segment
  • Step 1 — Data Cleaning

Convert

  • Order_Date → datetime
  • Quantity → numeric
  • Revenue → numeric
  • Profit → numeric
  • Step 2 — Revenue Trend

Create

Interactive line chart

Show

  • Date
  • Revenue
  • Category
  • Step 3 — Regional Performance

Create

Interactive bar chart

Show

  • Region
  • Revenue
  • Profit
  • Step 4 — Customer Segmentation

Create

Box plot

Compare

  • Customer_Segment
  • Revenue
  • Step 5 — Product Analysis

Create

Treemap

Hierarchy

Region
Category
Product

Value

Revenue

Step 6 — Correlation

Create

Heatmap

Use

  • Quantity
  • Revenue
  • Profit
  • Step 7 — Geographic Analysis

If latitude/longitude are available

Interactive map

Show

  • City
  • Revenue
  • Orders
  • Step 8 — Add Interactivity

Implement

  • Hover
  • Zoom
  • Legend filtering
  • Category filtering
  • Date range
  • Step 9 — Export

Save the dashboard

fig.write_html(

"ecommerce_dashboard.html"

)

6.10.63Interview Questions

  • Basic
  • What is Plotly?
  • Why is Plotly different from Matplotlib?
  • What is Plotly Express?
  • What are Graph Objects?
  • How do you create an interactive line chart?
  • How do you create a scatter plot?
  • How do you create a bar chart?
  • How do you create a histogram?
  • How do you create a box plot?
  • How do you create a heatmap?

Intermediate

  • What is a Plotly trace?
  • What is fig.update_layout()?
  • What is fig.update_traces()?
  • How do you customize hover information?
  • How do you create subplots?
  • How do you create a range slider?
  • How do you create dropdown controls?
  • How do you create a treemap?
  • How do you create a funnel chart?
  • How do you create geographic visualizations?

Advanced

  • What is the difference between Plotly Express and Graph Objects?
  • When would you use Graph Objects instead of Plotly Express?
  • How do you create interactive dashboards?
  • How can Plotly integrate with Django?
  • How can Plotly integrate with Dash?
  • How do you export Plotly charts to HTML?
  • How do you export Plotly charts to PNG?
  • How do you create animated visualizations?
  • How would you visualize millions of time-series records?
  • How would you design an interactive executive KPI dashboard using Plotly?

6.10.64Key Takeaways

The Plotly workflow is

DATA

Pandas

Plotly Express

┌──────────────┼──────────────┐

▼ ▼ ▼

Trend Distribution Relationship

│ │ │

▼ ▼ ▼

Line Hist/Box Scatter

│ │ │

└──────────────┼──────────────┘

Graph Objects

Advanced Controls

┌─────────────┼─────────────┐

▼ ▼ ▼

Filters Dropdowns Sliders

│ │ │

└─────────────┼─────────────┘

Interactive Dashboard

┌────────┴────────┐

▼ ▼

HTML Web App

  • Most important Plotly functions
FunctionPurpose
px.line()Interactive trends
px.bar()Category comparison
px.scatter()Relationships
px.histogram()Distribution
px.box()Outliers/distribution
px.violin()Distribution shape
px.pie()Composition
px.area()Trend + magnitude
px.treemap()Hierarchical data
px.sunburst()Hierarchical composition
px.funnel()Conversion stages
px.imshow()Heatmaps
px.scatter_map()Geographic data
px.scatter_geo()Geographic analysis
px.line() + range sliderInteractive time series
go.Figure()Advanced custom figures

Plotly turns data visualization from a static picture into an interactive analytical experience.

For a Data Analyst, the progression to master is

Plotly Express → Interactive Line/Bar/Scatter → Distribution → Box/Violin → Heatmap → Time Series → Maps → Treemap → Graph Objects → Subplots → Filters/Dropdowns → Interactive Dashboard.

Module 6 · Lesson 6.11

EDA Techniques

Exploratory Data Analysis (EDA) is the systematic process of examining, cleaning, summarizing, and visualizing a dataset before performing statistical analysis or building machine-learning models.

EDA helps answer

What is in the data, what is unusual, what relationships exist, and what should we do next?

EDA is one of the most important skills for a Data Analyst because it connects raw data → understanding → business insight.

6.11.1Learning Objectives

By the end of this topic, you should be able to

  • Understand the purpose of EDA.
  • Inspect a dataset.
  • Understand data types and structure.
  • Analyze numerical and categorical variables.
  • Detect missing values.
  • Detect duplicates.
  • Identify outliers.
  • Analyze distributions.
  • Analyze correlations.
  • Identify relationships between variables.
  • Perform univariate, bivariate, and multivariate analysis.
  • Use Pandas, Matplotlib, Seaborn, and Plotly for EDA.
  • Generate business insights from data.
  • Build a reusable EDA workflow.

6.11.2What Is EDA?

EDA stands for

Exploratory Data Analysis

It was popularized by statistician John Tukey.

The basic idea is

Raw Dataset
Understand Structure
Check Data Quality
Explore Variables
Find Relationships
Identify Patterns
Detect Anomalies
Generate Insights
Prepare for Modeling / Reporting

EDA is not simply creating charts.

It combines

Statistics

+

Data Cleaning

+

Visualization

+

Domain Knowledge

+

Critical Thinking

6.11.3Why Is EDA Important?

Imagine you receive this dataset

  • Customer_ID
  • Age
  • Income
  • Region
  • Orders
  • Revenue
  • Profit

Before calculating KPIs, you need to know

  • Are there missing values?
  • Are ages valid?
  • Are there duplicate customers?
  • Are revenue values negative?
  • Are some regions incorrectly spelled?
  • Are there extreme revenue values?
  • Is revenue highly skewed?
  • Are orders related to revenue?
  • Which region performs best?

EDA helps answer these questions.

6.11.4Types of EDA

There are three major levels.

1. Univariate Analysis

Analyze one variable.

Example

Revenue

Questions

  • What is the average?
  • What is the median?
  • What is the distribution?
  • Are there outliers?

2. Bivariate Analysis

Analyze two variables.

Example

Revenue vs Profit

Questions

  • Are they correlated?
  • Does one change with the other?
  • Are there differences between groups?

3. Multivariate Analysis

Analyze three or more variables.

Example

  • Revenue
  • vs
  • Profit
  • by
  • Region
  • and
  • Customer Segment
  • This helps identify complex patterns.

6.11.5Step 1 — Load the Data

For CSV

import pandas as pd
df = pd.read_csv(
    "sales.csv"
)

For Excel

df = pd.read_excel(
    "sales.xlsx"
)

For SQL

df = pd.read_sql(
    query,
    connection
)

6.11.6Step 2 — Inspect the First Records

Use

df.head()

Default

First 5 rows

You can specify

df.head(10)

This is useful for understanding

  • Column names
  • Data values
  • Formatting
  • Potential data quality issues

6.11.7Inspect the Last Records

df.tail()

This can help identify issues at the end of the dataset.

6.11.8Random Sample

Instead of looking only at the beginning

df.sample(10)

Random sampling is often useful for discovering unexpected values.

6.11.9Dataset Shape

Use

df.shape

Example

(100000, 15)

Means

100,000 rows

15 columns

6.11.10Column Names

df.columns

Convert to a list

df.columns.tolist()

6.11.11Data Types

Use

df.dtypes

Typical types

  • int64
  • float64
  • object
  • string
  • bool
  • datetime64
  • category

Understanding data types is critical before analysis.

6.11.12Dataset Information

One of the most important EDA commands

df.info()

It shows

  • Number of rows
  • Columns
  • Non-null counts
  • Data types
  • Memory usage

Example

  • <class 'pandas.DataFrame'>
  • RangeIndex: 10000 entries
  • Data columns: 8

--------------------------------

Customer_ID 10000 non-null

Age 9800 non-null

Region 10000 non-null

Revenue 9950 non-null

Immediately, you can identify missing values.

6.11.13Statistical Summary

For numerical columns

df.describe()

Typical output includes

  • count
  • mean
  • std
  • min
  • 25%
  • 50%
  • 75%
  • max

6.11.14Understanding describe()

Suppose

Revenue

count 1000

mean 12500

std 5000

min 1000

25% 9000

50% 12000

75% 15000

max 80000

This tells us

Average revenue = 12,500

Median = 12,000
Maximum = 80,000

Possible high-value outlier

6.11.15Categorical Summary

Use

df.describe(
    include="object"
)

For modern Pandas datasets with string/category columns, you can also explicitly select categorical columns.

You may see

  • count
  • unique
  • top
  • freq

6.11.16Describe All Columns

df.describe(
    include="all"
)

This provides a broader summary, although some statistics are not meaningful for every data type.

6.11.17Unique Values

For a categorical column

df["Region"].unique()

Example

\["North", "South", "East", "West"\]

This helps detect unexpected categories.

6.11.18Number of Unique Values

df["Region"].nunique()

6.11.19Frequency Counts

df["Region"].value_counts()

Example

South 4500

North 3000

West 1500

East 1000

This reveals category distribution.

6.11.20Percentage Distribution

df["Region"].value_counts(

normalize=True

) * 100

This gives percentages.

6.11.21Missing-Value Analysis

First

df.isnull().sum()

Percentage

missing_pct = (
    df.isnull().mean() * 100
)
print(
    missing_pct
)

This is an essential EDA step.

6.11.22Visualizing Missing Values

import seaborn as sns
import matplotlib.pyplot as plt
missing = (
    df.isnull()
    .sum()
    .sort_values(
        ascending=False
    )
)

sns.barplot(

x=missing.values,
y=missing.index

)

plt.title(
    "Missing Values by Column"
)
plt.show()

6.11.23Duplicate Analysis

Check duplicate rows

df.duplicated().sum()

View duplicates

df[

df.duplicated()

]

Remove duplicates

df = df.drop_duplicates()

But don't automatically delete duplicates without understanding whether they are legitimate records.

6.11.24Data Validation

EDA should verify business rules.

Suppose

  • Age
  • Quantity
  • Revenue

Potential rules

  • Age > 0
  • Quantity >= 0
  • Revenue >= 0

Check

df[df["Age"] <= 0]

df[df["Quantity"] < 0]

df[df["Revenue"] < 0]

These are examples of domain-specific EDA.

6.11.25Univariate Analysis

Univariate analysis examines one variable at a time.

For numerical data

  • Mean
  • Median
  • Standard deviation
  • Minimum
  • Maximum
  • Distribution
  • Outliers
  • Skewness

For categorical data

  • Frequency
  • Percentage
  • Unique values
  • Mode

6.11.26Numerical Distribution

Use

sns.histplot(

data=df,
x="Revenue",
kde=True

)

plt.show()

This helps identify

  • Normal distribution
  • Skewness
  • Multiple peaks
  • Extreme values

6.11.27Box Plot

sns.boxplot(

data=df,
y="Revenue"

)

plt.show()

Useful for detecting outliers.

6.11.28Skewness

Calculate

df["Revenue"].skew()

Interpretation

≈ 0

→ Approximately symmetric

> 0

→ Right-skewed

< 0

→ Left-skewed

Large positive skew is common in

  • Income
  • Revenue
  • Transaction values
  • House prices

6.11.29Kurtosis

df["Revenue"].kurt()

Kurtosis can help describe the heaviness of distribution tails.

In practical EDA, it is best interpreted alongside histograms, box plots, and domain knowledge rather than in isolation.

6.11.30Categorical EDA

Use

df["Region"].value_counts()

Visualize

sns.countplot(

data=df,
x="Region"

)

plt.show()

Questions

  • Which category is most common?
  • Are some categories rare?
  • Are categories imbalanced?

6.11.31Bivariate Analysis

Bivariate analysis examines relationships between two variables.

Common combinations

  • Numerical + Numerical
  • Numerical + Categorical
  • Categorical + Categorical

6.11.32Numerical vs Numerical

Example

Revenue vs Profit

Use scatter plot

sns.scatterplot(

data=df,
x="Revenue",
y="Profit"

)

plt.show()

6.11.33Correlation

Calculate

df[

\["Revenue", "Profit", "Orders"\]

].corr()

Visualize

sns.heatmap(

df.corr(numeric_only=True),
annot=True,
fmt=".2f"

)

plt.show()

6.11.34Numerical vs Categorical

Example

Region vs Revenue

Use box plot

sns.boxplot(

data=df,
x="Region",
y="Revenue"

)

plt.show()

Or violin plot

sns.violinplot(

data=df,
x="Region",
y="Revenue"

)

6.11.35Categorical vs Categorical

Example

Region vs Customer Segment

Create a cross-tab

pd.crosstab(
    df["Region"],
    df["Customer_Segment"]
)

Visualize

pd.crosstab(
    df["Region"],
    df["Customer_Segment"]
).plot(
    kind="bar"
)
plt.show()

6.11.36Multivariate Analysis

Multivariate analysis studies multiple variables simultaneously.

Example

  • Revenue
  • Profit
  • Orders
  • Region
  • Customer Segment

A Plotly scatter chart can encode multiple dimensions

fig = px.scatter(
    df,
    x="Revenue",
    y="Profit",
    color="Region",
    size="Orders",
    hover_data=[
        "Customer_Segment"
    ]
)

fig.show()

This can reveal complex patterns quickly.

6.11.37Pair Plot

For numerical variables

sns.pairplot(

df[

[

  • "Revenue",
  • "Profit",
  • "Orders"

]

]

)

plt.show()

This creates multiple pairwise comparisons.

6.11.38Correlation Does Not Mean Causation

Suppose

Advertising Spend ↑

Sales ↑

You find

Correlation = 0.85

This does not prove

Advertising causes sales.

Other variables could influence both.

EDA identifies relationships; further analysis is needed to establish causality.

6.11.39Time-Series EDA

If you have

Order_Date

Revenue

First convert

df["Order_Date"] = pd.to_datetime(

df["Order_Date"]

)

Sort

df = df.sort_values(
    "Order_Date"
)

Plot

fig = px.line(
    df,
    x="Order_Date",
    y="Revenue"
)

fig.show()

Look for

  • Trends
  • Seasonality
  • Spikes
  • Drops
  • Structural changes
  • Missing periods

6.11.40Monthly Aggregation

monthly = (
    df.set_index("Order_Date")
    .resample("ME")["Revenue"]
    .sum()
    .reset_index()
)

Then

fig = px.line(
    monthly,
    x="Order_Date",
    y="Revenue"
)

fig.show()

This is often easier to interpret than individual transactions.

6.11.41Rolling Analysis

Calculate a rolling average

  • monthly["Rolling_3M"] = (
  • monthly["Revenue"]
  • .rolling(3)
  • .mean()

)

Plot

fig = px.line(
    monthly,
    x="Order_Date",
    y=[
        "Revenue",
        "Rolling_3M"
    ]
)

fig.show()

This helps reveal the underlying trend.

6.11.42EDA for Outliers

  • Use multiple techniques.
  • Statistical
  • IQR
  • Z-score
  • Percentiles
  • Visual
  • Box plot
  • Scatter plot
  • Histogram

Example

sns.boxplot(

data=df,
x="Region",
y="Revenue"

)

plt.show()

Don't automatically remove outliers.

An outlier could represent

  • Data error
  • Fraud
  • Important customer
  • Major transaction
  • Rare but legitimate event

6.11.43Distribution Comparison

Compare customer segments

sns.histplot(

data=df,
x="Revenue",
hue="Customer_Segment",
kde=True

)

plt.show()

This helps determine whether segments behave differently.

6.11.44Group-Based EDA

One of the most important business analysis techniques is grouping.

Example

df.groupby(
    "Region"
)["Revenue"].agg(
    [
        "count",
        "sum",
        "mean",
        "median"
    ]
)

This provides

  • Region
  • Count
  • Total Revenue
  • Average Revenue
  • Median Revenue

6.11.45Multi-Level Grouping

df.groupby(
    [
        "Region",
        "Category"
    ]
)["Revenue"].sum()

This can reveal

Which categories perform best

within each region?

6.11.46Business KPI EDA

Suppose the business tracks

  • Revenue
  • Orders
  • Customers
  • Profit
  • Conversion Rate
  • Average Order Value

Calculate

Total Revenue

total_revenue = df["Revenue"].sum()

Total Orders

total_orders = df["Order_ID"].nunique()

Average Order Value

aov = (
    total_revenue /
    total_orders
)

Profit Margin

profit_margin = (
    df["Profit"].sum()
    / df["Revenue"].sum()
    * 100
)

EDA should verify that KPI definitions and calculations make business sense.

6.11.47Detecting Data Leakage

For machine-learning projects, EDA should identify potential data leakage.

Example

Suppose you're predicting

Customer Churn

and you have

Churn_Date

  • Cancellation_Reason
  • These variables may contain information that would only become available after churn occurs.
  • Using them during model training could create unrealistic model performance.

6.11.48Detecting Class Imbalance

Suppose the target is

Churn

Check

df["Churn"].value_counts(

normalize=True

) * 100

Example

No 92%

Yes 8%

This is a highly imbalanced target.

EDA should identify this before modeling.

6.11.49EDA for Feature Relationships

Suppose

  • Age
  • Income
  • Orders
  • Revenue
  • Churn

You might investigate

  • Income → Revenue
  • Orders → Revenue
  • Age → Churn
  • Revenue → Churn

Example

sns.boxplot(

data=df,
x="Churn",
y="Revenue"

)

plt.show()

This can reveal whether churned customers have different revenue distributions.

6.11.50EDA and Feature Engineering

EDA often tells you what transformations are needed.

For example

EDA finds highly skewed Revenue
Apply log transformation
Recheck distribution

Or

EDA finds Date column

Extract

  • Year
  • Month
  • Quarter
DayOfWeek
Create useful features

EDA and feature engineering are therefore closely connected.

6.11.51Automated EDA

For larger projects, tools can automate portions of EDA.

Examples include

  • ydata-profiling
  • Sweetviz
  • D-Tale

However, automated reports should support, not replace, analyst reasoning.

A good analyst still asks

Why does this pattern exist?

6.11.52EDA Report Structure

A professional EDA report can follow

  • 1. Executive Summary
  • 2. Dataset Overview
  • 3. Data Quality
  • 4. Missing Values
  • 5. Duplicate Analysis
  • 6. Univariate Analysis
  • 7. Bivariate Analysis
  • 8. Multivariate Analysis
  • 9. Outlier Analysis
  • 10. Correlation Analysis
  • 11. Time-Series Analysis
  • 12. Business KPI Analysis
  • 13. Key Findings
  • 14. Recommendations
  • 6.11.53Example EDA Report
  • Suppose you analyze an e-commerce dataset.
  • Finding 1
  • South region generates the highest revenue.
  • Finding 2

Revenue is positively associated with order volume.

  • Finding 3
  • A small number of transactions have unusually high revenue.
  • Finding 4
  • Revenue is right-skewed.
  • Finding 5

4% of customer records have missing income.

Finding 6

Premium customers have significantly higher average order values.

The important point is that EDA should finish with insights, not just charts.

6.11.54EDA Insight vs Observation

  • Observation
  • South region has ₹18M revenue.
  • Insight

South region generates the highest revenue, contributing approximately 35% of total sales.

Recommendation

Investigate South-region practices to identify strategies that can be replicated in lower-performing regions.

This progression is important

Data
Observation
Insight
Business Recommendation

6.11.55Complete EDA Template

Here is a reusable starting template

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import plotly.express as px

# -------------------------

# 1. Load Data

# -------------------------

df = pd.read_csv(
    "sales.csv"
)

# -------------------------

# 2. Basic Inspection

# -------------------------

print(df.head())
print(df.shape)
print(df.info())
print(df.describe())

# -------------------------

# 3. Missing Values

# -------------------------

print(
    df.isnull().sum()
)

# -------------------------

# 4. Duplicates

# -------------------------

print(
    df.duplicated().sum()
)

# -------------------------

# 5. Unique Values

# -------------------------

for column in df.select_dtypes(
include="object"

).columns

print(
    column,
    df[column].nunique()
)

# -------------------------

# 6. Numerical Distribution

# -------------------------

sns.histplot(

data=df,
x="Revenue",
kde=True

)

plt.show()

# -------------------------

# 7. Outliers

# -------------------------

sns.boxplot(

data=df,
y="Revenue"

)

plt.show()

# -------------------------

# 8. Correlation

# -------------------------

corr = df.corr(
    numeric_only=True
)

sns.heatmap(

corr,

annot=True,
fmt=".2f"

)

plt.show()

# -------------------------

# 9. Relationship

# -------------------------

fig = px.scatter(
    df,
    x="Revenue",
    y="Profit",
    color="Region"
)

fig.show()

6.11.56EDA Best-Practice Checklist

Before declaring EDA complete, check

  • Dataset
  • Shape checked
  • Columns reviewed
  • Data types checked
  • Sample records inspected
  • Data Quality
  • Missing values checked
  • Duplicates checked
  • Invalid values checked
  • Category consistency checked
  • Date validity checked
  • Numerical Analysis
  • Mean
  • Median
  • Standard deviation
  • Min/max
  • Distribution
  • Skewness
  • Outliers
  • Categorical Analysis
  • Unique values
  • Frequency
  • Percentage
  • Category imbalance
  • Relationships
  • Correlation
  • Scatter plots
  • Group comparisons
  • Cross-tabulation
  • Business
  • KPIs calculated
  • Trends identified
  • Anomalies investigated
  • Insights documented
  • Recommendations created

6.11.57EDA Workflow for a Data Analyst

A strong practical workflow is

┌──────────────┐

│ Load Data │

└──────┬───────┘

┌──────────────┐

│ Inspect Data │

└──────┬───────┘

┌───────────────┐

│ Data Quality │

└───────┬───────┘

┌────────────┼────────────┐

↓ ↓ ↓

Missing Duplicate Invalid

Values Records Values

└────────────┼────────────┘

┌──────────────┐

│ Univariate │

└──────┬───────┘

┌──────────────┐

│ Bivariate │

└──────┬───────┘

┌──────────────┐

│ Multivariate │

└──────┬───────┘

┌──────────────┐

│ Outliers │

└──────┬───────┘

┌──────────────┐

│ Correlation │

└──────┬───────┘

┌──────────────┐

│ Time Series │

└──────┬───────┘

┌──────────────┐

│ Business KPI │

└──────┬───────┘

┌──────────────┐

│ Insights │

└──────┬───────┘

┌──────────────┐

│Recommendations│

└──────────────┘

6.11.58EDA Tool Selection

TaskPandasMatplotlibSeabornPlotly
Data inspection⭐⭐⭐
Data cleaning⭐⭐⭐
Summary statistics⭐⭐⭐
Distribution⭐⭐⭐⭐⭐⭐⭐⭐
Box plot⭐⭐⭐⭐⭐⭐⭐⭐
Correlation⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Interactive analysis⭐⭐⭐
Time series⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
Business dashboard⭐⭐⭐⭐⭐⭐⭐
Statistical EDA⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

6.11.59Mini Project — Complete E-Commerce EDA

Use an e-commerce dataset containing

  • Order_ID
  • Order_Date
  • Customer_ID
  • Region
  • Category
  • Product
  • Quantity
  • Revenue
  • Cost
  • Profit
  • Customer_Segment

Perform

Analysis 1 — Dataset Profile

Find

  • Rows
  • Columns
  • Data types
  • Missing values
  • Duplicates
  • Analysis 2 — Revenue Distribution

Create

Histogram + KDE

Answer

Is revenue normally distributed?

Analysis 3 — Outlier Analysis

Create

Box plot

Answer

Are there unusually large transactions?

Analysis 4 — Regional Analysis

Calculate

  • Revenue by Region
  • Profit by Region
  • Orders by Region
  • Analysis 5 — Category Analysis

Find

Top 10 Categories by Revenue

Analysis 6 — Customer Analysis

Compare

  • Customer Segment
  • Average Order Value
  • Profit
  • Analysis 7 — Correlation

Analyze

  • Quantity
  • Revenue
  • Cost
  • Profit
  • Analysis 8 — Time Series

Analyze

  • Daily Revenue
  • Monthly Revenue
  • Quarterly Revenue
  • Analysis 9 — Multivariate Analysis

Create an interactive Plotly chart

X = Revenue
Y = Profit
Color = Region
Size = Quantity

Analysis 10 — Final Insights

Produce at least

  • 5 observations
  • 3 business insights
  • 3 recommendations

6.11.60Interview Questions

  • Basic
  • What is EDA?
  • Why is EDA important?
  • What is univariate analysis?
  • What is bivariate analysis?
  • What is multivariate analysis?
  • How do you inspect a DataFrame?
  • How do you find missing values?
  • How do you find duplicate records?
  • How do you identify categorical variables?
  • How do you calculate descriptive statistics?

Intermediate

  • How do you detect outliers?
  • How do you analyze skewness?
  • What is correlation?
  • What is the difference between covariance and correlation?
  • Why doesn't correlation imply causation?
  • How do you analyze categorical variables?
  • How do you compare distributions between groups?
  • How do you perform time-series EDA?
  • How do you identify data-quality problems?
  • How do you visualize correlations?

Advanced

  • How would you perform EDA on a 100-million-row dataset?
  • How would you identify data leakage?
  • How would you detect class imbalance?
  • How would you handle highly skewed variables?
  • How do you distinguish a data error from a legitimate outlier?
  • How would you perform EDA before building a machine-learning model?
  • How would you automate an EDA report?
  • How do you incorporate domain knowledge into EDA?
  • How would you communicate EDA findings to business stakeholders?
  • What is the difference between an observation, insight, and recommendation?

6.11.61Key Takeaways

EDA is not just

"Make some charts."

It is a structured investigation

RAW DATA
Understand Data
Check Data Quality

┌──────────┼──────────┐

↓ ↓ ↓

Missing Duplicates Invalid

│ │ │

└──────────┼──────────┘

Univariate EDA
Bivariate EDA
Multivariate EDA
Outlier Check
Correlation Analysis
Time-Series EDA
KPI Analysis
INSIGHTS
RECOMMENDATIONS
  • The most important EDA techniques to master
TechniquePurpose
head() / sample()Inspect data
shape / info()Understand structure
describe()Summary statistics
value_counts()Category analysis
isnull()Missing values
duplicated()Duplicate detection
Histogram/KDEDistribution
Box plotOutliers
Scatter plotRelationships
Correlation matrixVariable relationships
GroupByBusiness comparisons
CrosstabCategorical relationships
Time-series plotsTrends/seasonality
Pair plotMultivariate relationships
PlotlyInteractive EDA

The ultimate goal of EDA is to turn an unfamiliar dataset into a set of reliable, explainable business or analytical insights.

A strong Data Analyst should be able to move naturally from Pandas → Seaborn/Matplotlib → Plotly → statistical reasoning → business insight rather than treating visualization as an isolated task.

Module 6 · Lesson 6.12

Dashboard Basics

A dashboard is a visual interface that presents important data, metrics, trends, and insights in a single place so that users can monitor performance, identify problems, and make decisions quickly.

A dashboard converts

Raw Data → KPIs → Visualizations → Insights → Decisions

6.12.1Learning Objectives

By the end of this topic, you should be able to

  • Understand what a dashboard is.
  • Differentiate dashboards from reports.
  • Identify different types of dashboards.
  • Select appropriate KPIs.
  • Design an effective dashboard layout.
  • Choose suitable charts.
  • Apply dashboard design principles.
  • Add filters and interactions.
  • Understand dashboard data architecture.
  • Build dashboards using Excel, Power BI, Tableau, Plotly, or Python.
  • Avoid common dashboard design mistakes.
  • Design a basic business dashboard.

6.12.2What Is a Dashboard?

A dashboard is a visual summary of important information.

For example, a sales dashboard might show

┌─────────────────────────────────────────────────┐

│ SALES DASHBOARD │

├────────────┬────────────┬────────────┬───────────┤

│ Revenue │ Profit │ Orders │ Customers │

│ ₹25.4 L │ ₹5.2 L │ 4,820 │ 2,150 │

├────────────┴────────────┴────────────┴───────────┤

│ │

│ Revenue Trend │

│ ╱╲ ╱╲ │

│ ╱╲ ╱ ╲____╱ ╲ │

│ ╱ ╲_╱ ╲ │

│ │

├───────────────────────┬─────────────────────────┤

│ Revenue by Region │ Profit by Category │

│ │ │

│ North ███████ │ Electronics █████ │

│ South ██████████ │ Furniture ███ │

│ East █████ │ Clothing ████ │

│ West ███████ │ │

└───────────────────────┴─────────────────────────┘

The purpose isn't to show everything.

The purpose is to show what matters.

6.12.3Dashboard vs Report

These are often confused.

DashboardReport
Usually interactiveOften static
Monitoring-focusedAnalysis/documentation-focused
Key informationDetailed information
Quick decision-makingDeep investigation
Often real-time/near-real-timeUsually periodic
Fewer visualsCan contain many pages
KPI focusedDetail focused

Example

A dashboard might show

Revenue = ₹25.4L

A report might contain

Revenue by customer, product, region, month, salesperson, transaction, etc.

6.12.4Dashboard vs Visualization

A visualization is usually one chart.

Example

Revenue Trend

A dashboard combines several visualizations

KPI Cards

+

Revenue Trend

+

Regional Chart

+

Product Chart

+

Filters

+

Alerts

Therefore

A dashboard is a coordinated collection of visualizations designed around a specific decision-making purpose.

6.12.5Types of Dashboards

There are three common categories.

1. Operational Dashboard

Used for day-to-day monitoring.

Examples

  • Pipeline monitoring
  • Server monitoring
  • Sales monitoring
  • Customer support
  • Production monitoring

Typical characteristics

  • Real-time / Near-real-time
  • Alerts
  • Exceptions
  • Current status
  • Operational KPIs

2. Analytical Dashboard

Used for deeper investigation.

Examples

  • Customer analysis
  • Sales analysis
  • Marketing analysis
  • Product analysis

Typical characteristics

  • Filters
  • Drill-down
  • Trends
  • Segmentation
  • Comparisons
  • Detailed analysis

3. Strategic Dashboard

Used by executives and management.

Examples

  • Executive KPI dashboard
  • Company performance
  • Financial performance
  • Business strategy

Typical characteristics

  • High-level KPIs
  • Long-term trends
  • Targets
  • Strategic metrics
  • Minimal detail

6.12.6Dashboard Hierarchy

A useful hierarchy is

Strategic
Analytical
Operational

For example

Executive
"Are we meeting our business goals?"
Management
"Why is performance changing?"
Operations
"What is happening right now?"

6.12.7Dashboard Design Process

A good dashboard should be designed systematically.

Business Requirement
Identify Users
Define Decisions
Define KPIs
Understand Data
Choose Visualizations
Design Layout
Add Filters
Validate Numbers
Test Usability
Publish
Monitor

6.12.8Step 1 — Identify the Audience

Before designing a dashboard, ask

Who will use this dashboard?

Possible users

  • CEO
  • Manager
  • Data Analyst
  • Sales Team
  • Finance Team
  • Operations Team
  • IT Support
  • Data Engineering Team
  • Different users require different information.

6.12.9Step 2 — Identify the Business Questions

Don't start with

Which chart should I create?

Start with

What decision does the user need to make?

For a sales manager

  • Which region is underperforming?
  • Which product is growing?
  • Are we meeting our target?
  • Which salesperson needs attention?

Then select metrics and charts.

6.12.10Step 3 — Define KPIs

KPI means

Key Performance Indicator

Examples

  • Revenue
  • Profit
  • Orders
  • Customers
  • Conversion Rate
  • Customer Retention
  • Average Order Value
  • Pipeline Value

A KPI should be connected to a business objective.

6.12.11KPI vs Metric

A metric is any measurable value.

A KPI is a metric that is important for evaluating performance against a goal.

Example

  • Number of website visits
  • → Metric
  • Conversion Rate
  • → KPI
  • Not every metric is a KPI.

6.12.12KPI Card

A KPI card displays an important number prominently.

Example

┌──────────────────────┐

│ TOTAL REVENUE │

│ │

│ ₹25.4 L │

│ ↑ 12.5% │

│ vs Previous Month │

└──────────────────────┘

A good KPI card often contains

  • Metric
  • Current Value
  • Target
  • Variance
  • Trend
  • Comparison

6.12.13KPI Example

Suppose

Actual Revenue = ₹25L

Target Revenue = ₹30L

Variance

₹25L - ₹30L

= -₹5L

Percentage achievement

25 / 30 × 100

= 83.33%

Dashboard

  • Revenue
  • ₹25L
  • 83.3% of Target

6.12.14KPI Color Usage

Color should communicate meaning.

For example

Good → Positive indicator

Warning → Needs attention

Critical → Immediate action

But don't rely on color alone.

For accessibility, combine color with

  • Icons
  • Text
  • Symbols
  • Labels

Example

↑ 12.5% Improving

↓ 8.2% Declining

6.12.15Dashboard Layout

A common layout

┌───────────────────────────────────────────┐

│ DASHBOARD TITLE │

├──────────┬──────────┬──────────┬──────────┤

│ KPI 1 │ KPI 2 │ KPI 3 │ KPI 4 │

├──────────┴──────────┴──────────┴──────────┤

│ │

│ MAIN TREND │

│ │

├──────────────────────┬────────────────────┤

│ Category Analysis │ Regional Analysis │

│ │ │

├──────────────────────┼────────────────────┤

│ Detail Table │ Alerts / Insights │

└──────────────────────┴────────────────────┘

6.12.16Visual Hierarchy

Users normally scan a dashboard from

Top
Left → Right
Top → Bottom

Therefore

Top

Put

  • KPIs
  • Important alerts
  • Current status
  • Middle

Put

  • Trends
  • Main business analysis
  • Bottom

Put

  • Detailed breakdowns
  • Supporting tables
  • Secondary information

6.12.17Dashboard Grid

Use a consistent grid.

For example

12-column grid

Conceptually

|1|2|3|4|5|6|7|8|9|10|11|12|

A chart can occupy

  • 4 columns
  • 6 columns
  • 8 columns
  • 12 columns
  • This keeps the dashboard aligned.

6.12.18Choosing the Right Chart

Choosing the correct chart is critical.

QuestionRecommended Chart
Trend over timeLine
Compare categoriesBar
DistributionHistogram
OutliersBox plot
RelationshipScatter
CompositionStacked bar
HierarchyTreemap
Geographic analysisMap
CorrelationHeatmap
KPICard
Detailed valuesTable
Conversion processFunnel

6.12.19Line Chart

Use a line chart when the x-axis represents an ordered sequence, especially time.

Example

Revenue

│ ╱╲

│ ╱──╯ ╲

│ ╱─╯ ╲

└────────────────

Jan Feb Mar Apr

Good for

  • Revenue trend
  • Orders
  • Users
  • Pipeline
  • Performance over time

6.12.20Bar Chart

Use bars to compare categories.

Revenue

North ███████

South ██████████

East █████

West ████████

Good for

  • Revenue by region
  • Sales by product
  • Tickets by category
  • Profit by department

6.12.21Scatter Plot

Use scatter plots to analyze relationships.

Example

Profit

│ •

│ •

│ •

│ •

│ •

└────────────── Revenue

Good for

  • Revenue vs Profit
  • Orders vs Revenue
  • Age vs Spending
  • Cost vs Sales

6.12.22Tables

Tables are useful when users need exact values.

Example

RegionRevenueProfitOrders
South₹8.2L₹1.8L1,420
North₹6.4L₹1.3L1,100
West₹5.8L₹1.1L950
East₹5.0L₹1.0L850

Don't use charts when exact numbers are the primary requirement.

6.12.23Filters

Filters allow users to change the dashboard view.

Common filters

  • Date
  • Region
  • Product
  • Category
  • Customer Segment
  • Department
  • Status

Example

Date: [Jan 2026 ▼]

Region: [South ▼]

Category: [All ▼]

The dashboard updates according to the selections.

6.12.24Date Filters

Common date options

  • Today
  • Yesterday
  • This Week
  • This Month
  • This Quarter
  • This Year
  • Last 30 Days
  • Custom Range

For operational dashboards, date filters should be easy to access.

6.12.25Drill-Down

Drill-down allows users to move from high-level information to detailed information.

Example

Revenue
Region
Category
Product
Transaction

This is especially useful in Power BI and Tableau.

6.12.26Drill-Through

Drill-through takes the user to another page containing detailed information about a selected item.

Example

Sales Dashboard
Select "South"
South Region Detail
Products
  • Customers
  • Salespeople
  • Transactions

6.12.27Tooltips

Tooltips display additional information when users hover over a visual.

Example

  • Revenue: ₹2.4M
  • Orders: 18,400
  • Profit Margin: 21.5%

Tooltips allow you to keep the main dashboard clean while still providing details.

6.12.28Dashboard Interactivity

Common interactive features

  • Filters
  • Drill-down
  • Drill-through
  • Tooltips
  • Sorting
  • Highlighting
  • Cross-filtering
  • Dropdowns
  • Date selectors
  • Bookmarks

The goal is

Give users control without making the dashboard complicated.

6.12.29Dashboard Data Architecture

A typical architecture

DATA SOURCES

┌───────────┼───────────┐

▼ ▼ ▼

SQL Excel APIs

│ │ │

└───────────┼───────────┘

ETL / ELT

Data Warehouse

Semantic Model

Dashboard

User

6.12.30Dashboard Data Layer

A professional dashboard shouldn't always query raw transactional tables directly.

A better architecture is

Raw Data
Staging
Transformation
Data Warehouse
Data Mart / Semantic Model
Dashboard

Benefits

  • Better performance
  • Consistent KPIs
  • Easier maintenance
  • Better governance

6.12.31KPI Definition

Every KPI should have a clear definition.

For example

  • Revenue
  • Revenue =
  • SUM(Net Sales)
  • Profit
  • Profit =
  • Revenue - Cost
  • Profit Margin
  • Profit Margin =
  • Profit / Revenue × 100
  • Average Order Value
  • AOV =
  • Revenue / Number of Orders

If KPI definitions are unclear, different teams may report different numbers.

6.12.32Dashboard Performance

A beautiful dashboard is useless if it takes 30 seconds to load.

Important performance factors

  • Data volume
  • Number of visuals
  • Query complexity
  • Calculated columns
  • Calculated measures
  • Relationships
  • Filters
  • Refresh frequency
  • Data model design

6.12.33Improving Dashboard Performance

Use

  • Aggregated tables
  • Proper indexing
  • Efficient SQL
  • Star schema
  • Measures instead of unnecessary calculated columns
  • Incremental refresh where appropriate
  • Query optimization
  • Appropriate data granularity

Avoid

  • Loading unnecessary columns
  • Loading unnecessary rows
  • Too many visuals
  • Complex calculations repeated unnecessarily
  • Excessive visual interactions

6.12.34Star Schema for Dashboards

A common analytical model

Dim_Date

Dim_Product ─── Fact_Sales ─── Dim_Customer

Dim_Region

Fact table

Fact_Sales

-----------

  • Date_Key
  • Product_Key
  • Customer_Key
  • Region_Key
  • Quantity
  • Revenue
  • Cost
  • Profit

Dimension tables

  • Dim_Date
  • Dim_Product
  • Dim_Customer
  • Dim_Region

This structure is widely used for analytical dashboards.

6.12.35Dashboard Refresh

Dashboards can refresh data at different frequencies.

Examples

  • Real-time
  • Every 5 minutes
  • Every 30 minutes
  • Hourly
  • Daily
  • Weekly

Choose refresh frequency based on business requirements.

Don't refresh every minute if the underlying business data changes only once per day.

6.12.36Real-Time Dashboard

Example

  • Pipeline Monitoring Dashboard
  • Total Pipelines: 120
  • Running: 8
  • Succeeded: 108
  • Failed: 4
  • Delayed: 0

The dashboard could refresh every few minutes.

Typical use cases

  • IT monitoring
  • Manufacturing
  • Trading
  • Logistics
  • Data pipelines
  • Customer support

6.12.37Dashboard Alerts

Dashboards can highlight exceptions.

Example

  • ⚠ Revenue below target
  • ⚠ Pipeline failed
  • ⚠ Data refresh delayed
  • SLA within target

An alert should answer

  • What happened?
  • How serious is it?
  • What action is needed?

6.12.38Exception-Based Dashboard Design

A good operational dashboard should not force users to inspect every chart.

Instead

Normal
No Action
Exception
Investigate
Take Action

Example

Pipeline Health

────────────────────

  • 95 Pipelines Healthy
  • 18 Running
  • ⚠ 4 Failed
  • ⚠ 2 Delayed
  • The failures deserve attention.

6.12.39Dashboard Color Principles

Use a limited color system.

For example

Neutral → Normal information

Positive → Good

Warning → Attention

Critical → Action required

Avoid using many unrelated colors.

Color should have meaning.

6.12.40Avoid 3D Charts

3D charts can distort perception.

Prefer

  • 2D Bar
  • 2D Line
  • 2D Scatter

instead of

  • 3D Pie
  • 3D Bar
  • 3D Surface
  • unless 3D provides genuine analytical value.

6.12.41Avoid Chart Junk

Chart junk refers to unnecessary visual elements.

Examples

  • Excessive borders
  • Decorative backgrounds
  • Unnecessary gradients
  • 3D effects
  • Excessive labels
  • Unnecessary icons

The goal is

Maximize information and minimize distraction.

6.12.42Don't Overload the Dashboard

Bad

  • 30 charts
  • 15 filters
  • 10 tables
  • 20 KPI cards

Good

  • 4–6 important KPIs
  • 3–5 meaningful charts
  • 2–5 useful filters

The exact number depends on the use case, but simplicity should be the default.

6.12.43Dashboard Storytelling

A good dashboard should tell a logical story

What is happening?

Why is it happening?

Where is it happening?

What needs attention?

What action should we take?

Example

Revenue ↓ 8%
South region ↓ 15%
Electronics ↓ 20%
Product X accounts for most decline
Investigate Product X availability

This is much more useful than simply showing five unrelated charts.

6.12.44Executive Dashboard Example

┌──────────────────────────────────────────────────┐

│ EXECUTIVE DASHBOARD │

├───────────┬───────────┬───────────┬──────────────┤

│ Revenue │ Profit │ Growth │ Customers │

│ ₹25.4M │ ₹5.2M │ +12.5% │ 45,200 │

├───────────┴───────────┴───────────┴──────────────┤

│ │

│ Revenue Trend │

│ │

├────────────────────────┬─────────────────────────┤

│ Revenue by Region │ Revenue by Category │

│ │ │

├────────────────────────┴─────────────────────────┤

│ Key Business Insights │

│ │

│ • South region leads growth │

│ • Electronics drives revenue │

│ • Profit margin improved by 2.1% │

└──────────────────────────────────────────────────┘

An executive dashboard should focus on decisions, not operational details.

6.12.45Operational Dashboard Example

┌─────────────────────────────────────────────────┐

│ PIPELINE MONITORING │

├──────────┬──────────┬──────────┬────────────────┤

│ Total │ Running │ Success │ Failed │

│ 120 │ 8 │ 108 │ 4 │

├──────────┴──────────┴──────────┴────────────────┤

│ Pipeline Execution Trend │

├────────────────────────┬────────────────────────┤

│ Pipeline Status │ Failed Pipelines │

│ │ │

├────────────────────────┴────────────────────────┤

│ Recent Failures │

│ │

│ Pipeline Time Error │

│ CustomerLoad 14:25 Timeout │

│ OrdersLoad 14:31 Source unavailable │

└─────────────────────────────────────────────────┘

This is designed for action, not executive reporting.

6.12.46Dashboard Tools

Common tools include

Excel

Good for

  • Small datasets
  • Quick analysis
  • Basic dashboards
  • Familiar business workflows
  • Power BI

Excellent for

  • Enterprise BI
  • Semantic models
  • DAX
  • Interactive dashboards
  • Microsoft ecosystem
  • Tableau

Excellent for

  • Interactive visualization
  • Visual analytics
  • Dashboard exploration
  • Python + Plotly

Excellent for

  • Custom applications
  • Data science
  • Interactive visualizations
  • Programmatic dashboards
  • Streamlit / Dash

Excellent for

Python-based analytical web applications

6.12.47Choosing a Dashboard Tool

RequirementRecommended
Small business analysisExcel
Enterprise Microsoft BIPower BI
Advanced visual analyticsTableau
Python applicationPlotly
Python dashboardStreamlit / Dash
Custom web portalPlotly + Web Framework

6.12.48Dashboard Testing

Before publishing, validate

  • Data
  • KPI values correct
  • Filters work
  • Dates correct
  • Aggregations correct
  • Refresh works
  • Visual
  • Titles clear
  • Labels readable
  • Colors meaningful
  • Charts appropriate
  • User Experience
  • Dashboard loads quickly
  • Filters are understandable
  • Important information is visible immediately
  • No unnecessary charts

6.12.49Dashboard Validation

Never assume the dashboard is correct because the visualization looks good.

For example

Dashboard shows

Revenue = ₹25.4M

Validate against SQL

  • SELECT
  • SUM(Revenue) AS Total_Revenue
  • FROM Fact_Sales;

The dashboard and source calculation should reconcile.

This is especially important for financial and executive dashboards.

6.12.50Dashboard Development Lifecycle

Requirement
Wireframe
Data Model
KPI Definitions
Prototype
Visualization
Interactivity
Validation
Performance Testing
User Acceptance Testing
Deployment
Monitoring

6.12.51Dashboard Wireframe

Before building the actual dashboard, create a simple wireframe.

Example

┌───────────────────────────────────────┐

│ TITLE │

├────────┬────────┬────────┬────────────┤

│ KPI 1 │ KPI 2 │ KPI 3 │ KPI 4 │

├────────┴────────┴────────┴────────────┤

│ │

│ Main Trend │

│ │

├────────────────────┬──────────────────┤

│ Category │ Region │

│ │ │

├────────────────────┴──────────────────┤

│ Detail Table │

└───────────────────────────────────────┘

Only after agreeing on the layout should you implement it.

6.12.52Dashboard Requirements Example

Suppose management asks

"Build a sales performance dashboard."

Don't immediately start building charts.

Ask

  • Who will use it?
  • What decisions do they make?
  • What time period?
  • What KPIs?
  • What targets?
  • What dimensions?
  • What filters?
  • How frequently should data refresh?
  • What level of detail?

Then translate the requirements into a dashboard specification.

6.12.53Dashboard Specification Example

  • Dashboard
  • Sales Performance Dashboard
  • Audience
  • Sales Managers
  • KPIs
  • Revenue
  • Profit
  • Orders
  • Customers
  • Average Order Value
  • Dimensions
  • Region
  • Product
  • Category
  • Salesperson
  • Customer Segment
  • Filters
  • Date
  • Region
  • Category
  • Customer Segment
  • Visuals
  • Revenue KPI
  • Profit KPI
  • Orders KPI
  • Revenue Trend
  • Revenue by Region
  • Profit by Category
  • Top Products
  • Customer Segment Analysis

6.12.54Practical Project

Project: Sales Performance Dashboard

Create a dataset

  • Order_ID
  • Order_Date
  • Customer_ID
  • Region
  • Category
  • Product
  • Salesperson
  • Quantity
  • Revenue
  • Cost
  • Profit
  • Customer_Segment

Build a dashboard containing

  • KPI Cards
  • Total Revenue
  • Total Profit
  • Total Orders
  • Total Customers
  • Profit Margin
  • Average Order Value
  • Charts
  • Revenue trend
  • Profit trend
  • Revenue by region
  • Revenue by category
  • Top 10 products
  • Customer segment performance
  • Filters
  • Date
  • Region
  • Category
  • Customer Segment
  • Salesperson
  • Insights

Display

  • Top-performing region
  • Top product
  • Highest-growth category
  • Lowest-performing region
  • Profit margin

6.12.55Python + Plotly Dashboard Concept

A simple Python dashboard architecture

SQL DATABASE

Pandas

Data Cleaning

KPI Calculation

Plotly

Dashboard Framework

┌────────┴────────┐

▼ ▼

Dash Streamlit

│ │

└────────┬────────┘

User

This is especially useful when you want to build a custom analytics portal rather than relying entirely on a BI platform.

6.12.56Common Dashboard Mistakes

1. Starting with charts

Wrong

"Let's create a pie chart."

Correct

"What question are we trying to answer?"

2. Too many KPIs

Showing 25 KPIs makes nothing important.

3. Wrong chart

Example

Using a pie chart to compare 20 products.

A bar chart is usually more effective.

4. No comparison

Showing

Revenue = ₹25M

is less useful than

Revenue = ₹25M
Target = ₹27M
Variance = -₹2M

5. No context

A KPI without

  • Target
  • Previous period
  • Benchmark
  • Trend
  • can be difficult to interpret.

6. Slow dashboard

A dashboard that takes 20 seconds to load will frustrate users.

7. Inconsistent definitions

One team says

Revenue = Gross Sales

Another says

Revenue = Net Sales

This creates distrust.

8. Excessive decoration

A dashboard is an analytical product, not a poster.

6.12.57Dashboard Golden Rules

Remember

  • Rule 1
  • Know your audience.
  • Rule 2
  • Start with business questions.
  • Rule 3
  • Define KPIs before creating charts.
  • Rule 4
  • Use the simplest chart that answers the question.
  • Rule 5
  • Show comparisons whenever possible.
  • Rule 6
  • Use color meaningfully.
  • Rule 7
  • Keep the dashboard uncluttered.
  • Rule 8
  • Make important information visible immediately.
  • Rule 9
  • Validate every KPI against the source data.
  • Rule 10
  • Optimize performance.

6.12.58Interview Questions

  • Basic
  • What is a dashboard?
  • What is the difference between a dashboard and a report?
  • What is a KPI?
  • What is the difference between a KPI and a metric?
  • What are operational dashboards?
  • What are analytical dashboards?
  • What are strategic dashboards?
  • What is a KPI card?
  • What is a filter?
  • What is drill-down?

Intermediate

  • How do you choose the right chart?
  • What makes a good dashboard?
  • How do you design a dashboard layout?
  • What is visual hierarchy?
  • How do you improve dashboard performance?
  • Why should dashboards have targets?
  • How do you validate dashboard numbers?
  • What is cross-filtering?
  • What is drill-through?
  • What is the difference between real-time and scheduled refresh?

Advanced

  • How would you design an executive dashboard?
  • How would you design an operational monitoring dashboard?
  • How would you optimize a slow Power BI dashboard?
  • How would you design a dashboard for millions of rows?
  • How would you define KPIs with business stakeholders?
  • How do you prevent inconsistent KPI definitions?
  • How would you implement row-level security?
  • How would you design a star schema for a dashboard?
  • How would you decide between Power BI, Tableau, and Python?
  • How would you validate an executive dashboard before production deployment?

6.12.59Key Takeaways

The fundamental dashboard lifecycle is

BUSINESS QUESTION
AUDIENCE
REQUIREMENTS
KPIs
DATA MODEL
VISUALIZATION
LAYOUT
INTERACTION
VALIDATION
PERFORMANCE
DASHBOARD
DECISION

Most important concepts

ConceptPurpose
KPIMeasure important performance
KPI CardDisplay key number
FilterChange dashboard context
Drill-downMove from summary to detail
Drill-throughNavigate to detailed page
TooltipShow additional information
Cross-filterConnect visual interactions
TargetDefine expected performance
VarianceCompare actual vs target
RefreshKeep data current
Data ModelOrganize analytical data
Visual HierarchyGuide user attention
Dashboard LayoutOrganize information
ExceptionHighlight something requiring attention

A good dashboard does not simply display data. It helps the user understand what is happening, why it is happening, and what action should be taken.

The natural progression for this module is now

EDA → Dashboard Basics → Data Storytelling → Time Series → Business KPIs → Excel → Power BI → Tableau → Case Study → Visualization Project.

Module 6 · Lesson 6.13

Data Storytelling

Data Storytelling is the practice of combining data, visualization, context, and narrative to communicate insights clearly and persuade an audience to take an informed action.

A dashboard tells users what is happening.

Data storytelling goes further

What happened → Why it happened → Why it matters → What should we do?

6.13.1Learning Objectives

By the end of this topic, you should be able to

  • Understand the principles of data storytelling.
  • Distinguish data, visualization, and narrative.
  • Identify the audience and their needs.
  • Convert analysis into a compelling story.
  • Select appropriate visualizations.
  • Highlight important insights.
  • Explain trends, anomalies, and relationships.
  • Build an executive data story.
  • Present analytical findings effectively.
  • Avoid common storytelling mistakes.
  • Create a complete data storytelling presentation.

6.13.2What Is Data Storytelling?

Data storytelling combines three major components

DATA STORYTELLING

┌────────────┼────────────┐

▼ ▼ ▼

DATA VISUALIZATION NARRATIVE

│ │ │

Evidence Pattern Meaning

│ │ │

└────────────┼────────────┘

INSIGHT

  • ACTION
  • Data
  • Provides evidence.
  • Visualization
  • Makes patterns easier to see.
  • Narrative
  • Explains why the pattern matters.

Together they produce a story that supports decision-making.

6.13.3Example

Suppose the data shows

January Revenue ₹10M

February Revenue ₹11M

March Revenue ₹12M

April Revenue ₹8M

A simple chart tells us

Revenue dropped in April.

Data storytelling says

Revenue declined 33% in April, primarily due to a 45% decline in Electronics sales in the South region. Inventory shortages affected the two highest-selling products. Restoring inventory levels before May could recover a significant portion of the lost revenue.

Notice the progression

Data
Observation
Explanation
Business Impact
Recommendation

6.13.4Why Data Storytelling Matters

A business stakeholder usually doesn't want to see

  • 500 rows
  • 20 charts
  • 15 statistics

They want to know

  • What happened?
  • Why?
  • How important is it?
  • What should we do?

Data storytelling helps analysts communicate answers to these questions.

6.13.5Data vs Visualization vs Story

These are different.

Data

Revenue = ₹8M

Visualization

Revenue

12M│ ███

10M│ ███ ███

8M│ ███ ███ ███

  • 6M│
  • Jan Feb Mar Apr
  • Story

Revenue declined significantly in April after strong growth during Q1. The decline was concentrated in Electronics and appears to be associated with inventory shortages.

The story gives the visualization meaning.

6.13.6The Data Storytelling Framework

A practical framework is

1. Context

2. Question

3. Analysis

4. Insight

5. Impact

6. Recommendation

7. Action

Let's examine each.

6.13.7Step 1 — Context

Explain the situation.

Example

The company experienced strong revenue growth during the first quarter.

This gives the audience a starting point.

6.13.8Step 2 — Question

Identify the business question.

Example

Why did revenue decline sharply in April?

A good story should answer a meaningful question.

6.13.9Step 3 — Analysis

Analyze the relevant data.

Investigate

  • Region
  • Category
  • Product
  • Customer
  • Price
  • Quantity
  • Inventory
  • Date

Don't analyze everything simply because the data is available.

Focus on variables relevant to the question.

6.13.10Step 4 — Insight

Identify the important finding.

Example

70% of the April revenue decline came from Electronics sales in the South region.

This is much more valuable than

Electronics revenue was ₹2M.

6.13.11Step 5 — Impact

Explain why the finding matters.

Example

Electronics contributes 40% of company revenue, so continued weakness could significantly affect quarterly targets.

This connects analysis to business consequences.

6.13.12Step 6 — Recommendation

Suggest an action.

Example

Prioritize inventory replenishment for the top five Electronics products in the South region.

6.13.13Step 7 — Action

Define what happens next.

Example

  • Sales Team
  • → Contact major customers
  • Supply Chain
  • → Replenish inventory
  • Marketing
  • → Launch targeted campaign
  • Management
  • → Review performance weekly

A strong data story ends with action, not merely a chart.

6.13.14Understanding the Audience

Different audiences need different stories.

Executive

Wants

  • Overall performance
  • Business impact
  • Risk
  • Opportunity
  • Recommendation
  • Manager

Wants

  • Performance by team
  • Root causes
  • Targets
  • Exceptions
  • Actions
  • Analyst

Wants

  • Methodology
  • Data quality
  • Statistics
  • Detailed patterns
  • Assumptions
  • Technical Team

Wants

  • Data source
  • Pipeline
  • Transformation
  • Quality
  • Performance
  • Technical root cause

Therefore

The same dataset can produce different stories for different audiences.

6.13.15Start With the Decision

A powerful principle

Don't start with the data. Start with the decision.

For example

Instead of

"I have sales data. What charts can I create?"

Ask

"Management needs to decide where to invest the next ₹10M. What does the data tell us?"

Then analyze

  • Region
  • Growth
  • Profitability
  • Customer demand
  • Market opportunity
  • This produces a more focused story.

6.13.16Story Structure

A common business structure is

Situation
Complication
Insight
Resolution
  • Situation
  • Revenue grew 15% during Q1.
  • Complication
  • Growth slowed significantly in April.
  • Insight
  • The slowdown was concentrated in two regions and three products.
  • Resolution
  • Reallocate inventory and marketing budget toward high-performing segments.

6.13.17Another Useful Structure: SCQA

SCQA means

  • S → Situation
  • C → Complication
  • Q → Question
  • A → Answer

Example

  • Situation
  • The company achieved record sales in Q1.
  • Complication
  • Growth slowed sharply in April.

Question

What caused the slowdown?

Answer

Inventory shortages in Electronics caused most of the decline.

This structure works well for executive presentations.

6.13.18Observation vs Insight

  • This distinction is critical.
  • Observation
  • South region sales decreased by 12%.
  • Insight
  • South region sales decreased by 12%, primarily because Electronics sales fell by 25%.
  • Recommendation
  • Increase inventory availability for high-demand Electronics products in South.

Think

Observation

Why?

Insight

So what?

Recommendation

6.13.19The "So What?" Test

After every important finding, ask

So what?

Example

Revenue increased 10%.

So what?

Profit increased only 2%.

So what?

Costs are increasing faster than revenue.

So what?

If this continues, revenue growth will not translate into proportional profit growth.

Now the finding becomes meaningful.

6.13.20The "Why?" Chain

Continue asking why.

Example

  • Revenue ↓
  • ↓ Why?
  • Electronics sales ↓
  • ↓ Why?
  • Product availability ↓
  • ↓ Why?
  • Inventory shortage
  • ↓ Why?
  • Supplier delay

This can lead to a root-cause story.

6.13.21Choosing the Right Visualization

The visualization should support the story.

Story QuestionRecommended Visualization
What changed over time?Line chart
Which category is largest?Bar chart
How are values distributed?Histogram
Are there outliers?Box plot
Are two variables related?Scatter plot
What contributes to total?Stacked bar
Where is performance concentrated?Map
What is the hierarchy?Treemap
How does actual compare to target?Bullet/bar
What are the exact values?Table

6.13.22Don't Start With the Chart

Bad workflow

I know how to create a pie chart

I'll find data for it

Better

Business Question
Required Comparison
Best Visualization

For example

Which regions are underperforming?

Use

Bar chart

not a pie chart simply because pie charts look attractive.

6.13.23Highlight the Insight

A chart should make the important point obvious.

Suppose

North ██████████

South █████

East ███████

West ████████

If South is the problem, highlight South and explain

South revenue is 30% below target.

Don't force the audience to discover the insight themselves.

6.13.24Annotations

Annotations can explain important events.

For example

Revenue

│ ╱╲

│ ╱──╯ ╲

│ ╱─╯ ↓

└───────────────

Inventory

shortage

In Plotly

fig.add_annotation(

x="Apr",
y=8000000,
text="Inventory shortage",
showarrow=True

)

Annotations turn charts into explanations.

6.13.25Titles Should Communicate Insights

Weak title

Monthly Revenue

Better

Revenue Declined 18% in April

Weak

Sales by Region

Better

South Region Missed Target by 15%

The second version communicates the conclusion immediately.

6.13.26Descriptive vs Insightful Titles

  • Descriptive
  • Revenue by Region
  • Insightful
  • South Region Drives 40% of Revenue but Has the Lowest Margin

The second title tells the audience why they should care.

6.13.27Visual Hierarchy

Use visual hierarchy to control attention.

Example

MAIN INSIGHT

┌──────────────────┐

│ Revenue ↓ 18% │

└──────────────────┘

Supporting Evidence
Root Cause Analysis
Recommendation

Not every chart should have equal visual importance.

6.13.28Color in Data Storytelling

Use color intentionally.

For example

  • Neutral → Background data
  • Highlight → Important category
  • Warning → Risk
  • Positive → Improvement

Avoid using ten colors just because the visualization supports them.

6.13.29Color as an Analytical Tool

Suppose

  • North
  • South
  • East
  • West

If the story concerns South

  • North → neutral
  • South → highlighted
  • East → neutral
  • West → neutral

This directs attention to the relevant data.

6.13.30Avoid Rainbow Charts

A rainbow palette can make every category appear equally important.

Instead

One primary color

+

One highlight color

+

Neutral tones

This improves focus.

6.13.31Data Storytelling With Time Series

Time-series storytelling often follows

Baseline
Trend
Change
Anomaly
Cause
Impact

Example

  • January → Stable
  • February → Growth
  • March → Growth
  • April → Sharp decline
  • May → Recovery

Story

Revenue grew consistently through March before declining sharply in April. The decline coincided with supply constraints, while May showed recovery after inventory levels normalized.

6.13.32Before-and-After Story

A simple storytelling technique

BEFORE

Revenue = ₹10M

Change

AFTER

Revenue = ₹13M

Then explain

  • What caused the change?
  • What did we learn?
  • Can it be repeated?

6.13.33Target vs Actual Story

Suppose

Target = ₹30M
Actual = ₹25M

Story

Revenue reached ₹25M against a ₹30M target, leaving a ₹5M gap.

Then analyze

  • Where is the gap?
  • Why does it exist?
  • What can close it?

6.13.34Variance Storytelling

Variance is

Actual - Target

Percentage variance

(Actual - Target)

---------------- × 100

Target

Example

Actual = ₹25M
Target = ₹30M
Variance = -₹5M

Variance %

= -5 / 30 × 100

= -16.67%

Story

Revenue is 16.7% below target.

This is more useful than simply saying

Revenue is ₹25M.

6.13.35Trend Storytelling

Suppose

Jan = ₹10M
Feb = ₹12M
Mar = ₹15M
Apr = ₹14M

Don't simply report each number.

Say

Revenue increased 50% from January to March before declining slightly in April.

This summarizes the pattern.

6.13.36Comparison Storytelling

Instead of

South revenue = ₹15M.

Say

South generated the highest revenue at ₹15M, 25% above the company average.

Comparisons provide context.

6.13.37Context Is Critical

Consider

Revenue = ₹100M

Is this good?

We don't know.

Compare against

  • Target
  • Previous year
  • Previous month
  • Industry benchmark
  • Budget
  • Forecast

Then the number becomes meaningful.

6.13.38Storytelling With Benchmarks

Example

Actual Revenue ₹25M

Budget ₹28M

Last Year ₹22M

Now

vs Budget

↓10.7%

vs Last Year

↑13.6%

Story

Revenue is below budget but remains 13.6% higher than last year.

This is a much richer insight.

6.13.39Finding the Root Cause

A dashboard may show

Revenue ↓ 12%

Storytelling requires investigating

  • Region
  • Category
  • Product
  • Customer
  • Quantity
  • Price
  • Inventory
  • Marketing

For example

Revenue ↓ 12%
Quantity ↓ 15%
Electronics ↓ 25%
South Region
Inventory shortage

Now you have a possible root-cause narrative.

6.13.40Storytelling With Segmentation

Segments can reveal hidden patterns.

Example

Customer Segment

Premium Revenue ↑ 20%

Standard Revenue ↑ 5%

Basic Revenue ↓ 8%

Story

Overall revenue grew 10%, but growth was driven almost entirely by Premium customers while the Basic segment declined.

This is more actionable.

6.13.41Storytelling With Distribution

Average alone can be misleading.

Suppose

Average Order Value = ₹5,000

But distribution is

Most orders = ₹1,000–₹3,000

Few orders = ₹50,000+

The mean may be inflated by high-value transactions.

Storytelling should therefore consider

  • Mean
  • Median
  • Percentiles
  • Distribution
  • Outliers

6.13.42Storytelling With Outliers

Suppose one customer generated

₹10M

while most customers generate:
    ₹20K–₹100K

Don't immediately remove the customer.

Investigate

  • Is it legitimate?
  • Is it a bulk order?
  • Is it a data error?
  • Is it a strategic customer?

The outlier may itself be the story.

6.13.43Storytelling With Correlation

Suppose

Marketing Spend

Revenue

Correlation = 0.82

Don't say

Marketing caused revenue growth.

Say

Marketing spend and revenue show a strong positive relationship. Further analysis is required to determine whether increased marketing directly caused the revenue increase.

This is statistically responsible storytelling.

6.13.44Data Storytelling Workflow

A practical workflow

1. Understand the business

2. Identify audience

3. Define decision

4. Analyze data

5. Find important pattern

6. Validate finding

7. Identify cause

8. Quantify impact

9. Build visualization

10. Write narrative

11. Recommend action

6.13.45Storytelling Template

Use this simple template

  • What happened?
  • Revenue declined 18% in April.
  • Where?
  • The decline was concentrated in the South region.
  • Why?
  • Electronics sales fell due to inventory shortages.

So what?

  • Electronics represents 40% of regional revenue.
  • What should we do?
  • Prioritize replenishment of the top-selling Electronics products.

This is one of the most useful frameworks for analysts.

6.13.46Executive Storytelling

An executive usually needs

Headline
Evidence
Business Impact
Recommendation

Example

  • Headline
  • Revenue growth slowed sharply in Q2.
  • Evidence
  • Growth declined from 15% in Q1 to 4% in Q2.
  • Business Impact
  • The company is now ₹3M below the quarterly forecast.
  • Recommendation
  • Reallocate marketing investment toward the two highest-converting regions.

6.13.47Analyst Storytelling

An analyst presentation may include more detail

Business Question
Data Sources
Data Preparation
Methodology
Analysis
Statistical Findings
Visualization
Limitations
Recommendations

The audience determines the appropriate level of detail.

6.13.48Technical Storytelling

For a technical/data engineering audience

Pipeline failures increased 20% last week.

Then

Which pipeline?

Which source?

What error?

What changed?

Root cause?

Resolution?

The story becomes operational and technical.

6.13.49Dashboard Story vs Presentation Story

Dashboard

Designed for

  • Exploration
  • Monitoring
  • Self-service
  • Interaction
  • Presentation

Designed for

  • Guided narrative
  • Persuasion
  • Decision
  • Action

A dashboard allows the user to explore.

A presentation guides the user through a specific story.

6.13.50Storytelling in Power BI

Power BI supports storytelling through

  • KPI cards
  • Charts
  • Slicers
  • Drill-down
  • Drill-through
  • Tooltips
  • Bookmarks
  • Page navigation
  • Smart narratives
  • Conditional formatting

A Power BI story might be

Page 1

Executive Summary
Page 2
Revenue Analysis
Page 3
Regional Analysis
Page 4
Product Analysis
Page 5

Recommendations

6.13.51Storytelling in Tableau

Tableau supports

  • Dashboards
  • Worksheets
  • Stories
  • Filters
  • Parameters
  • Actions
  • Tooltips
  • Highlighting

A Tableau story can guide the user through a sequence of analytical views.

6.13.52Storytelling With Plotly

Plotly can support interactive stories.

For example

import plotly.express as px
fig = px.line(
    df,
    x="Date",
    y="Revenue",
    title="Revenue Declined 18% in April"
)

fig.add_annotation(

x="2026-04-01",
y=8000000,
text="Inventory shortage",
showarrow=True

)

fig.show()

The visualization itself contains part of the narrative.

6.13.53Storytelling With Python

A simple reporting workflow

summary = (
df.groupby("Region")
\["Revenue"\]

.sum()

.sort_values(

ascending=False

)

)

print(summary)

Then visualize

fig = px.bar(
    summary,
    title="South Region Leads Revenue"
)

fig.show()

The important part is not the code.

The important part is explaining

Why does South lead, and what should the business learn from it?

6.13.54Data Storytelling Example

Suppose an e-commerce company has this information

Revenue

Q1 = ₹100M
Q2 = ₹115M
Q3 = ₹110M
Q4 = ₹130M

A basic statement

Q4 revenue was ₹130M.

A better story

Revenue reached a yearly high of ₹130M in Q4, increasing 18% from Q3. Growth was primarily driven by Electronics and Premium customers.

An even better story

Revenue reached a yearly high of ₹130M in Q4, 18% above Q3 and 8% above the forecast. Electronics contributed 60% of the increase, while Premium customers accounted for most of the incremental demand. Maintaining Electronics inventory and retaining Premium customers should be priorities for Q1.

That's data storytelling.

6.13.55Storytelling Pyramid

Think of a story as a pyramid

ACTION

RECOMMENDATION

IMPACT

INSIGHT

EVIDENCE

DATA

The audience should be able to move from raw evidence toward a decision.

6.13.56The Three-Layer Story

A simple framework

  • Layer 1 — What?
  • What happened?
  • Layer 2 — So What?
  • Why does it matter?
  • Layer 3 — Now What?
  • What should we do?
  • WHAT?

Revenue ↓ 18%

SO WHAT?

₹3M forecast gap

NOW WHAT?

Fix Electronics inventory

This framework is extremely useful in interviews and business presentations.

6.13.57Storytelling With KPIs

A KPI alone

Profit Margin = 18%

Story

Profit Margin

18%
22% last year
-4 percentage points
Mainly due to rising logistics costs
Review shipping contracts

The KPI becomes meaningful when placed in context.

6.13.58Storytelling With Targets

Use

  • Actual
  • Target
  • Variance
  • Trend

Example

  • Revenue
  • ₹25M Actual
  • ₹30M Target
  • -₹5M Gap

-16.7%

Narrative

Revenue is 16.7% below target, with most of the gap coming from the South region.

6.13.59Storytelling With Forecasts

Suppose

  • Actual Q1 = ₹100M
  • Forecast Q2 = ₹120M
  • Target Q2 = ₹125M

Story

Q2 revenue is projected to reach ₹120M, which is 4% below target. The forecast suggests the current growth rate will not be sufficient to meet the quarterly goal.

Then

Additional sales activity is required to close the projected ₹5M gap.

6.13.60Avoiding Misleading Stories

Never manipulate the audience.

Avoid

  • Truncated axes without explanation
  • Selective date ranges
  • Hiding unfavorable results
  • Misleading colors
  • Cherry-picked metrics
  • Ignoring sample size
  • Confusing correlation with causation

Good storytelling is persuasive because it is accurate, not because it is deceptive.

6.13.61Statistical Honesty

Suppose

Conversion Rate

Control = 10.0%
Experiment = 10.5%

Don't immediately say

The experiment improved conversion by 5%.

You need to distinguish

Relative increase

= 5%

Percentage-point increase

= 0.5 percentage points

And determine whether the difference is statistically meaningful.

6.13.62Storytelling With Uncertainty

Some results contain uncertainty.

For example

Forecast Revenue

₹120M ± ₹10M

Story

The forecast is approximately ₹120M, with a plausible range around ₹110M–₹130M.

This is more honest than presenting ₹120M as a guaranteed result.

6.13.63Storytelling Checklist

Before presenting a story, ask

  • Audience
  • Who is listening?
  • What do they care about?
  • What decision do they need to make?
  • Data
  • Is the data accurate?
  • Are definitions clear?
  • Are there limitations?
  • Analysis
  • What happened?
  • Why?
  • Is the finding statistically/business meaningful?
  • Visualization
  • Is the chart appropriate?
  • Is the key insight obvious?
  • Is unnecessary information removed?
  • Narrative
  • Is the story clear?
  • Is there context?
  • Is the business impact explained?
  • Is there a recommendation?
  • Action
  • What should happen next?
  • Who owns the action?
  • When should it happen?

6.13.64Common Data Storytelling Mistakes

  • Mistake 1 — Showing everything
  • More data does not mean more insight.
  • Mistake 2 — No clear message
  • The audience should know the main point.
  • Mistake 3 — Too many charts
  • Every chart should earn its place.
  • Mistake 4 — Descriptive titles

Instead of

Sales by Region

Use

  • South Region Drives 40% of Sales but Has the Lowest Margin
  • Mistake 5 — No context
  • A number without comparison is often meaningless.
  • Mistake 6 — No recommendation

A business story should ideally answer

  • What should we do next?
  • Mistake 7 — Overusing technical terminology
  • Don't explain statistical concepts unnecessarily to an executive audience.

6.13.65Data Storytelling Project

Project: E-Commerce Business Performance Story

Use a dataset containing

  • Order_ID
  • Order_Date
  • Customer_ID
  • Region
  • Category
  • Product
  • Quantity
  • Revenue
  • Cost
  • Profit
  • Customer_Segment
  • Business Problem

Management wants to understand

  • Why has revenue growth slowed, and what should the company do next quarter?
  • Analysis
  • Step 1 — Overall Performance

Calculate

  • Total Revenue
  • Total Profit
  • Total Orders
  • Profit Margin
  • Average Order Value
  • Step 2 — Trend

Create

  • Monthly Revenue
  • Monthly Profit
  • Monthly Orders

Look for

  • Growth
  • Decline
  • Seasonality
  • Anomalies
  • Step 3 — Regional Analysis

Compare

  • Revenue
  • Growth
  • Profit Margin
  • Orders
  • Step 4 — Product Analysis

Identify

  • Top Products
  • Declining Products
  • High-margin Products
  • Low-margin Products
  • Step 5 — Customer Analysis

Compare

  • Premium
  • Standard
  • Basic

Look at

  • Revenue
  • Orders
  • AOV
  • Profit
  • Step 6 — Root Cause
  • Find the main drivers of growth/decline.

Example

Revenue Growth ↓
South Region ↓
Electronics ↓
Product Availability ↓
Inventory Issue

Step 7 — Build the Story

Your final story should contain

  • 1. Executive Summary
  • 2. Current Performance
  • 3. Key Trend
  • 4. Main Problem
  • 5. Root Cause
  • 6. Business Impact
  • 7. Opportunity
  • 8. Recommendation
  • 9. Next Steps
  • 6.13.66Example Final Story
  • Executive Summary
  • Revenue grew 8% year-over-year but remained 6% below the quarterly target.
  • Key Finding

The shortfall was concentrated in the South region, where Electronics revenue declined 22%.

  • Root Cause
  • The decline was primarily associated with low availability of the three highest-selling products.
  • Business Impact

Electronics represents 35% of regional revenue, making the decline material to overall performance.

  • Recommendation
  • Prioritize inventory replenishment for the top three Electronics products and increase targeted marketing once availability is restored.
  • Expected Outcome

Recovering half of the lost Electronics sales could materially reduce the quarterly revenue gap.

This is a complete data story.

6.13.67Interview Questions

  • Basic
  • What is data storytelling?
  • Why is data storytelling important?
  • What are the three components of data storytelling?
  • What is the difference between data and insight?
  • What is the difference between a dashboard and a data story?
  • What is an observation?
  • What is an insight?
  • What is a recommendation?
  • Why is audience important?
  • What is visual hierarchy?

Intermediate

  • How do you choose a visualization for a story?
  • How do you explain a trend to management?
  • How do you communicate an outlier?
  • How do you communicate negative results?
  • How do you use annotations?
  • How do you write an effective chart title?
  • What is the "So What?" test?
  • What is SCQA?
  • How do you communicate correlation without implying causation?
  • How do you turn an EDA result into a business recommendation?

Advanced

  • How would you present bad business performance to senior management?
  • How would you tell a story when the data contradicts management assumptions?
  • How would you design an executive data story?
  • How would you communicate uncertainty in a forecast?
  • How would you avoid misleading visualizations?
  • How do you identify the root cause of a KPI decline?
  • How do you balance technical accuracy with executive simplicity?
  • How would you build a data storytelling presentation from raw SQL data?
  • How would you turn a Power BI dashboard into an executive narrative?
  • How would you defend your recommendation using data?

6.13.68Key Takeaways

The most important framework to remember is

DATA

WHAT HAPPENED?

WHY?

SO WHAT?

BUSINESS IMPACT

NOW WHAT?

RECOMMENDATION
ACTION

Or simply

What → Why → So What → Now What

The complete Data Storytelling process

Business Question
Audience
Data
Analysis
Insight
Visualization
Narrative
Impact
Recommendation
Action
  • Golden Rule

Don't make the audience find the story in your data. Find the story first, then design the visualization to make that story obvious.

A strong Data Analyst doesn't just say

"Sales decreased by 18%."

A strong Data Analyst says

"Sales decreased 18%, primarily because Electronics sales in the South region fell 25% following inventory shortages. Electronics represents 40% of regional sales, creating a material risk to the quarterly target. Replenishing the top-selling products should be the immediate priority."

That is the difference between reporting data and telling a data story.

Module 6 · Lesson 6.14

Time Series Basics

Time Series Analysis is the process of analyzing data points collected or recorded over time.

Examples

  • Daily sales
  • Monthly revenue
  • Stock prices
  • Website traffic
  • Temperature
  • Electricity consumption
  • Pipeline executions
  • Customer sign-ups
  • CPU utilization

The key difference from ordinary data analysis is that time order matters.

6.14.1Learning Objectives

By the end of this topic, you should be able to

  • Understand time-series data.
  • Work with dates and timestamps in Pandas.
  • Convert columns to datetime.
  • Sort and index time-series data.
  • Resample data.
  • Aggregate daily, weekly, and monthly data.
  • Analyze trends.
  • Understand seasonality.
  • Detect cycles and anomalies.
  • Calculate percentage changes.
  • Calculate rolling statistics.
  • Compare periods.
  • Create time-series visualizations.
  • Understand stationarity at a basic level.
  • Prepare time-series data for forecasting.

6.14.2What Is Time-Series Data?

A time series consists of observations associated with time.

Example

DateRevenue
Jan 1₹10,000
Jan 2₹12,000
Jan 3₹11,500
Jan 4₹14,000
Jan 5₹15,000

The important characteristic is

Date 1 → Date 2 → Date 3 → Date 4 → Date 5

The sequence matters.

6.14.3Examples of Time-Series Data

  • Business
  • Daily Revenue
  • Monthly Sales
  • Quarterly Profit
  • Finance
  • Stock Price
  • Exchange Rate
  • Interest Rate
  • Technology
  • CPU Usage
  • Memory Usage
  • API Requests
  • Pipeline Runs
  • Energy
  • Daily Power Consumption
  • Hourly Electricity Demand
  • Monthly Peak Load
  • Marketing
  • Daily Website Visits
  • Weekly Leads
  • Monthly Conversions

6.14.4Time Series vs Regular Data

Consider

Customer | Revenue

This is ordinary cross-sectional data.

But

Date | Revenue

is time-series data.

In time-series analysis, we care about

Current value
Previous value
Historical pattern
Future behavior

6.14.5Components of a Time Series

A time series can contain four important components

Time Series
├── Trend
  • ├── Seasonality
  • ├── Cyclical Variation
  • └── Irregular / Random Variation

6.14.6Trend

A trend is the long-term direction of a series.

Example

Revenue

│ ╱

│ ╱

│ ╱

│ ╱

│ ╱

└──────────────── Time

Revenue is generally increasing.

Trend can be

  • Increasing
  • Decreasing
  • Flat

6.14.7Seasonality

Seasonality is a predictable pattern that repeats at a known frequency.

Example

Monthly Sales

Jan ███

Feb ████

Mar █████

Apr ███

May ████

Jun █████

...

If sales consistently increase every December, that's seasonal behavior.

Examples

  • Retail sales during festivals
  • Hotel bookings during holidays
  • Ice cream sales during summer
  • Electricity consumption during certain hours

6.14.8Cyclical Patterns

A cycle is a longer-term fluctuation that does not necessarily have a fixed period.

For example

Economic expansion
Economic slowdown
Recovery
Expansion

Cyclical patterns are different from fixed seasonal patterns.

6.14.9Random Variation

Random variation represents unpredictable movement.

Example

  • Revenue
  • 100
  • 105
  • 98
  • 103
  • 97
  • 101
  • Some movement may simply be noise.

6.14.10Example

Imagine monthly revenue

Jan ₹10L

Feb ₹11L

Mar ₹12L

Apr ₹11L

May ₹13L

Jun ₹14L

Jul ₹13L

Aug ₹15L

Sep ₹16L

Possible interpretation

Long-term pattern → Increasing trend

Monthly fluctuations → Short-term variation

If December is consistently much higher every year, there may also be seasonality.

6.14.11Loading Time-Series Data

import pandas as pd
df = pd.read_csv(
    "sales.csv"
)

Suppose

Order_Date

Revenue

6.14.12Convert to Datetime

Always convert date columns properly

df["Order_Date"] = pd.to_datetime(

df["Order_Date"]

)

Check

print(
    df["Order_Date"].dtype
)

Expected

datetime64[ns]

6.14.13Why Datetime Conversion Matters

Once Pandas recognizes the column as datetime, you can easily extract

  • Year
  • Month
  • Quarter
  • Week
  • Day
  • Day of Week

and perform

  • Resampling
  • Filtering
  • Rolling calculations
  • Time differences

6.14.14Sort by Date

Always ensure chronological order

df = df.sort_values(
    "Order_Date"
)

This is particularly important before

  • Rolling calculations
  • Lag calculations
  • Trend analysis
  • Time-series modeling

6.14.15Set Date as Index

For many Pandas time-series operations

df = df.set_index(
    "Order_Date"
)

Now

print(df.head())

might show

Revenue

Order_Date

2026-01-01 10000

2026-01-02 12000

2026-01-03 11500

6.14.16Extract Year

df["Year"] = (

df.index.year

)

Or before setting the index

df["Year"] = (

df["Order_Date"].dt.year

)

6.14.17Extract Month

df["Month"] = (

df["Order_Date"].dt.month

)

For month name

  • df["Month_Name"] = (
  • df["Order_Date"]
  • .dt.month_name()

)

6.14.18Extract Quarter

df["Quarter"] = (

df["Order_Date"].dt.quarter

)

Example

  • 1 → Q1
  • 2 → Q2
  • 3 → Q3
  • 4 → Q4

6.14.19Day of Week

df["DayOfWeek"] = (

df["Order_Date"].dt.day_name()

)

Possible values

  • Monday
  • Tuesday
  • Wednesday

...

Sunday

This is useful for analyzing weekday behavior.

6.14.20Basic Time-Series Plot

Using Plotly

import plotly.express as px
fig = px.line(
    df,
    x="Order_Date",
    y="Revenue",
    title="Revenue Over Time"
)

fig.show()

Line charts are usually the natural starting point for time-series visualization.

6.14.21Daily vs Monthly Data

Suppose your transaction data contains

  • 2026-01-01
  • 2026-01-01
  • 2026-01-01
  • 2026-01-02
  • 2026-01-02

...

There may be thousands of transactions per day.

For business analysis, you may want

Daily Revenue

rather than individual transactions.

This is where resampling becomes important.

6.14.22Resampling

Resampling means changing the time frequency.

For example

  • Daily → Weekly
  • Daily → Monthly
  • Hourly → Daily
  • Monthly → Quarterly

6.14.23Monthly Revenue

With a datetime index

monthly = (
    df["Revenue"]
    .resample("ME")
    .sum()
)

Here

ME = Month End

Depending on your Pandas version, you may encounter older examples using "M"; "ME" is the clearer modern alias for month-end frequency.

6.14.24Monthly Average

monthly_avg = (
    df["Revenue"]
    .resample("ME")
    .mean()
)

Use mean() when you want average value rather than total revenue.

6.14.25Weekly Revenue

weekly = (
    df["Revenue"]
    .resample("W")
    .sum()
)

6.14.26Quarterly Revenue

quarterly = (
    df["Revenue"]
    .resample("QE")
    .sum()
)

6.14.27Choosing the Correct Aggregation

This is very important.

For revenue

SUM

is usually appropriate.

For temperature

MEAN

may be appropriate.

For inventory

LAST

may sometimes be appropriate.

For transaction count

COUNT

may be appropriate.

Always ask

What does this measure represent?

6.14.28Multiple Aggregations

monthly = (
    df.resample("ME")
    .agg({
        "Revenue": "sum",
        "Profit": "sum",
        "Orders": "sum"
    })
)

You can also use multiple functions

monthly = (
    df["Revenue"]
    .resample("ME")
    .agg([
        "sum",
        "mean",
        "min",
        "max"
    ])
)

6.14.29Year-over-Year Growth

Suppose

2025 Revenue = ₹100M

2026 Revenue = ₹120M

Growth

(120 - 100) / 100 × 100

= 20%

In Pandas

  • df["YoY_Growth"] = (
  • df["Revenue"]
  • .pct_change(12)
  • * 100

)

For monthly data, 12 compares with the same month of the previous year.

6.14.30Month-over-Month Growth

  • df["MoM_Growth"] = (
  • df["Revenue"]
  • .pct_change()
  • * 100

)

Example

January → ₹100K

February → ₹120K

MoM

20%

6.14.31Percentage Change

General formula

Percentage Change =

(Current - Previous)

-------------------- × 100

Previous

Pandas

  • df["Growth"] = (
  • df["Revenue"]
  • .pct_change()
  • * 100

)

6.14.32Lag

  • A lag value is a previous observation.
  • df["Previous_Revenue"] = (
  • df["Revenue"].shift(1)

)

Example

MonthRevenuePrevious Revenue
Jan100NaN
Feb120100
Mar110120

6.14.33Lead

A lead looks at a future value

df["Next_Revenue"] = (

df["Revenue"].shift(-1)

)

This can be useful for analysis and feature engineering.

6.14.34Rolling Average

  • A rolling average smooths short-term fluctuations.
  • df["Rolling_3M"] = (
  • df["Revenue"]
  • .rolling(3)
  • .mean()

)

For

  • 100
  • 120
  • 110
  • 130
  • 150

the rolling average gives a smoother view of the underlying movement.

6.14.35Rolling Sum

  • df["Rolling_3M_Sum"] = (
  • df["Revenue"]
  • .rolling(3)

.sum()

)

Useful for

  • Rolling sales
  • Rolling orders
  • Rolling costs

6.14.36Rolling Maximum

  • df["Rolling_Max"] = (
  • df["Revenue"]
  • .rolling(3)

.max()

)

6.14.37Expanding Average

  • An expanding average uses all observations from the beginning up to the current point.
  • df["Expanding_Avg"] = (
  • df["Revenue"]
  • .expanding()
  • .mean()

)

Difference

  • Rolling
  • → Fixed-size window
  • Expanding
  • → Grows from the beginning

6.14.38Moving Average Visualization

  • monthly["Rolling_3M"] = (
  • monthly["Revenue"]
  • .rolling(3)
  • .mean()

)

fig = px.line(
    monthly,
    x=monthly.index,
    y=[
        "Revenue",
        "Rolling_3M"
    ],
    title="Revenue and 3-Month Moving Average"
)

fig.show()

The moving average makes the underlying trend easier to see.

6.14.39Detecting Seasonality

Suppose monthly sales are

  • Jan → 100
  • Feb → 110
  • Mar → 120

...

  • Dec → 200
  • If the same pattern occurs every year, there may be seasonality.
  • One useful approach is to compare the same month across years.

For example

monthly["Month"] = (

monthly.index.month

)

Then

  • monthly.groupby(
  • "Month"
  • )["Revenue"].mean()

6.14.40Seasonality Visualization

seasonal = (
    monthly
    .groupby("Month")["Revenue"]
    .mean()
    .reset_index()
)
fig = px.line(
    seasonal,
    x="Month",
    y="Revenue",
    markers=True,
    title="Average Revenue by Month"
)

fig.show()

This can reveal recurring monthly patterns.

6.14.41Time-Series Decomposition

A basic conceptual decomposition is

Observed

=

Trend

+

Seasonality

+

Residual

Using statsmodels

from statsmodels.tsa.seasonal import (
    seasonal_decompose
)
result = seasonal_decompose(
    monthly["Revenue"],
    model="additive",
    period=12
)

result.plot()

This separates the series into

  • Observed
  • Trend
  • Seasonal
  • Residual

6.14.42Additive vs Multiplicative

  • Additive
  • Observed =
  • Trend + Seasonality + Residual
  • Useful when seasonal variation is relatively constant.
  • Multiplicative
  • Observed =
  • Trend × Seasonality × Residual

Useful when seasonal variation grows with the level of the series.

6.14.43Stationarity

A stationary time series has statistical properties that are relatively stable over time.

Conceptually

Mean → roughly stable

Variance → roughly stable

Structure → roughly stable

Example

  • 50
  • 52
  • 48
  • 51
  • 49
  • 53

The series fluctuates around a relatively stable level.

6.14.44Non-Stationary Series

A series with a strong trend

  • 50
  • 60
  • 70
  • 80
  • 90
  • 100
  • is generally non-stationary.

Many forecasting methods work better when the data is transformed into a more stationary form.

6.14.45Differencing

First difference

df["Difference"] = (

df["Revenue"].diff()

)

Formula

Difference_t =

Value_t - Value_(t-1)

Example

  • 100
  • 120
  • 110
  • 130

becomes

  • NaN
  • 20
  • -10
  • 20
  • Differencing can help remove trend.

6.14.46Autocorrelation

Autocorrelation measures the relationship between a time series and its previous values.

For example

Today's Revenue

Yesterday's Revenue

If high revenue tends to follow high revenue, there may be positive autocorrelation.

This is important for forecasting.

6.14.47Lag Plot

A simple way to explore autocorrelation

from pandas.plotting import lag_plot

lag_plot(

df["Revenue"]

)

plt.show()

If points form a pattern rather than random scatter, there may be autocorrelation.

6.14.48Rolling Volatility

For financial or operational metrics, rolling standard deviation can measure changing variability:

  • df["Rolling_Std"] = (
  • df["Revenue"]
  • .rolling(12)

.std()

)

Higher values indicate greater variation within the window.

6.14.49Detecting Anomalies

Suppose daily revenue normally ranges between

₹90K–₹120K

but suddenly

₹500K

This may be an anomaly.

Potential causes

  • Major customer order
  • Promotion
  • Data duplication
  • ETL issue
  • Fraud
  • Business event
  • Don't automatically remove it.
  • Investigate it first.

6.14.50Missing Dates

Time series often have missing periods.

Check

date_range = pd.date_range(
    start=df.index.min(),
    end=df.index.max(),
    freq="D"
)
missing_dates = (
    date_range
    .difference(df.index)
)

But remember: missing dates aren't necessarily errors. A business may legitimately have no transactions on certain days.

6.14.51Filling Missing Dates

You can create a complete date index

df = df.reindex(
    date_range
)

Then decide how to handle missing values based on business meaning.

Possible approaches

  • 0
  • Forward fill
  • Backward fill
  • Interpolation
  • Leave missing

Do not blindly fill missing time-series values.

6.14.52Time-Based Filtering

With a datetime index

df.loc[
    "2026-01-01":"2026-03-31"
]

This is very convenient for selecting periods.

6.14.53Specific Year

df.loc["2026"]

6.14.54Specific Month

df.loc["2026-03"]

6.14.55Business-Day Frequency

Pandas can work with business days

pd.date_range(
    start="2026-01-01",
    end="2026-01-31",
    freq="B"
)

This excludes weekends.

6.14.56Time Zones

For global applications, time zones matter.

You can localize

df.index = (
    df.index
    .tz_localize("UTC")
)

Convert

df.index = (
    df.index
    .tz_convert("Asia/Kolkata")
)

This is especially important for

  • Global systems
  • Financial applications
  • Monitoring
  • APIs
  • Distributed data pipelines

6.14.57Time-Series Visualization Best Practices

Use

  • Line charts
  • For continuous trends.
  • Bar charts
  • For discrete period comparisons.
  • Moving averages
  • For smoothing noise.
  • Annotations
  • For important events.
  • Consistent frequency

Don't mix daily and monthly observations without clear explanation.

6.14.58Example: Business Revenue Analysis

Suppose you have

Order_Date

Revenue

Workflow

df["Order_Date"] = pd.to_datetime(

df["Order_Date"]

)

df = df.sort_values(
    "Order_Date"
)
df = df.set_index(
    "Order_Date"
)
monthly = (
    df["Revenue"]
    .resample("ME")
    .sum()
)
monthly_growth = (
    monthly.pct_change() * 100
)
rolling_avg = (
    monthly.rolling(3).mean()
)

Now you have

  • Monthly Revenue
  • Growth %
  • 3-Month Moving Average

These three measures provide a strong basic business view.

6.14.59Complete Time-Series Visualization

result = pd.DataFrame({
    "Revenue": monthly,
    "Growth": monthly_growth,
    "Rolling_Avg": rolling_avg
}).reset_index()
fig = px.line(
    result,
    x="Order_Date",
    y=[
        "Revenue",
        "Rolling_Avg"
    ],
    title="Revenue Trend"
)

fig.show()

For growth

fig = px.line(
    result,
    x="Order_Date",
    y="Growth",
    title="Monthly Revenue Growth"
)

fig.show()

6.14.60Time-Series KPI Analysis

A dashboard might display

┌─────────────────────────────────────────────┐

│ TIME-SERIES PERFORMANCE │

├────────────┬────────────┬───────────────────┤

│ Revenue │ MoM Growth │ YoY Growth │

│ ₹25.4M │ +8.2% │ +14.5% │

├────────────┴────────────┴───────────────────┤

│ │

│ Revenue Trend │

│ │

├───────────────────────┬─────────────────────┤

│ Monthly Growth │ Seasonal Pattern │

│ │ │

└───────────────────────┴─────────────────────┘

6.14.61Time-Series Analysis for Data Engineering

Time-series analysis is also extremely useful for monitoring data pipelines.

For example

  • Pipeline
  • Run_Date
  • Duration
  • Records_Processed
  • Status

You can analyze

Execution Trend

fig = px.line(
    df,
    x="Run_Date",
    y="Duration",
    color="Pipeline"
)

fig.show()

Record Volume

fig = px.line(
    df,
    x="Run_Date",
    y="Records_Processed",
    color="Pipeline"
)

fig.show()

Failure Trend

Aggregate failures by day

failures = (
    df[df["Status"] == "Failed"]
    .groupby("Run_Date")
    .size()
    .reset_index(
        name="Failures"
    )
)

Then plot

fig = px.line(
    failures,
    x="Run_Date",
    y="Failures",
    title="Pipeline Failures Over Time"
)

fig.show()

This is a practical example of time-series analysis in an operational environment.

6.14.62Forecasting vs Time-Series Analysis

These are related but different.

Time-Series Analysis

Answers

What has happened?

and

What patterns exist?

Forecasting

Answers

What is likely to happen next?

For example

Historical

───────────────→

Jan Feb Mar Apr May Jun

Forecast

───────→

Jul Aug Sep

Forecasting is a later topic and builds on time-series fundamentals.

6.14.63Time-Series Workflow

A practical workflow

Load Data
Convert Dates
Sort Chronologically
Check Frequency
Check Missing Periods
Aggregate / Resample
Visualize
Analyze Trend
Analyze Seasonality
Calculate Growth
Calculate Rolling Statistics
Investigate Anomalies
Check Stationarity
Prepare for Forecasting

6.14.64Common Mistakes

Mistake 1 — Treating dates as strings

Bad

  • "01/02/2026"
  • "15/01/2026"
  • "05/03/2026"
  • Convert them to datetime.
  • Mistake 2 — Not sorting dates
  • Always ensure chronological order.
  • Mistake 3 — Wrong aggregation

For example, using

mean()

when you actually need

  • sum()
  • can completely change business conclusions.
  • Mistake 4 — Ignoring missing dates

Missing periods may indicate

  • Data issue
  • Business closure
  • No transactions
  • Pipeline failure
  • Investigate before making assumptions.
  • Mistake 5 — Removing anomalies automatically
  • An anomaly might represent a real business event.
  • Mistake 6 — Confusing seasonality with trend
  • A series can have both.
  • Mistake 7 — Using inappropriate comparisons

For monthly sales, comparing January to December may be misleading if the business is seasonal.

Year-over-year comparison may be more appropriate.

6.14.65Mini Project — Monthly Sales Analysis

Create a dataset

  • Order_Date
  • Order_ID
  • Region
  • Category
  • Revenue
  • Profit

Perform

Step 1

Convert Order_Date to datetime.

Step 2

Sort by date.

Step 3

Create a monthly revenue series.

Step 4

Calculate

  • MoM Growth
  • YoY Growth
  • 3-Month Moving Average

Step 5

Identify

  • Highest Revenue Month
  • Lowest Revenue Month
  • Highest Growth Month
  • Largest Decline

Step 6

Analyze seasonality.

Step 7

Identify anomalies.

Step 8

Create an interactive Plotly dashboard.

Step 9

Write three business insights.

6.14.66Example Insights

After analysis, your findings might look like

Revenue increased 18% year-over-year, driven primarily by strong Q4 performance.

December generated the highest revenue, indicating a recurring year-end seasonal effect.

March experienced an unusual 15% decline compared with February and should be investigated for operational or market-related causes.

Notice that these are insights, not just numbers.

6.14.67Interview Questions

  • Basic
  • What is time-series data?
  • How is time-series data different from cross-sectional data?
  • What is a trend?
  • What is seasonality?
  • What is a cycle?
  • What is noise?
  • What is resampling?
  • What is a moving average?
  • What is lag?
  • What is differencing?

Intermediate

  • How do you convert a column to datetime in Pandas?
  • How do you extract year and month?
  • How do you aggregate daily data into monthly data?
  • How do you calculate month-over-month growth?
  • How do you calculate year-over-year growth?
  • How do you detect missing dates?
  • How do you identify seasonality?
  • What is time-series decomposition?
  • What is autocorrelation?
  • What is stationarity?

Advanced

  • Why is stationarity important for forecasting?
  • What is the difference between additive and multiplicative seasonality?
  • How would you detect an anomaly in a time series?
  • How would you handle missing observations?
  • How would you choose between sum, mean, and last-value aggregation?
  • How would you analyze a highly seasonal business?
  • How would you analyze pipeline execution times over time?
  • How would you distinguish a real business anomaly from a data-quality issue?
  • How would you prepare a time series for forecasting?
  • How would you explain a time-series trend to an executive?

6.14.68Key Takeaways

The most important concepts are

ConceptMeaning
Time SeriesData ordered by time
TrendLong-term direction
SeasonalityRepeating predictable pattern
CycleLonger-term fluctuation
NoiseRandom variation
ResamplingChanging time frequency
LagPrevious observation
LeadFuture observation
pct_change()Percentage change
Rolling MeanSmoothed moving average
DifferencingCurrent − previous
AutocorrelationRelationship with past values
DecompositionTrend + seasonality + residual
StationarityStable statistical behavior
AnomalyUnusual observation

The fundamental workflow is

TIME SERIES
Convert Dates
Sort Chronologically
Check Frequency
Resample / Aggregate
Visualize

┌──────────┼──────────┐

↓ ↓ ↓

Trend Seasonality Noise

│ │ │

└──────────┼──────────┘

Growth Analysis
Rolling Statistics
Anomaly Detection
Stationarity
Forecasting

The key idea of time-series analysis is that time itself contains information. A good analyst doesn't just ask "What is the value?" but also "How has it changed, what pattern does it follow, and what does that pattern tell us?"

Illustrative monthly revenue trend

Example time-series pattern showing an overall upward trend with normal monthly variation.

monthrevenue
Jan100,000
Feb112,000
Mar118,000
Apr109,000
May128,000
Jun140,000
Module 6 · Lesson 6.15

Business KPIs

KPI stands for Key Performance Indicator.

A KPI is a measurable value used to determine whether a business, team, process, or project is achieving an important objective.

The most important idea is

A KPI connects business objectives with measurable performance.

For example

Business Goal
Increase Revenue
KPI
Revenue Growth %
Target

+15%

Actual

+11%

Action

Improve underperforming regions

6.15.1Learning Objectives

By the end of this topic, you should be able to

  • Understand KPIs and business metrics.
  • Distinguish KPIs from ordinary metrics.
  • Define effective KPIs.
  • Understand leading and lagging indicators.
  • Calculate common business KPIs.
  • Analyze revenue and profitability KPIs.
  • Analyze sales and marketing KPIs.
  • Analyze customer KPIs.
  • Analyze operational KPIs.
  • Analyze financial KPIs.
  • Compare actual vs target.
  • Calculate variance and growth.
  • Design KPI dashboards.
  • Interpret KPIs for business decisions.

6.15.2KPI vs Metric

  • This distinction is important.
  • Metric
  • Any measurable business value.

Examples

  • Website Visits
  • Number of Employees
  • Number of Orders
  • Number of Emails
  • KPI

A metric directly connected to an important business objective.

Example

Goal

Increase customer retention

KPI

Customer Retention Rate

So

All KPIs are metrics,

but not all metrics are KPIs.

6.15.3Example

Suppose an e-commerce company tracks

  • Website Visits
  • Orders
  • Revenue
  • Profit
  • Customers

These are metrics.

If the business goal is

Increase sales profitability.

Then useful KPIs might be

  • Revenue Growth
  • Profit Margin
  • Average Order Value
  • Customer Lifetime Value

6.15.4Characteristics of a Good KPI

A good KPI should be

  • Specific
  • Clearly defined.
  • Measurable
  • Can be calculated from data.
  • Relevant
  • Connected to a business objective.
  • Time-bound
  • Measured over a specific period.
  • Actionable
  • The business can respond to it.

A useful framework is SMART

  • S → Specific
  • M → Measurable
  • A → Achievable
  • R → Relevant
  • T → Time-bound

6.15.5KPI Categories

Business KPIs can be organized into

Business KPIs
├── Financial
  • ├── Sales
  • ├── Marketing
  • ├── Customer
  • ├── Operational
  • ├── Product
  • ├── Employee / HR
  • ├── Supply Chain
  • └── Technology / IT

6.15.6Financial KPIs

Important financial KPIs include

  • Revenue
  • Revenue Growth
  • Gross Profit
  • Gross Margin
  • Operating Profit
  • Operating Margin
  • EBITDA
  • Net Profit
  • Net Profit Margin
  • Cash Flow
  • Return on Investment
  • Cost-to-Revenue Ratio

6.15.7Revenue

Revenue represents income generated from business activities before subtracting applicable costs and expenses according to the organization's accounting definition.

Basic calculation

Revenue =

Price × Quantity

For multiple transactions

total_revenue = df["Revenue"].sum()

Example

  • Product A → ₹10,000
  • Product B → ₹20,000
  • Product C → ₹15,000
  • Total Revenue = ₹45,000

6.15.8Revenue Growth

Revenue growth measures how revenue changes between periods.

Formula

Revenue Growth %

=

(Current Revenue - Previous Revenue)

------------------------------------- × 100

Previous Revenue

Example

Previous Revenue = ₹100M

Current Revenue = ₹120M

Growth =

(120 - 100) / 100 × 100

= 20%

Python

growth = (
    current_revenue -
    previous_revenue
) / previous_revenue * 100

6.15.9Gross Profit

Basic formula

Gross Profit =

Revenue - Cost of Goods Sold

Example

Revenue = ₹100M
COGS = ₹60M

Gross Profit = ₹40M

6.15.10Gross Profit Margin

Formula

Gross Margin %

=

Gross Profit

------------ × 100

Revenue

Example

Gross Profit = ₹40M

Revenue = ₹100M

Gross Margin = 40%

Python

gross_margin = (
    gross_profit /
    revenue *
    100
)

6.15.11Net Profit Margin

Formula

Net Profit Margin %

=

Net Profit

---------- × 100

Revenue

Example

Revenue = ₹100M

Net Profit = ₹10M

Net Profit Margin = 10%

This tells us how much profit remains from each ₹100 of revenue after the relevant expenses.

6.15.12Operating Margin

Formula

Operating Margin %

=

Operating Profit

--------------- × 100

Revenue

It measures profitability from core operations before considering certain non-operating items, depending on the accounting definition used.

6.15.13EBITDA

EBITDA means

Earnings Before Interest, Taxes, Depreciation, and Amortization

It is commonly used to evaluate operating performance.

A simplified relationship is

EBITDA

=

Operating Profit

+

Depreciation

+

Amortization

The exact calculation should follow the organization's financial reporting definition.

6.15.14Return on Investment

ROI measures return relative to investment.

Formula

ROI %

=

(Net Return / Investment)

× 100

Example

Investment = ₹10L
Return = ₹12L
Gain = ₹2L
ROI = 20%

6.15.15Sales KPIs

Common sales KPIs

  • Sales Revenue
  • Revenue Growth
  • Orders
  • Average Order Value
  • Conversion Rate
  • Sales Target Achievement
  • Sales Pipeline
  • Win Rate
  • Sales Cycle Length
  • Customer Acquisition Cost

6.15.16Average Order Value

AOV represents the average revenue generated per order.

Formula

AOV =

Total Revenue

-------------

Number of Orders

Example

Revenue = ₹10,00,000
Orders = 2,000
AOV = ₹500

Python

aov = (
    df["Revenue"].sum()
    / df["Order_ID"].nunique()
)

6.15.17Conversion Rate

Conversion rate measures how many users complete a desired action.

For e-commerce

Conversion Rate %

=

Orders

------- × 100

Visitors

Example

Visitors = 100,000
Orders = 3,000

Conversion Rate = 3%

6.15.18Sales Target Achievement

Formula

Achievement %

=

Actual Sales

----------- × 100

Target Sales

Example

Actual = ₹25M
Target = ₹30M

Achievement =

25 / 30 × 100

= 83.3%

Dashboard

  • Sales
  • ₹25M
  • 83.3% of Target

6.15.19Sales Variance

Formula

Variance =

Actual - Target

Example

Actual = ₹25M
Target = ₹30M
Variance = -₹5M

Percentage variance

Variance %

=

(Actual - Target)

---------------- × 100

Target

Result

-16.7%

6.15.20Sales Pipeline

A sales pipeline tracks opportunities through stages

Lead
Qualified
Proposal
Negotiation
Won

Important pipeline KPIs

  • Pipeline Value
  • Number of Opportunities
  • Win Rate
  • Average Deal Size
  • Sales Cycle
  • Stage Conversion

6.15.21Pipeline Value

Example

  • Opportunity A = ₹10L
  • Opportunity B = ₹20L
  • Opportunity C = ₹15L
  • Pipeline Value = ₹45L

But total pipeline value does not necessarily equal expected revenue.

6.15.22Weighted Pipeline

Suppose

Opportunity A

Value = ₹10L
Probability = 80%
Opportunity B
Value = ₹20L
Probability = 40%

Weighted pipeline

10 × 80%

+

20 × 40%

= ₹8L + ₹8L

= ₹16L

Formula

Weighted Pipeline =

Σ(Opportunity Value × Probability)

6.15.23Win Rate

Formula

Win Rate %

=

Won Opportunities

----------------- × 100

Total Closed Opportunities

Example

Won = 40
Lost = 60

Win Rate = 40%

Be careful about the denominator. Organizations sometimes define win rate differently.

6.15.24Sales Cycle Length

  • Measures how long it takes to close a deal.
  • Sales Cycle =
  • Close Date - Opportunity Date

Example

  • Opportunity Created
  • January 1
  • Closed
  • January 31
  • Sales Cycle = 30 days

Lower is not always better if deal quality or deal size changes; interpret it with context.

6.15.25Marketing KPIs

Important marketing KPIs

  • Website Traffic
  • Leads
  • Marketing Qualified Leads
  • Lead Conversion Rate
  • Customer Acquisition Cost
  • Cost per Lead
  • Click-Through Rate
  • Conversion Rate
  • Return on Ad Spend
  • Customer Lifetime Value

6.15.26Customer Acquisition Cost

CAC measures the average cost to acquire a customer.

Formula

CAC =

Sales + Marketing Cost

----------------------

New Customers

Example

Marketing Cost = ₹10L

New Customers = 500

CAC = ₹2,000

6.15.27Cost per Lead

Formula

CPL =

Marketing Cost

-------------

Number of Leads

Example

Marketing Cost = ₹5L

Leads = 10,000
CPL = ₹50

6.15.28Customer Lifetime Value

CLV or LTV estimates the economic value generated by a customer over their relationship with the business.

A simplified model

  • LTV ≈
  • Average Order Value
  • × Purchase Frequency
  • × Customer Lifetime

Example

AOV = ₹1,000

Purchases/year = 5

Lifetime = 4 years

LTV ≈ ₹20,000

Actual LTV models may incorporate margin, retention, discounting, and customer-specific behavior.

6.15.29CAC vs LTV

This is an important business relationship.

CAC = Cost to acquire customer
LTV = Value generated by customer

Suppose

CAC = ₹2,000
LTV = ₹10,000

The relationship looks attractive, but the organization should also consider gross margin, payback period, retention, and other costs.

6.15.30Customer KPIs

Common customer KPIs

  • Customer Count
  • New Customers
  • Returning Customers
  • Retention Rate
  • Churn Rate
  • Customer Lifetime Value
  • Average Order Value
  • Customer Satisfaction
  • Net Promoter Score
  • Customer Complaint Rate

6.15.31Customer Retention Rate

One common formula

Retention Rate %

=

(Ending Customers - New Customers)

---------------------------------- × 100

Beginning Customers

Example

Beginning = 1,000
Ending = 1,100
New = 200

Retention =

(1,100 - 200) / 1,000 × 100

= 90%

6.15.32Churn Rate

Churn measures customers lost during a period.

A common formula

Churn Rate %

=

Customers Lost

-------------- × 100

Customers at Beginning

Example

Beginning Customers = 10,000

Lost Customers = 500

Churn = 5%

The exact definition should be standardized for the business.

6.15.33Customer Satisfaction

Customer Satisfaction Score (CSAT) is commonly measured through surveys.

For example

Satisfied Responses

------------------- × 100

Total Responses

If

Satisfied = 800
Responses = 1,000

Then

CSAT = 80%

6.15.34Net Promoter Score

NPS is based on a 0–10 recommendation question.

Respondents are categorized as

0–6 → Detractors

7–8 → Passives

9–10 → Promoters

Formula

NPS =

% Promoters

-

% Detractors

Example

Promoters = 60%
Detractors = 20%
NPS = 40

NPS ranges from -100 to +100.

6.15.35Operational KPIs

Operational KPIs measure process efficiency and reliability.

Examples

  • Cycle Time
  • Processing Time
  • Throughput
  • Capacity Utilization
  • SLA Compliance
  • Error Rate
  • Defect Rate
  • Downtime
  • On-Time Delivery

6.15.36Cycle Time

Cycle time measures how long a process takes.

Example

Order Received
Order Delivered

Cycle Time = Delivery Date - Order Date

Average cycle time

avg_cycle_time = (
    df["Cycle_Time"]
    .mean()
)

6.15.37Throughput

Throughput measures how much work is completed within a period.

Example

1,000 orders

per day

or

500 pipeline records

per minute

Higher throughput is generally desirable, but only if quality and SLA requirements are maintained.

6.15.38Capacity Utilization

Formula

Utilization %

=

Actual Capacity Used

-------------------- × 100

Available Capacity

Example

Used = 800 hours
Available = 1,000 hours
Utilization = 80%

6.15.39SLA Compliance

SLA = Service Level Agreement

Suppose

Total Requests = 10,000

Requests Within SLA = 9,700

Then

SLA Compliance =

9,700 / 10,000 × 100

= 97%

6.15.40Error Rate

Formula

Error Rate %

=

Errors

------- × 100

Total Transactions

Example

Errors = 200
Transactions = 100,000

Error Rate = 0.2%

6.15.41Defect Rate

Manufacturing example

Defective Units

--------------- × 100

Total Units

If

Defective = 500
Produced = 100,000

Then

Defect Rate = 0.5%

6.15.42On-Time Delivery

Formula

On-Time Delivery %

=

On-Time Orders

--------------- × 100

Total Orders

Example

On-time = 9,500

Total = 10,000
OTD = 95%

6.15.43IT / Data Engineering KPIs

For technology and data teams, useful KPIs include

  • Pipeline Success Rate
  • Pipeline Failure Rate
  • Pipeline SLA Compliance
  • Data Freshness
  • Data Quality Score
  • Average Pipeline Duration
  • Incident Count
  • Mean Time to Recovery
  • System Availability

6.15.44Pipeline Success Rate

Formula

Success Rate %

=

Successful Runs

--------------- × 100

Total Runs

Example

Successful = 950
Total = 1,000

Success Rate = 95%

Python

success_rate = (
    (df["Status"] == "Success").sum()
    / len(df)
    * 100
)

6.15.45Pipeline Failure Rate

Failure Rate %

=

Failed Runs

----------- × 100

Total Runs

If

Failed = 20
Total = 1,000

Then

Failure Rate = 2%

6.15.46Data Freshness

Data freshness measures how recently data was updated.

Example

  • Last Successful Refresh
  • 10:30 AM
  • Current Time
  • 10:45 AM
  • Data Age
  • 15 minutes

A dashboard could display

Data Freshness

  • 15 minutes ago

6.15.47System Availability

Formula

Availability %

=

Available Time

-------------- × 100

Total Time

Example

Available = 719 hours
Total = 720 hours

Availability ≈ 99.86%

6.15.48Mean Time to Recovery

MTTR measures average time needed to recover from failures.

A simplified formula

MTTR =

Total Recovery Time

-------------------

Number of Incidents

Example

Recovery time = 20 hours

Incidents = 5
MTTR = 4 hours

6.15.49HR KPIs

Common HR KPIs

  • Employee Turnover
  • Attrition Rate
  • Absenteeism
  • Time to Hire
  • Cost per Hire
  • Employee Retention
  • Training Completion
  • Employee Engagement

6.15.50Employee Turnover

A common formula

Turnover Rate %

=

Employees Leaving

----------------- × 100

Average Employees

Example

Employees leaving = 20

Average employees = 500

Turnover = 4%

6.15.51Time to Hire

Measures time from job opening or candidate entry to hiring, depending on the organization's definition.

Example

  • Job Opened → Employee Joined
  • = 35 days
  • Useful for evaluating recruitment efficiency.

6.15.52Supply Chain KPIs

Examples

  • Inventory Turnover
  • Stockout Rate
  • On-Time Delivery
  • Order Fulfillment Rate
  • Lead Time
  • Supplier Defect Rate
  • Warehouse Utilization

6.15.53Inventory Turnover

A commonly used formula

Inventory Turnover =

COGS

----

Average Inventory

Higher turnover can indicate efficient inventory movement, but excessively high turnover can also signal insufficient stock.

6.15.54Stockout Rate

A simple measure

Stockout Rate %

=

Stockout Events

--------------- × 100

Total Demand Events

The exact definition depends on the inventory system.

6.15.55Leading vs Lagging KPIs

This is an important concept.

Lagging KPI

Measures something that has already happened.

Examples

  • Revenue
  • Profit
  • Churn
  • Quarterly Sales
  • Leading KPI

Provides an early signal of future performance.

Examples

  • Qualified Leads
  • Website Engagement
  • Pipeline Value
  • Product Usage
  • Customer Complaints

Conceptually

Leading KPI
Future Performance
Lagging KPI

6.15.56Example

Suppose

Qualified Leads ↓

This could be a leading signal.

Later

Sales Revenue ↓

The revenue decline is a lagging result.

Therefore

Leading KPIs help you anticipate problems; lagging KPIs help you measure outcomes.

6.15.57KPI Tree

KPIs can be connected hierarchically.

For example

Revenue
├── Number of Customers
├── Orders per Customer
└── Average Order Value

Since

  • Revenue
  • ≈ Customers
  • × Orders per Customer
  • × Average Order Value
  • This helps explain why revenue changes.

6.15.58Profit KPI Tree

Profit
├── Revenue

│ ├── Customers

│ ├── Orders

│ └── Average Order Value

  • └── Costs
  • ├── COGS
  • ├── Marketing
  • ├── Operations
  • └── Administration

This helps management move from

What changed?

to

What caused the change?

6.15.59KPI Target Setting

A KPI becomes much more useful when paired with a target.

Example

KPIActualTargetVariance
Revenue₹25M₹30M-₹5M
Profit₹5M₹4.5M+₹0.5M
Orders4,8005,000-200
Success Rate98%95%+3 pp

Now the dashboard communicates performance relative to expectations.

6.15.60KPI Traffic-Light System

A common dashboard approach

  • Green
  • → On target
  • Amber
  • → At risk
  • Red
  • → Off target

For example

Revenue 🔴 83%

Profit 🟢 111%

Orders 🟠 96%

Thresholds should be defined based on business requirements, not arbitrary colors.

6.15.61KPI Trend

A KPI should often show its direction.

Example

Revenue

₹25M

↑ 12%

Profit

₹5M

↑ 8%

Customers

45K

↓ 3%

The number tells you where you are.

The trend tells you where you are going.

6.15.62KPI Dashboard Design

A typical executive KPI dashboard

┌─────────────────────────────────────────────┐

│ BUSINESS PERFORMANCE │

├────────────┬────────────┬──────────┬────────┤

│ Revenue │ Profit │ Growth │ Margin │

│ ₹25M │ ₹5M │ +12% │ 20% │

├────────────┴────────────┴──────────┴────────┤

│ │

│ Revenue Trend │

│ │

├─────────────────────┬───────────────────────┤

│ Revenue by Region │ Profit by Category │

│ │ │

├─────────────────────┴───────────────────────┤

│ KPI Performance vs Target │

└─────────────────────────────────────────────┘

6.15.63KPI Dashboard Principles

A good KPI dashboard should show

Current Value

+

Target

+

Variance

+

Trend

+

Context

For example

  • Revenue
  • ₹25M
  • Target ₹30M

↓ 16.7%

vs Last Month

This is much more informative than

Revenue

₹25M

6.15.64KPI Calculation Using Pandas

Suppose

df = pd.DataFrame({
    "Order_ID": [1, 2, 3, 4],
    "Revenue": [1000, 2000, 1500, 2500],
    "Cost": [600, 1200, 900, 1500]
})

Calculate

total_revenue = (
    df["Revenue"].sum()
)
total_cost = (
    df["Cost"].sum()
)
profit = (
    total_revenue -
    total_cost
)
profit_margin = (
    profit /
    total_revenue *
    100
)

6.15.65KPI Calculation Example

Output

Total Revenue = ₹7,000

Total Cost = ₹4,200

Profit = ₹2,800

Profit Margin = 40%

This can feed directly into a dashboard.

6.15.66KPI by Region

regional_kpi = (
    df.groupby("Region")
    .agg(
        Revenue=("Revenue", "sum"),
        Profit=("Profit", "sum"),
        Orders=("Order_ID", "nunique")
    )
)

Then

  • regional_kpi["Profit_Margin"] = (
  • regional_kpi["Profit"]
  • / regional_kpi["Revenue"]
  • * 100

)

6.15.67KPI Time-Series Analysis

Suppose you have

  • Date
  • Revenue
  • Profit
  • Orders

Create monthly KPIs

monthly = (
    df.set_index("Date")
    .resample("ME")
    .agg({
        "Revenue": "sum",
        "Profit": "sum",
        "Orders": "sum"
    })
)

Calculate margin

  • monthly["Profit_Margin"] = (
  • monthly["Profit"]
  • / monthly["Revenue"]
  • * 100

)

Now you can visualize

  • Revenue Trend
  • Profit Trend
  • Margin Trend
  • Order Trend

6.15.68KPI Storytelling

Suppose

Revenue ↑ 15%

Profit ↑ 3%

A weak conclusion

Revenue increased.

A better conclusion

Revenue grew 15%, but profit increased only 3%, indicating margin pressure.

Then investigate

Revenue ↑
Costs ↑ faster
Gross Margin ↓
Profit Growth limited

This is where KPIs connect directly to Data Storytelling.

6.15.69KPI Relationships

KPIs should not always be viewed independently.

For example

Revenue ↑

Profit ↓

Potential issue

Costs increasing

Or

Customers ↑

Revenue flat

Potential issue

AOV ↓

Or

Traffic ↑

Conversions ↓

Potential issue

Conversion rate deterioration

Good KPI analysis looks at relationships between metrics.

6.15.70North Star Metric

A North Star Metric is a central measure that represents the value a company delivers to customers and uses to align teams around growth.

Examples can vary by business

  • Marketplace
  • → Successful transactions
  • Subscription service
  • → Active subscribers receiving value
  • SaaS
  • → Weekly active teams

It should not simply be the biggest number; it should represent meaningful customer/business value.

6.15.71Vanity Metrics

A vanity metric may look impressive but provide limited decision value.

Example

Social Media Followers

If followers increase but

  • Revenue
  • Conversions
  • Retention

do not improve, the follower count may not be an important KPI for the business objective.

Always ask

Does this metric help us make a better decision?

6.15.72KPI Governance

Large organizations need standardized KPI definitions.

For every KPI, document

KPI Name

Definition

  • Formula
  • Data Source
  • Owner
  • Frequency
  • Target
  • Threshold
  • Refresh Time
  • Business Meaning

Example

KPI: Pipeline Success Rate

Definition

Percentage of scheduled pipeline runs

completed successfully.

Formula

Successful Runs / Total Runs × 100

Source

Pipeline Monitoring Table

Owner

Data Engineering

Frequency

Daily

Target

≥ 98%

This prevents different teams from calculating the same KPI differently.

6.15.73KPI Data Model

A typical business analytics architecture

Source Systems

┌────────────┼────────────┐

▼ ▼ ▼

CRM ERP Website

│ │ │

└────────────┼────────────┘

ETL / ELT
Data Warehouse
Semantic Model
KPI Measures
Dashboard

6.15.74KPI Refresh Frequency

Different KPIs need different refresh schedules.

KPITypical Frequency
Stock priceReal-time
Website trafficMinutes/Hours
Pipeline statusMinutes
SalesHourly/Daily
RevenueDaily
ProfitDaily/Monthly
Employee turnoverMonthly
Strategic KPIsMonthly/Quarterly

Don't make a KPI "real-time" unless the business actually benefits from real-time data.

6.15.75KPI Dashboard Example

Imagine an e-commerce dashboard

┌────────────────────────────────────────────────┐

│ E-COMMERCE KPI DASHBOARD │

├───────────┬───────────┬───────────┬────────────┤

│ Revenue │ Profit │ Orders │ Customers │

│ ₹25.4M │ ₹5.1M │ 48.2K │ 32.5K │

│ ↑ 12% │ ↑ 8% │ ↑ 15% │ ↑ 6% │

├───────────┴───────────┴───────────┴────────────┤

│ │

│ Revenue Trend │

│ │

├──────────────────────┬─────────────────────────┤

│ Revenue by Region │ Profit Margin │

│ │ by Category │

├──────────────────────┴─────────────────────────┤

│ KPI vs Target │

│ Revenue ████████████ 92% │

│ Profit █████████████ 105% │

│ Orders ████████████ 98% │

└────────────────────────────────────────────────┘

6.15.76Common KPI Mistakes

1. Too many KPIs

If everything is a KPI, nothing is important.

2. No target

A KPI without context can be difficult to interpret.

3. Unclear definition

Everyone must understand exactly what the KPI means.

4. Wrong denominator

For example

Conversion Rate =

Orders / Visitors

not

Orders / Revenue

unless that is intentionally defined for a specific purpose.

5. Mixing incompatible time periods

Don't compare

January revenue

against

Q4 revenue

without adjusting for the time period.

6. Ignoring seasonality

December sales may naturally be higher than February.

7. Measuring what is easy instead of what matters

A metric is not automatically a KPI just because it is easy to calculate.

8. No owner

Someone should be responsible for monitoring and acting on important KPIs.

6.15.77KPI Best-Practice Checklist

For every KPI ask

  • 1. What business objective does it measure?
  • 2. What is the exact definition?
  • 3. What is the formula?
  • 4. What is the data source?
  • 5. Who owns it?
  • 6. How frequently does it refresh?
  • 7. What is the target?
  • 8. What is the acceptable threshold?
  • 9. What does good performance look like?
  • 10. What action should happen when it deteriorates?
  • 6.15.78Mini Project — Business KPI Dashboard

Dataset

Use

  • Order_ID
  • Order_Date
  • Customer_ID
  • Region
  • Category
  • Product
  • Quantity
  • Revenue
  • Cost
  • Profit
  • Customer_Segment
  • Calculate
  • Financial KPIs
  • Total Revenue
  • Gross Profit
  • Profit Margin
  • Revenue Growth
  • Sales KPIs
  • Orders
  • Average Order Value
  • Target Achievement
  • Sales Growth
  • Customer KPIs
  • Customers
  • New Customers
  • Returning Customers
  • Retention
  • Operational KPIs
  • Orders per Day
  • Average Processing Time
  • Order Fulfillment Rate

6.15.79Dashboard Layout

Create

┌─────────────────────────────────────────────┐

│ BUSINESS KPI DASHBOARD │

├───────────┬───────────┬───────────┬─────────┤

│ Revenue │ Profit │ Orders │ AOV │

├───────────┴───────────┴───────────┴─────────┤

│ │

│ Revenue & Profit Trend │

│ │

├──────────────────────┬──────────────────────┤

│ Revenue by Region │ Profit by Category │

├──────────────────────┼──────────────────────┤

│ Customer Segment │ KPI vs Target │

└──────────────────────┴──────────────────────┘

Add filters

  • Date
  • Region
  • Category
  • Customer Segment

6.15.80Interview Questions

  • Basic
  • What is a KPI?
  • What is the difference between a KPI and a metric?
  • What makes a good KPI?
  • What are financial KPIs?
  • What are sales KPIs?
  • What are customer KPIs?
  • What are operational KPIs?
  • What is a leading KPI?
  • What is a lagging KPI?
  • What is a North Star Metric?

Intermediate

  • How do you calculate revenue growth?
  • How do you calculate profit margin?
  • How do you calculate AOV?
  • How do you calculate conversion rate?
  • How do you calculate customer retention?
  • How do you calculate churn?
  • What is CAC?
  • What is LTV?
  • What is ROAS?
  • What is sales target achievement?

Advanced

  • How would you define KPIs with business stakeholders?
  • How do you prevent inconsistent KPI definitions?
  • How do you design a KPI hierarchy?
  • How do you identify the root cause of KPI deterioration?
  • How would you design an executive KPI dashboard?
  • How do leading and lagging indicators work together?
  • How would you handle a KPI whose value looks good but business performance is actually deteriorating?
  • How do you validate KPI calculations?
  • How would you optimize KPI calculations for a large data warehouse?
  • How would you design KPI governance for an enterprise BI environment?

6.15.81Key Takeaways

The fundamental KPI framework is

BUSINESS OBJECTIVE
KPI
FORMULA
ACTUAL VALUE
TARGET
VARIANCE
TREND
INSIGHT
ACTION

Most important KPIs

CategoryImportant KPIs
FinancialRevenue, Profit, Margin, ROI
SalesRevenue Growth, AOV, Win Rate, Pipeline
MarketingCAC, CPL, Conversion Rate, ROAS
CustomerRetention, Churn, LTV, CSAT, NPS
OperationsCycle Time, Throughput, SLA, Error Rate
IT/DataSuccess Rate, Failure Rate, Freshness, Availability, MTTR
Supply ChainInventory Turnover, Stockout Rate, Lead Time, OTD
HRTurnover, Attrition, Time to Hire, Engagement

The most important principle is

A KPI is valuable only when it is connected to a business objective and leads to a decision or action.

A mature KPI analysis therefore goes beyond

Revenue = ₹25M

and asks

Revenue = ₹25M

Target = ₹30M

Gap = ₹5M

Why?

South region + Electronics

What should we do?

Inventory + sales action

That is how Business KPIs → Dashboard → Data Storytelling → Business Decision connect together.

Module 6 · Lesson 6.16

Excel for Data Analysis

Lesson focus: This lesson is part of Module 6 — Data Analysis & Visualization. Detailed lesson content can be added here from the corresponding source material.
Module 6 · Lesson 6.17

Power BI Basics

Lesson focus: This lesson is part of Module 6 — Data Analysis & Visualization. Detailed lesson content can be added here from the corresponding source material.
Module 6 · Lesson 6.18

Tableau Basics

Lesson focus: This lesson is part of Module 6 — Data Analysis & Visualization. Detailed lesson content can be added here from the corresponding source material.
Module 6 · Lesson 6.19

Case Study

Lesson focus: This lesson is part of Module 6 — Data Analysis & Visualization. Detailed lesson content can be added here from the corresponding source material.
Module 6 · Lesson 6.20

Visualization Project

Lesson focus: This lesson is part of Module 6 — Data Analysis & Visualization. Detailed lesson content can be added here from the corresponding source material.