100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Data Analysis & Feature Engineering
55 minintermediate

Practice — Build a Feature Selection Pipeline

What You'll Build

In this exercise you will build a complete, principled feature-selection pipeline that chains filter, wrapper, and embedded methods in the correct order, evaluating each stage's contribution and producing a lean, predictive feature set for a player-performance prediction task. You will implement variance thresholding, correlation filtering, RFECV, and Lasso-based embedded selection, comparing their outputs and combining them into a two-stage filter-then-wrapper pipeline inside a cross-validation loop that prevents feature-selection leakage throughout. This consolidates the entire feature-selection module into one tool that mirrors professional practice — not any single method but a principled chain of complementary methods that together deliver the right features efficiently and honestly.

Analogy🏏Cricket
🏏 Think of it like cricket: Building this pipeline is like a head analyst codifying the entire pre-match preparation routine into a single reusable playbook — pitch inspection, opposition profiling, matchup analysis, and threat assessment — that can be run before any match against any opponent. Just as the playbook turns scattered preparation habits into one repeatable, complete routine, your EDA pipeline turns scattered exploration steps into one repeatable engine. Just as a good playbook ensures no aspect of preparation is forgotten before any match, your pipeline ensures no aspect of exploration is skipped on any dataset. The insight is that codifying the full exploration workflow into a reusable tool is what makes thorough EDA fast, consistent, and complete every time.

Prerequisites

  • Python 3.10 or later with NumPy, pandas, and scikit-learn installed.
  • Mastery of filter methods from Lesson 19 and wrapper methods from Lesson 20.
  • Understanding of embedded methods (Lasso and tree importance) from Lesson 21.
  • Familiarity with PCA from Lesson 22 and with pipeline-based leakage prevention throughout.
  • Comfort with scikit-learn pipelines, GridSearchCV, and cross-validation.

Setup & Project Structure

You will create a project with a selection-pipeline module and a script that applies the full pipeline to a player-performance dataset. Separating the reusable pipeline from the data-specific script lets you apply the same selection to any future high-dimensional dataset. All selectors will be placed inside pipelines to prevent feature-selection leakage during cross-validation, the non-negotiable discipline that makes the evaluation honest.

Analogy🏏Cricket
🏏 Think of it like cricket: a smart side doesn't build a brand-new practice routine for every opponent — it develops a reusable set of net drills and fitness protocols that can be pointed at any upcoming team, then layers opponent-specific prep on top. That is exactly why you separate a reusable EDA engine module, holding your general analysis functions, from the dataset-specific script that aims them at the housing data: build the engine once and you can explore any future dataset with it, just as a well-designed net session works against any tour. Just as a coach fixes the bowling-machine settings and pitch so today's session can be repeated identically tomorrow, you install dependencies into an isolated virtual environment and seed every source of randomness — including the outlier detection — so the whole analysis is reproducible. Just as a structured training ground keeps drills organised and repeatable, a clean project structure keeps your engine and script cleanly divided. The payoff: a disciplined setup you can reuse and trust match after match.
python
# Create the project
mkdir cricket_feature_selection && cd cricket_feature_selection
python -m venv venv
source venv/bin/activate          # Windows: venv\Scripts\activate
pip install numpy pandas scikit-learn

# Project files
touch selection_pipeline.py run_selection.py

# Verify
python -c "import numpy, pandas, sklearn; print('Feature selection stack ready')"

Step 1 — Foundation

Step 1 builds the foundation: the fast, model-free filter stage that removes the obviously irrelevant before any expensive model fitting. You will implement a variance threshold combined with an ANOVA F-score filter, chained together as the first stage of the pipeline. This is the foundation because it is the cheapest selection — requiring no model training — and it narrows the feature space to a manageable size that makes the subsequent wrapper stage computationally feasible.

Analogy🏏Cricket
🏏 Think of it like cricket: Step 1 is like the opening pitch-and-conditions inspection — establishing the basic facts of the playing surface before any tactical planning. Just as the pitch inspection must come first because every tactic depends on the conditions, the structure and quality checks must come first because every analysis depends on understanding them. Just as a misread pitch ruins the game plan, missed type errors or missingness ruin the analysis. The insight is that the foundational structure-and-quality inspection is the bedrock the whole exploration stands on.
python
# selection_pipeline.py
from sklearn.feature_selection import VarianceThreshold, SelectKBest, f_classif
from sklearn.pipeline import Pipeline

