Knowledge / Artificial Intelligence and Data Science / Data cleaning and feature engineering

Data cleaning and feature engineering

Artificial Intelligence and Data Science (B.E./B.Tech) · Programming and data · free study notes for exams, placements and interviews

In short

Data cleaning is the set of operations that turn raw, messy records into a consistent, analysable table — fixing missing values, duplicates, wrong types, inconsistent categories and outliers. Feature engineering is the step that follows: converting cleaned columns into numeric predictors a model can learn from, by encoding, scaling, transforming and combining them. Together they consume 60-80% of a real data science project's effort and usually decide model accuracy more than the choice of algorithm. Indian university syllabi examine this under Data Science / Machine Learning labs and the "data preprocessing" unit; GATE DA (Data Science and AI, introduced 2024) tests imputation, standardisation, outliers and the curse of dimensionality. Placement rounds at TCS, Infosys, Wipro, Accenture, Mu Sigma, Fractal, ZS Associates, Flipkart, Swiggy and bank analytics teams almost always include a pandas cleaning task and a question on data leakage.

Explained

What "dirty data" looks like and the quality dimensions

Handling missing values

Duplicates, types and consistency

  1. Exact duplicates: df.duplicated(subset=key_cols) → drop_duplicates(keep='first'). Decide the business key first (order_id? customer+timestamp?).
  2. Fuzzy / near duplicates: same entity typed differently. Standardise (lowercase, strip, remove punctuation) then match with edit distance (Levenshtein), Jaro-Winkler, or phonetic keys (Soundex, Metaphone) — "record linkage" or "entity resolution". Blocking on a coarse key (PIN code, first letter) keeps the pairwise comparison tractable.
  3. Type fixing: strip ₹, commas and "Lakh"; pd.to_numeric(..., errors='coerce'); pd.to_datetime(..., dayfirst=True) because Indian data is DD-MM-YYYY while the pandas default reads MM-DD-YYYY.
  4. Categorical standardisation: case folding, whitespace trim, mapping tables for state/city names, validating against masters — PIN code (6 digits), GSTIN (15 characters: 2-digit state code + 10-character PAN + entity code + "Z" + checksum), Aadhaar (12 digits, Verhoeff check digit), IFSC (4 letters + "0" + 6 alphanumerics), mobile (10 digits starting 6-9).
  5. Constraint checks: range (0 ≤ age ≤ 120), cross-field (date_of_discharge ≥ date_of_admission), uniqueness, referential integrity against a dimension table.
  6. Privacy note: personal data cleaning in India is governed by the Digital Personal Data Protection Act, 2023; its rules were notified in 2025 with phased implementation, so obligations such as consent notices and breach reporting are being rolled out in stages. Practical effect: de-identify or mask Aadhaar/PAN/mobile before analysis, keep purpose limitation in mind, and do not copy production PII into notebooks. (Dates of each phase have been revised; check the current notification.)

Outliers

Feature engineering: transformations and encodings

Feature selection, dimensionality and the pipeline

Key points

Formulas and examples

Z-score standardisation: z = (x − μ) / σ — μ = column mean, σ = population SD (same unit as x); z is dimensionless.
Min-max normalisation: x′ = (x − x_min) / (x_max − x_min) → range [0, 1]; to [a, b]: x′ = a + (x − x_min)(b − a)/(x_max − x_min).
Robust scaling: x′ = (x − median) / IQR, where IQR = Q3 − Q1 (same unit as x).
IQR outlier fence: lower = Q1 − 1.5·IQR, upper = Q3 + 1.5·IQR (extreme fences use 3·IQR).
MAD and robust z: MAD = median(|xᵢ − median(x)|); z_robust = 0.6745(x − median)/MAD; flag |z_robust| > 3.5.
Pearson correlation: r = Σ(xᵢ − x̄)(yᵢ − ȳ) / √[Σ(xᵢ − x̄)² · Σ(yᵢ − ȳ)²], −1 ≤ r ≤ 1.
Variance Inflation Factor: VIF_j = 1/(1 − R²_j), R²_j from regressing feature j on all other features; VIF > 5-10 ⇒ multicollinearity.
Pearson's moment coefficient of skewness: g₁ = (1/n)Σ(xᵢ − x̄)³ / s³; g₁ > 0 means right-skewed (log transform helps).
Smoothed target encoding: enc(c) = (n_c · ȳ_c + m · ȳ_global) / (n_c + m) — n_c = rows in category c, ȳ_c = its target mean, m = smoothing weight.
Box-Cox: y(λ) = (xλ − 1)/λ for λ ≠ 0, ln x for λ = 0 (x > 0). Yeo-Johnson extends it to x ≤ 0.
Missing rate: % missing = (missing cells in column / total rows) × 100.

