AI & Machine Learning Cheatsheet

Data Preprocessing and Normalization

Use this AI & Machine Learning reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Why Preprocessing Matters

Raw data is almost never in the form models can learn from effectively. Missing values cause errors; unscaled features cause gradient instability; unremedied categorical columns need numerical encoding. Careful preprocessing often improves model performance more than switching algorithms.

Garbage in, garbage out — no model compensates for poorly prepared data.

Inspecting Raw Data

Before anything, understand what you have:

import pandas as pd
import numpy as np

df = pd.read_csv("data.csv")

print(df.shape)            # (n_rows, n_cols)
print(df.dtypes)           # column types
print(df.describe())       # stats for numeric columns
print(df.isnull().sum())   # missing values per column
print(df.duplicated().sum()) # duplicate rows

# Distribution of categorical columns
print(df["category"].value_counts())

Handling Missing Values

StrategyWhen to UseRisk
Drop rows with any NaNVery few missing (<1%), large datasetLoses data
Drop columns with >N% missingColumn mostly empty (>70% missing)Lose feature
Mean/median imputationNumeric, MAR (missing at random)Distorts variance
Mode imputationCategoricalMay over-represent majority
Model-based imputationImportant feature, complex missingnessComputational
Add indicator columnMissingness is informativeExtra feature
Forward-fill / back-fillTime series, ordered dataAssumes continuity

MAR = Missing At Random (missingness doesn't depend on the missing value itself) MNAR = Missing Not At Random (dangerous — the value being missing is informative)

from sklearn.impute import SimpleImputer, KNNImputer

# Numeric columns: fill with median
num_imputer = SimpleImputer(strategy="median")
X_num = num_imputer.fit_transform(df[num_cols])

# Categorical: fill with most frequent
cat_imputer = SimpleImputer(strategy="most_frequent")
X_cat = cat_imputer.fit_transform(df[cat_cols])

# Model-based: KNN imputation
knn_imp = KNNImputer(n_neighbors=5)
X_imputed = knn_imp.fit_transform(X)

# Add missingness indicator
df["age_missing"] = df["age"].isnull().astype(int)
df["age"] = df["age"].fillna(df["age"].median())

Feature Scaling / Normalization

Many algorithms are sensitive to feature magnitude: gradient descent converges faster, and distance-based algorithms (KNN, SVM, k-means) are biased by scale.

Algorithms that NEED scaling: logistic regression, linear SVM, KNN, k-means, PCA, neural networks, gradient descent in general.

Algorithms INSENSITIVE to scaling: decision trees, random forests, gradient boosting.

Standardization (Z-score Normalization)

z = (x − μ) / σ

Result has mean 0 and standard deviation 1. Most common choice. Works well when features are approximately Normal; robust to different scales.

Min-Max Normalization

x' = (x − xₘᵢₙ) / (xₘₐₓ − xₘᵢₙ) ∈ [0, 1]

Useful when you need bounded output (e.g., neural net input, pixel values). Sensitive to outliers — one extreme value compresses all others.

Robust Scaler

x' = (x − median) / IQR

Uses interquartile range instead of std — resistant to outliers. Good when data has extreme values.

Max Absolute Scaler

x' = x / |xₘₐₓ| ∈ [−1, 1]

Preserves sparsity (zeros stay zero). Useful for sparse feature matrices.

from sklearn.preprocessing import StandardScaler, MinMaxScaler, RobustScaler

scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)  # fit on train only!
X_test_scaled  = scaler.transform(X_test)        # apply same transform

Critical rule: always fit scalers on training data only. Fitting on the entire dataset leaks test-set statistics (data leakage).

Encoding Categorical Variables

Models require numeric inputs. Strategies depend on the cardinality and ordinality of the variable.

One-Hot Encoding (OHE)

Create a binary column per category. Use for nominal (unordered) categories with low cardinality (< ~20 unique values).

# Pandas
dummies = pd.get_dummies(df["color"], prefix="color", drop_first=True)

# Sklearn
from sklearn.preprocessing import OneHotEncoder
enc = OneHotEncoder(sparse_output=False, handle_unknown="ignore")
X_ohe = enc.fit_transform(df[["color"]])

Ordinal Encoding

Map categories to integers preserving order (e.g., small=0, medium=1, large=2). Use when the feature IS ordered.

Target Encoding (Mean Encoding)

Replace each category with the mean of the target within that category. High-cardinality categories. Must use out-of-fold (from k-fold) to prevent leakage.

Label Encoding

Map categories to arbitrary integers (0, 1, 2, …). Only use with tree-based models — linear models will interpret the integer ordering as meaningful.

Hashing Trick