def build_filter_stage(k_best=20):
    """Two-step filter: variance threshold (cheap) then F-score (target-aware)."""
    return Pipeline([
        ("vt", VarianceThreshold(threshold=0.01)),      # remove near-constants
        ("kbest", SelectKBest(f_classif, k=k_best)),    # keep top-k by ANOVA F-score
    ])

if __name__ == "__main__":
    import numpy as np
    rng = np.random.default_rng(42)
    X = np.column_stack([np.ones((300,2))*0.01, rng.normal(0,1,(300,28))])
    y = (X[:,2:7].sum(1)>0).astype(int)
    filt = build_filter_stage(k_best=20)
    filt.fit(X, y)
    print(f"After filter stage: {filt.transform(X).shape[1]} features from {X.shape[1]}")

Step 2 — Core Logic

Step 2 builds the core RFECV wrapper stage that follows the filter and precisely identifies the optimal feature subset by cross-validated model performance. This is the analytical core because it uses the model itself to evaluate which features genuinely help prediction, catching the interactions and complementarity that the filter misses, and RFECV automatically determines the right number of features rather than requiring arbitrary specification.

Analogy🏏Cricket
🏏 Think of it like cricket: Step 2 is like profiling every single player in both squads with the metrics appropriate to their role — batting stats for batsmen, bowling figures for bowlers — so each is understood individually before matchups are considered. Just as each player gets the right kind of profile, each variable gets a type-appropriate summary. Just as profiling every player ensures none is overlooked before the matchup analysis, profiling every variable ensures none is skipped before relationship analysis. The insight is that complete, type-appropriate univariate profiling is the analytical core that everything downstream builds on.
python
# selection_pipeline.py (continued)
from sklearn.feature_selection import RFECV
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import StratifiedKFold

def build_wrapper_stage():
    """RFECV: let cross-validation find the optimal feature count automatically."""
    cv = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)
    return RFECV(
        estimator=LogisticRegression(max_iter=500, random_state=42),
        cv=cv,
        scoring="roc_auc",
        min_features_to_select=1,
        step=1,
    )

def build_selection_pipeline(k_best=20):
    """Full leakage-safe selection pipeline: filter -> RFECV -> model."""
    return Pipeline([
        ("filter", build_filter_stage(k_best=k_best)),
        ("rfecv", build_wrapper_stage()),
        ("model", LogisticRegression(max_iter=500, random_state=42)),
    ])

Step 3 — Integration & Enhancement

Step 3 integrates Lasso-based embedded selection as an alternative path and adds a comparison function that evaluates all three approaches, assembling the diagnostic view of which method delivered the best features. You will tune the filter's k parameter via GridSearchCV, compare filter, embedded, and filter-then-RFECV approaches by cross-validated AUC, and report which feature sets each method identified. This integration completes the pipeline by connecting all three selection families and providing the comparative view that guides the final choice.

Analogy🏏Cricket
🏏 Think of it like cricket: Step 3 is like completing the preparation by analysing matchups between players, flagging anomalous performances, and turning the whole survey into a concrete game plan with specific instructions. Just as the matchup analysis and threat flags feed into an actionable plan, the bivariate and outlier analysis feed into the feature-engineering plan. Just as scattered observations are useless without a plan, EDA findings are useless without being made actionable. The insight is that integrating relationships and anomalies and converting everything into a concrete plan is what completes the pipeline and delivers EDA's real value.
python
# selection_pipeline.py (continued)
from sklearn.feature_selection import SelectFromModel
from sklearn.linear_model import LassoCV
from sklearn.model_selection import cross_val_score, GridSearchCV

def compare_methods(X, y):
    """Compare filter, embedded (Lasso), and filter+RFECV pipeline AUC."""
    results = {}

    # FILTER only (tune k via GridSearchCV)
    filter_pipe = Pipeline([
        ("filter", build_filter_stage(k_best=5)),
        ("model", LogisticRegression(max_iter=500)),
    ])
    gscv = GridSearchCV(filter_pipe, {"filter__kbest__k": [5,10,15,20]},
                        cv=5, scoring="roc_auc")
    gscv.fit(X, y)
    results["filter (best k)"] = (gscv.best_score_, gscv.best_params_)

    # EMBEDDED: Lasso selection
    emb_pipe = Pipeline([
        ("select", SelectFromModel(LassoCV(cv=5), threshold=1e-4)),
        ("model", LogisticRegression(max_iter=500)),
    ])
    results["embedded (Lasso)"] = (cross_val_score(emb_pipe,X,y,cv=5,scoring="roc_auc").mean(), {})

    # FILTER + RFECV (the full pipeline)
    full_pipe = build_selection_pipeline(k_best=20)
    results["filter + RFECV"] = (cross_val_score(full_pipe,X,y,cv=5,scoring="roc_auc").mean(), {})

    return results

