Siksha Sarovar

Siksha Sarovar (sikshasarovar.com) is a free educational web application that helps students in India learn programming and prepare for academic and competitive exams. The platform offers structured coding courses (C, C++, Python, Java, HTML, CSS, PHP, Power BI, AI, Machine Learning, Data Science), complete university curriculum notes for BCA/MCA students with previous year question papers, Class 10 and Class 12 CBSE/HBSE school notes, and dedicated preparation material for SSC, UPSC, Banking, Railway and other government exams. Browsing the site is completely free and requires no account. Users may optionally sign in with Google solely to save their learning progress, quiz scores and personal preferences across devices.

Privacy Policy | Terms of Service | Contact Siksha Sarovar | About Siksha Sarovar

v4.0.9 · PWA
Siksha Sarovar logo
Siksha Sarovar
Your Learning Universe

Siksha Sarovar is a free e-learning platform for coding courses, BCA university notes and competitive exam preparation. Optional Google sign-in saves your learning progress across devices.

Initializing knowledge base…
Compiling modules 0%

Unit 1 — Data Cleaning

Lesson 10 of 46 in the free Introduction to Data Analytics notes on Siksha Sarovar, written by Rohit Jangra.

Data Cleaning

Data cleaning (data cleansing / scrubbing) is the process of detecting and correcting corrupt, inaccurate, incomplete, duplicate or irrelevant records in a dataset. It is the single most time-consuming activity in analytics — commonly 60–80% of a project's effort.

Garbage In, Garbage Out (GIGO) — no algorithm can rescue conclusions built on dirty data.

Dimensions of Data Quality

DimensionQuestion it answers
AccuracyDo the values reflect reality?
CompletenessAre required values present?
ConsistencyDo values agree across sources/records?
ValidityDo values conform to defined rules/ranges?
UniquenessIs each real-world entity stored exactly once?
TimelinessIs the data current enough to be useful?

1. Handling Missing Values

Why data goes missing: non-response, sensor failure, data-entry omission, merge mismatches, or a value genuinely not applicable.

Types of missingness:

TypeMeaningExample
MCAR (Missing Completely At Random)Missingness unrelated to any variableA sensor randomly drops a reading
MAR (Missing At Random)Missingness depends on observed variablesOlder respondents skip income questions more often
MNAR (Missing Not At Random)Missingness depends on the missing value itselfHigh earners hide their income

Treatment strategies:

StrategyMethodWhen to useRisk
DeletionListwise (drop the whole row) / pairwiseVery few missing rows, MCARData loss, bias if not MCAR
Drop the columnColumn is >50–60% missingLoses a potentially useful feature
Mean imputationFill with column meanNumeric, roughly symmetricShrinks variance; distorted by outliers
Median imputationFill with column medianNumeric, skewed or with outliersShrinks variance
Mode imputationFill with most frequent valueCategorical columnsOver-represents the majority class
Forward/backward fillCarry last/next valueTime-series dataWrong if the series changes fast
InterpolationEstimate from neighboursOrdered numeric/time dataAssumes smooth trend
KNN imputationAverage of k most similar rowsCorrelated features availableComputationally heavier
Model-based / regressionPredict the missing valueStrong relationships existCan invent artificial precision
Flag + fillAdd an "is_missing" indicator columnMNAR — missingness itself is informativeAdds dimensions
import pandas as pd
import numpy as np

df = pd.DataFrame({
    "name":   ["Riya", "Zoya", "Kabir", "Aarav", "Meera"],
    "age":    [19, np.nan, 21, 20, np.nan],
    "marks":  [88, 76, np.nan, 54, 91],
    "city":   ["Delhi", "Noida", np.nan, "Delhi", "Delhi"],
})

# --- Detect ---
print(df.isnull().sum())
# age 2, marks 1, city 1
print(round(df.isnull().mean() * 100, 1))   # % missing per column

# --- Treat ---
df["age"]   = df["age"].fillna(df["age"].median())      # skew-safe
df["marks"] = df["marks"].fillna(df["marks"].mean())    # numeric, symmetric
df["city"]  = df["city"].fillna(df["city"].mode()[0])   # categorical -> mode
print(df.isnull().sum().sum())   # 0
# KNN imputation — uses similar rows instead of a single global statistic
from sklearn.impute import KNNImputer

num = pd.DataFrame({"age": [19, np.nan, 21, 20], "marks": [88, 76, np.nan, 54]})
imputer = KNNImputer(n_neighbors=2)
filled = pd.DataFrame(imputer.fit_transform(num), columns=num.columns)
print(filled.round(2))

2. Removing Duplicates

Duplicates arise from repeated form submissions, merging datasets, or ETL reruns. They inflate counts and bias every statistic.

df2 = pd.DataFrame({
    "roll": [101, 102, 101, 103, 102],
    "name": ["Riya", "Zoya", "Riya", "Kabir", "Zoya"],
    "marks": [88, 76, 88, 91, 79],
})