Example 1 — IQR outlier detection on monthly salaries (₹ thousand). Data (n = 11, sorted): 18, 22, 25, 28, 30, 33, 36, 40, 45, 52, 180.

Example 2 — scaling the same column (train statistics only). Train = the 10 values without 180: mean = 32.9; Σ(x − x̄)² = 222.01 + 118.81 + 62.41 + 24.01 + 8.41 + 0.01 + 9.61 + 50.41 + 146.41 + 364.81 = 1006.90 → population variance = 100.69, σ = 10.03.

Example 3 — smoothed target encoding of city for a loan-default model. Global default rate ȳ = 0.12; smoothing m = 10.

Cityn_cdefaultsȳ_cEncoded value
Mumbai500500.100(500×0.10 + 10×0.12)/510 = 51.2/510 = 0.1004
Pune4080.200(40×0.20 + 10×0.12)/50 = 9.2/50 = 0.1840
Nagpur520.400(5×0.40 + 10×0.12)/15 = 3.2/15 = 0.2133

Example 4 — missing-value decision table for a customer dataset of 10,000 rows.

ColumnMissing%MechanismAction
age1501.5MCARMedian impute
monthly_income2,40024.0MNAR (high earners skip)Median impute + income_missing flag
city900.9MCARMode impute or "Unknown"
second_phone8,70087.0—Drop column
last_txn_date6006.0MAR (new customers)Fill with onboarding date; add days_since_txn

Example 5 — pandas / scikit-learn pipeline that avoids leakage.

import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression

df = pd.read_csv("loans.csv")
df["income"] = (df["income"].astype(str)
                  .str.replace(r"[₹,]", "", regex=True)
                  .pipe(pd.to_numeric, errors="coerce"))
df["app_date"] = pd.to_datetime(df["app_date"], dayfirst=True, errors="coerce")
df["city"] = df["city"].str.strip().str.title()
df = df.drop_duplicates(subset="application_id")

num = ["age", "income", "emi"]
cat = ["city", "employment_type"]
X_tr, X_te, y_tr, y_te = train_test_split(
    df[num + cat], df["default"], test_size=0.2,
    stratify=df["default"], random_state=42)      # split FIRST

pre = ColumnTransformer([
    ("n", Pipeline([("imp", SimpleImputer(strategy="median", add_indicator=True)),
                    ("sc", StandardScaler())]), num),
    ("c", Pipeline([("imp", SimpleImputer(strategy="most_frequent")),
                    ("oh", OneHotEncoder(handle_unknown="ignore",
                                         drop="first"))]), cat)])

model = Pipeline([("pre", pre), ("clf", LogisticRegression(max_iter=1000))])
model.fit(X_tr, y_tr)        # all statistics learned from train only
print(model.score(X_te, y_te))
NeedUseWhy
Skewed numeric, outliers presentMedian impute + RobustScaler or log1pMean and SD are dragged by extremes
Nominal category, ≤ 15 levelsOne-hot (drop first for linear models)No false ordering; avoids dummy trap
Nominal category, thousands of levelsTarget (out-of-fold) / hashing / WoEOne-hot would explode dimensionality
Ordered categoryOrdinal encoding with explicit orderPreserves the real ranking
Tree-based modelImpute only; skip scalingSplits depend on order, not scale
Time-ordered dataChronological split, lag featuresRandom split leaks the future

Where it is used

Interview questions

Common mistakes

Related topics

Python for data analysis: NumPy and pandas · Python ML libraries: scikit-learn, PyTorch and TensorFlow · Data collection and web scraping · Data visualisation · SQL queries and joins · Probability and statistics for data science

Open in the Knowledge reader →Find jobs →