Step 4 — Testing & Verification

Now you will run the complete comparison on a controlled dataset where you know the ground truth — exactly which features are relevant — and verify that each method recovers the relevant features and that the full filter-then-RFECV pipeline achieves the best AUC. Comparing selected features against the known relevant set verifies the pipeline encodes the module's logic correctly, and the leakage-free evaluation verifies the pipeline is trusted.

Analogy🏏Cricket
🏏 Think of it like cricket: before the real series you play a full dress-rehearsal warm-up match to confirm all your preparation actually holds up under match conditions — and that is exactly what running the complete EDA pipeline on the housing data does. Just as you glance at the scoreboard to check it reads a sensible total and the right number of players are accounted for, you confirm the structure and quality checks report sensible shapes and plausible missingness. Just as you verify each player is listed in their correct role — batsman, bowler, keeper — you check the univariate profiles correctly classify and summarise every variable. Just as a warm-up reveals which opposition threats correlate most with danger, the relationship analysis should surface the features most strongly tied to valuation, and the outlier detection should flag genuinely reasonable freak cases, not nonsense. And just as you review the footage afterward to be sure nothing looked broken, you verify the whole output is coherent and actionable. The payoff: you trust the pipeline before it matters.
python
# run_selection.py
import numpy as np
from selection_pipeline import compare_methods, build_selection_pipeline
from sklearn.model_selection import train_test_split

rng = np.random.default_rng(42)
# Known ground truth: first 5 of 50 features are truly relevant
X = rng.normal(0, 1, (600, 50))
y = (X[:, :5].sum(axis=1) + rng.normal(0, 0.5, 600) > 0).astype(int)

print("=== Method Comparison (AUC, higher is better) ===")
results = compare_methods(X, y)
for method, (auc, params) in results.items():
    print(f"  {method:25}: AUC = {auc:.3f}  {params}")

# Verify the full pipeline selects features close to the known relevant set
pipe = build_selection_pipeline(k_best=20)
Xtr, Xte, ytr, yte = train_test_split(X, y, random_state=42)
pipe.fit(Xtr, ytr)

# Which original features survived the filter stage?
filt_mask = pipe["filter"].get_support()
print(f"\nFilter kept features: {filt_mask.nonzero()[0][:10]} ...")
print("Expected: features 0-4 should be among the survivors.")
print("All selection inside pipeline -> no leakage in the AUC estimates above.")

Warning: The most critical discipline in the entire feature-selection pipeline is that every selector — the variance threshold, the F-score filter, the RFECV, and the Lasso embedded selector — must be placed inside the pipeline so it refits on each training fold during cross-validation. Any selector fit outside the pipeline, on the full dataset or on combined train-plus-test data, leaks information from the test fold into training, inflating every AUC estimate in the comparison. Feature selection is preprocessing, and the leakage-prevention discipline applies to it exactly as it does to scaling and imputation.

Extension Challenge: Extend the pipeline to add a PCA stage after the RFECV selection, reducing the selected features further when the remaining set is still highly correlated, and tune whether to include PCA as a hyperparameter. As a stretch goal, implement permutation importance on the test set after fitting the full pipeline, ranking the features the pipeline selected by their actual contribution to the model's generalisation, and produce a feature-importance report that validates the selection from the evaluation perspective.

  • A principled feature-selection pipeline chains complementary methods: fast filters for breadth, then wrappers or embedded methods for precision.
  • Variance thresholding and F-score filtering remove the obviously irrelevant cheaply, making the expensive wrapper stage computationally feasible.
  • RFECV automatically determines the optimal feature count via cross-validated model performance, avoiding arbitrary specification.
  • Lasso embedded selection provides an efficient alternative path that performs selection and fitting in one model-fitting step.
  • Comparing filter, embedded, and filter-then-wrapper AUC with cross-validation guides the method choice for each dataset.
  • Every selector must be inside the pipeline so it refits on each training fold; selectors fit outside the pipeline leak test information.
  • Tune the filter's k parameter via GridSearchCV inside the pipeline, treating it as a hyperparameter to be optimised.
Lesson 24 of 35
0% complete