AI & Machine Learning Cheatsheet
Data Preprocessing and Normalization
Use this AI & Machine Learning reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
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
| Strategy | When to Use | Risk |
|---|---|---|
| Drop rows with any NaN | Very few missing (<1%), large dataset | Loses data |
| Drop columns with >N% missing | Column mostly empty (>70% missing) | Lose feature |
| Mean/median imputation | Numeric, MAR (missing at random) | Distorts variance |
| Mode imputation | Categorical | May over-represent majority |
| Model-based imputation | Important feature, complex missingness | Computational |
| Add indicator column | Missingness is informative | Extra feature |
| Forward-fill / back-fill | Time series, ordered data | Assumes 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.
| Method | Cardinality | Ordering | Model type |
|---|---|---|---|
| One-hot | Low (<20) | None | Any |
| Ordinal | Any | Yes | Tree or linear |
| Target | High | None | Tree (with OOF) |
| Embedding | Very high | None | Neural networks |
| Hashing | Very high | None | Any |
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.
| Method | Type | How |
|---|---|---|
| Variance threshold | Filter | Remove near-zero-variance features |
| Correlation filter | Filter | Remove highly correlated pairs |
| Univariate tests | Filter | F-test, mutual information vs. target |
| L1 regularization | Embedded | Lasso drives irrelevant weights to 0 |
| Feature importance | Embedded | Tree-based importance, permutation importance |
| Recursive Feature Elimination | Wrapper | Iteratively remove weakest feature |
| PCA | Projection | Not 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.
| Technique | Description |
|---|---|
| Class weights | Weight minority class more in loss |
| Oversampling (SMOTE) | Synthesize minority-class examples |
| Undersampling | Remove majority examples |
| Threshold tuning | Adjust decision threshold post-training |
| Specialized metrics | Use 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)