print(df2.duplicated().sum())                    # 1  (exact duplicate row)
df2_clean = df2.drop_duplicates()                # drops the exact duplicate

# Business rule: one row per roll number — keep the latest record
df2_latest = df2.drop_duplicates(subset=["roll"], keep="last")
print(df2_latest)
Watch for near-duplicates: "Riya Sharma" vs "riya sharma" vs "Riya Sharma". Normalise case and whitespace before de-duplicating (fuzzy matching handles harder cases).

3. Detecting and Treating Outliers

An outlier is an observation that lies abnormally far from the rest of the data.

Detection methods:

IQR method (most common, distribution-free)
    Q1 = 25th percentile,  Q3 = 75th percentile
    IQR = Q3 - Q1
    Lower fence = Q1 - 1.5 × IQR
    Upper fence = Q3 + 1.5 × IQR
    Anything outside the fences is an outlier

Z-score method (assumes roughly normal data)
    z = (x - mean) / standard deviation
    |z| > 3  =>  outlier
marks = pd.Series([55, 62, 58, 61, 60, 59, 63, 57, 5, 98])

Q1, Q3 = marks.quantile(0.25), marks.quantile(0.75)
IQR = Q3 - Q1
low, high = Q1 - 1.5 * IQR, Q3 + 1.5 * IQR
print(f"Fences: {low:.2f} to {high:.2f}")
print("Outliers:", marks[(marks < low) | (marks > high)].tolist())
# Fences: 47.62 to 72.38
# Outliers: [5, 98]

# Z-score alternative
z = (marks - marks.mean()) / marks.std()
print("Z-score outliers:", marks[z.abs() > 2].tolist())

Treatment options:

OptionDescriptionWhen appropriate
Investigate firstIs it a data-entry error or a genuine extreme value?Always the first step
CorrectFix the typo (age 250 → 25)Clear entry error
RemoveDelete the recordProven error, few rows
Cap / WinsorizeClip to the fence valuesGenuine but distorting extremes
TransformLog/square-root transform to compress the tailSkewed distributions
Keep and use robust statisticsMedian, IQR, robust modelsThe outlier is real and important (fraud detection!)
In fraud detection and fault detection, the outlier IS the signal — never blindly delete outliers.

4. Fixing Structural Errors

messy = pd.DataFrame({
    "city": [" delhi", "Delhi ", "DELHI", "noida", "Noida"],
    "gender": ["M", "male", "Male", "F", "female"],
})

messy["city"] = messy["city"].str.strip().str.title()
messy["gender"] = (messy["gender"].str.strip().str.lower()
                   .replace({"m": "Male", "male": "Male",
                             "f": "Female", "female": "Female"}))
print(messy["city"].unique())    # ['Delhi' 'Noida']
print(messy["gender"].unique())  # ['Male' 'Female']

Typical structural problems: inconsistent casing, stray whitespace, mixed date formats (14/01/2026 vs 2026-01-14), mixed units (kg vs g), spelling variants, and stray currency/percent symbols inside numeric columns.

5. Correcting Data Types

raw = pd.DataFrame({
    "roll": ["101", "102", "103"],
    "joined": ["2026-01-14", "2026-02-01", "2026-03-09"],
    "fee": ["12,500", "13,000", "11,750"],
})

raw["roll"] = raw["roll"].astype(int)
raw["joined"] = pd.to_datetime(raw["joined"])
raw["fee"] = raw["fee"].str.replace(",", "").astype(float)
print(raw.dtypes)
# roll               int64
# joined    datetime64[ns]
# fee              float64

6. Validation Rules

Define explicit rules and check every record against them:

def validate(df):
    problems = []
    if (df["age"] < 0).any() or (df["age"] > 120).any():
        problems.append("age out of valid range 0-120")
    if (df["marks"] < 0).any() or (df["marks"] > 100).any():
        problems.append("marks out of valid range 0-100")
    if df["roll"].duplicated().any():
        problems.append("duplicate roll numbers")
    return problems or ["All validation checks passed"]

check = pd.DataFrame({"roll": [1, 2, 3], "age": [19, 21, 20], "marks": [88, 76, 91]})
print(validate(check))    # ['All validation checks passed']

A Reusable Cleaning Checklist

  1. Profile the data — df.info(), df.describe(), df.isnull().sum(), df.nunique()
  2. Fix data types and parse dates
  3. Standardise text — case, whitespace, spelling, units
  4. Remove exact and near-duplicates
  5. Handle missing values with a documented, justified strategy
  6. Detect outliers; investigate before treating
  7. Apply domain validation rules
  8. Document every transformation — cleaning must be reproducible, never a one-off manual edit

Cleaning produces correct data. The next lesson, preprocessing, makes that correct data analysis-ready.