Map categories to hash buckets. Handles unseen categories and high cardinality without storing a vocabulary.

MethodCardinalityOrderingModel type
One-hotLow (<20)NoneAny
OrdinalAnyYesTree or linear
TargetHighNoneTree (with OOF)
EmbeddingVery highNoneNeural networks
HashingVery highNoneAny

Handling Outliers

Outliers can dominate loss functions, especially MSE.

Detection methods: - Z-score: |z| > 3 is often flagged - IQR rule: < Q1 − 1.5·IQR or > Q3 + 1.5·IQR - Isolation Forest, DBSCAN (algorithmic)

Treatment options: - Remove (if genuine data entry error) - Cap/clip (winsorize) to a percentile (e.g., clip to [1%, 99%]) - Log-transform to reduce skewness - Use robust loss functions (Huber, MAE instead of MSE)

# Winsorize
lower = df["price"].quantile(0.01)
upper = df["price"].quantile(0.99)
df["price"] = df["price"].clip(lower, upper)

# Log-transform (for positive, skewed features)
df["price_log"] = np.log1p(df["price"])   # log(1+x) safe for zeros

Feature Engineering

Transform existing features into more informative representations.

Polynomial Features

Add x², x³, x₁·x₂ etc. to model nonlinear relationships with a linear model.

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degree=2, include_bias=False)
X_poly = poly.fit_transform(X)  # adds x1², x2², x1*x2

Interaction Terms

Explicitly model how two features combine: income × age, price × discount.

Date/Time Features

Extract meaningful sub-features from timestamps: - Year, month, day, hour, weekday, quarter - Days since reference (ordinal time variable) - Is weekend, is holiday, is peak season - Cyclical encoding: sin(2π·month/12), cos(2π·month/12) — preserves circular structure

Text Features

  • Bag of words / TF-IDF vectors
  • Character count, word count, punctuation count
  • Readability scores

Binning / Discretization

Convert continuous to categorical (e.g., age → age_group). Useful when the relationship is non-monotone in a linear model.

df["age_bin"] = pd.cut(df["age"], bins=[0,18,35,60,100],
                        labels=["youth","adult","middle","senior"])

Feature Selection

Remove irrelevant or redundant features to reduce overfitting, speed up training, and improve interpretability.

MethodTypeHow
Variance thresholdFilterRemove near-zero-variance features
Correlation filterFilterRemove highly correlated pairs
Univariate testsFilterF-test, mutual information vs. target
L1 regularizationEmbeddedLasso drives irrelevant weights to 0
Feature importanceEmbeddedTree-based importance, permutation importance
Recursive Feature EliminationWrapperIteratively remove weakest feature
PCAProjectionNot selection, but reduces dimensionality
from sklearn.feature_selection import SelectKBest, mutual_info_classif, RFE
from sklearn.ensemble import RandomForestClassifier

# Filter: top-k features by mutual information
selector = SelectKBest(mutual_info_classif, k=10)
X_selected = selector.fit_transform(X_train, y_train)

# Embedded: feature importances from tree
rf = RandomForestClassifier().fit(X_train, y_train)
importances = rf.feature_importances_

The sklearn Pipeline

Combining preprocessing and model into one object prevents leakage and simplifies deployment:

from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.ensemble import GradientBoostingClassifier

# Define transformations per column type
num_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="median")),
    ("scaler", StandardScaler()),
])
cat_transformer = Pipeline([
    ("imputer", SimpleImputer(strategy="most_frequent")),
    ("ohe", OneHotEncoder(handle_unknown="ignore")),
])

preprocessor = ColumnTransformer([
    ("num", num_transformer, num_features),
    ("cat", cat_transformer, cat_features),
])

# Full pipeline
pipeline = Pipeline([
    ("preprocessor", preprocessor),
    ("classifier", GradientBoostingClassifier()),
])

pipeline.fit(X_train, y_train)
y_pred = pipeline.predict(X_test)  # same transform applied automatically

Class Imbalance

When one class vastly outnumbers another (e.g., fraud: 0.1% positive rate), vanilla training learns to always predict majority class.

TechniqueDescription
Class weightsWeight minority class more in loss
Oversampling (SMOTE)Synthesize minority-class examples
UndersamplingRemove majority examples
Threshold tuningAdjust decision threshold post-training
Specialized metricsUse F1, AUC-ROC, precision-recall instead of accuracy
from sklearn.utils.class_weight import compute_class_weight

weights = compute_class_weight("balanced", classes=np.unique(y), y=y)
class_weight_dict = dict(zip(np.unique(y), weights))

# Pass to model
from sklearn.linear_model import LogisticRegression
model = LogisticRegression(class_weight=class_weight_dict)