Concepts / Model-Based Feature Selection

Model-Based Feature Selection

Model-based feature selection uses a selector that is first fitted with fit(X_train, y_train) and then applied with transform(X_train) to produce a reduced feature matrix.

  • Programming

Starting With Too Many Columns

Many real datasets arrive with far more input columns than a model can comfortably use. Some of those columns carry useful information about what you want to predict. Others are noise: values that vary randomly and have no real connection to the target. Feature selection is the family of techniques for deciding which columns to keep before a model is trained on them.

What do you think happens?

If you were told to keep only half of a set of columns, what would you use to decide which half is worth keeping?

  • Pick the first half of the columns in the file

  • Score each column against the target and keep the strongest half

  • Keep the columns with the largest numbers in them

Reveal answer
Answer:

Score each column against the target and keep the strongest half

The approach this article covers judges each column by its own relationship with the target. Columns that show a weak relationship become candidates for removal. Picking by position or by raw magnitude ignores whether the column is related to what you want to predict.

Scoring Each Feature on Its Own

Definition

Univariate feature selection scores each feature separately against the target using a statistical test, then discards features whose p-values are too high. The word univariate means one variable at a time: every column is tested against the target separately, without asking how it interacts with the other columns. A high p-value means the feature is unlikely to be related to the target, so it becomes a candidate for removal.

Two decisions are needed. First, you choose a statistical test. The source names f_classif as the default for classification and f_regression for regression. Second, you choose a rule for discarding features based on the p-values that test produces. Every discarding method uses a threshold, and the methods differ in how they compute that threshold.

Feature 0tested against y aloneFeature 1tested against y aloneTarget yshared referenceFeature 2tested against y aloneown p-valueown p-valueown p-value
Univariate scoring gives every column its own test against y. The tests do not look at how the columns relate to one another, so each column can receive a different score.

Because each test is independent, two features can end up with very different results. One column may show a clear connection to the target while another, placed right beside it, shows none. The selector only sees each result separately and then applies the threshold to each one.

Keeping a Share of the Columns

The source names two of the simplest discarding methods. SelectKBest selects a fixed number k of features. SelectPercentile selects a fixed percentage of features. The difference lies only in how the size of the kept set is specified. With SelectKBest you say how many columns survive, regardless of how many columns the dataset has. With SelectPercentile you say what share of the columns survives, so the count adjusts automatically as the dataset grows or shrinks.

The percentile argument is a number from 0 to 100 that sets the share of features to keep. The source uses 50, meaning half of the features. Choosing between a fixed count and a fixed percentage is a judgment call. A percentage adapts to the dataset's width, which helps when the same pipeline must handle inputs of different sizes. A fixed count gives direct control over the model's input size, which helps when a downstream constraint sets a maximum number of columns.

Tracing the Fit and Transform Steps

The usage pattern has three steps. You create the selector with a percentile, fit it so that it can score the features against the training targets, and then transform the training data to keep only the selected columns. The code below follows that pattern. The source snippet does not show how the selector was created, so the setup line is assumed here as an illustration.

python
Notes on the code
  • SelectPercentile(percentile=50) keeps half of the input features
  • fit receives both the training inputs and the training targets
  • transform returns a new array and leaves X_train unchanged
  • The two print calls report the shape of each array, rows first then columns

The method select.fit(X_train, y_train) is the step where the selector examines the training data. It receives the feature matrix X_train and the target values y_train. The fit call changes the internal state of the select object, but it does not return a new dataset. The method select.transform(X_train) then applies the decision. It returns a new array containing only the kept features, which the source stores in a new variable called X_train_l1.

X_train80 feature columnsselect.fitlearns which features tokeepfitted selectorholds the keep decisionselect.transformapplies the decisionX_train_l140 feature columnsy_traintraining targetsinputstargetschanges statemust be fitted firstsame matrixreturns new array
The training matrix enters the process in two places. Fit uses X_train and y_train to learn which features to keep. Transform then uses X_train to produce the reduced array.

The order matters. The selector must be fitted before transform is called, because transform relies on the decision made during fit. Calling transform on an unfitted selector has no decision to apply, so the fit step always comes first.

Reading the Shape Before and After

Shape is the most useful first check in this exercise because it reports two facts at once: how many samples remain and how many features remain. A shape is written as a pair of numbers, rows first and then columns. Comparing the shapes printed before and after transform tells you what the transform step did.

284 samplesrows in X_train284 samplesrows in X_train_l180 featurescolumns in X_train40 featurescolumns in X_train_l1unchangedreduced
Transform removes columns but keeps every row. The number of training samples, 284, is the same before and after, while the number of features falls from 80 to 40.

In the source exercise, the feature count drops from 80 to 40 while the 284 samples remain unchanged. The shape printout is the main evidence that the selection worked. If the row count changed, something other than feature selection has gone wrong, because this step is meant to remove columns only.

Reading the Mask Back to Column Positions

Transforming the data tells you how many features survived, but not which ones. The get_support method answers the second question. It returns a Boolean mask, an array with one True or False value per original feature. True marks a feature that was kept and False marks a feature that was removed. The position of each entry in the mask matches the position of that feature in the original input, so the mask is a direct map from original column positions to keep-or-drop decisions.

Worked example

Decoding a six-feature mask

Suppose a selector was applied to six features and get_support returns the mask True, False, True, True, False, False. Which features were kept and which were dropped?

  1. 1
    Pair each entry with its position

    Position 0 is True, position 1 is False, position 2 is True, position 3 is True, position 4 is False, and position 5 is False.

  2. 2
    Collect the True positions

    True entries sit at positions 0, 2 and 3, so those original features were kept.

  3. 3
    Collect the False positions

    False entries sit at positions 1, 4 and 5, so those original features were dropped.

  4. 4
    Check the count

    Three of six features survived, which matches the share a percentile of 50 would keep.

Result

Features at positions 0, 2 and 3 were kept. Features at positions 1, 4 and 5 were dropped.

Position 0True, keptPosition 1False, droppedPosition 2True, keptPosition 3True, keptPosition 4False, droppedPosition 5False, dropped
Each position in the original six-feature input has one entry in the mask. True marks a kept column and False marks a dropped column.

Reading the mask this way lets you check the selector's decisions against the original column order. If you know that a column at position 2 is important, you can look up its entry directly instead of guessing from the transformed array, which has lost the original positions.

Displaying the Mask With matshow

After the reduced array has been checked, the source looks at which features were selected. The mask is reshaped with mask.reshape(1, -1) before it is displayed. This turns the one-dimensional mask into a single row with one column per original feature. The matshow function then draws that row as a strip of cells. The cmap='gray_r' argument chooses a reversed grayscale palette in which True cells are black and False cells are white.

python
Notes on the code
  • get_support returns one Boolean per original feature
  • reshape(1, -1) makes the mask a single row of cells
  • cmap='gray_r' draws True as black and False as white

Because the mask is drawn as one row, the horizontal positions in the plot correspond to original feature positions. The source's tick marks run from 0 to 70 along the horizontal axis, which covers the range of feature positions across the strip. Read the strip from left to right: each black cell is a kept feature and each white cell is a removed feature.

Does Removing Features Help a Model

Removing features only matters if the model does as well or better afterward. The source checks this by training logistic regression twice on the same split: once on all features and once on only the selected features, then scoring both on the test set. In the source's synthetic example, the reduced feature set scored slightly higher. Removing the noise columns improved performance, even though some genuine features were lost along the way.

The source's dataset was a breast cancer dataset with 30 real features, to which 50 columns of random noise were appended, giving 80 columns. The selection was imperfect: some real features were dropped and some noise columns were kept. The recovery was not perfect, yet the reduced set still scored higher.

Univariate selection is still useful in two situations the source names: when there are so many features that building a model on all of them is infeasible, and when you suspect that many features are completely uninformative.

Mistakes That Hide the Real Result

  • Common mistake

    Fitting the selector on the test data

    Why it fails

    The selector should learn which features to keep from training data only. Fitting it on test data lets the test set influence the selection.

    Do this instead

    Call fit with X_train and y_train, then call transform on both the training and test data.

  • Common mistake

    Trusting the transformed array to show which features survived

    Why it fails

    The transformed array shows how many columns remain but not which original positions they came from.

    Do this instead

    Use get_support to read the Boolean mask and map each True or False entry back to its feature position.

  • Common mistake

    Calling transform before fit

    Why it fails

    Transform applies the decision made during fit, so without a fitted selector there is no decision to apply.

    Do this instead

    Always call fit before transform.

  • Common mistake

    Assuming a better score proves selection helps

    Why it fails

    The source's example was simple and synthetic, and outcomes on real data are usually mixed.

    Do this instead

    Compare the model on all features against the model on selected features, and treat the result as evidence for one dataset only.

Locating a Failure in a Selection Script

When a selection script misbehaves, locate the failure by comparing each step's output with its expected output, starting from the fit call and moving forward. The shape printouts act as checkpoints. If X_train_l1 has the wrong number of columns, the problem lies in the fit step, the transform step, or the data passed to them. Work through the chain in order rather than guessing.

MEDIUM

A selector with percentile 50 is fitted on a training set with 284 samples and 80 features. After transform, the printed shape of X_train_l1 is (284, 80). Which step should you inspect first, and why?

Hints
  • Compare the expected feature count after a 50 percent selection with the printed count
  • The number of samples should be unchanged, so focus on the columns

Key Takeaways

  • Univariate feature selection judges each feature by its own relationship with the target, using a statistical test whose p-value decides whether the feature is a candidate for removal.
  • SelectPercentile keeps a fixed share of the features, so the kept count scales with the dataset, while SelectKBest keeps a fixed count.
  • A selector is fitted on training data with fit(X_train, y_train) and then applied with transform, and the Boolean mask from get_support maps every kept or dropped decision back to an original feature position.
  • Shape checks show what changed and what stayed the same: in the source exercise, features fell from 80 to 40 while 284 samples remained.
  • Whether selection helps must be checked by comparing model performance on all features against the selected features, because results on real data are usually mixed.

Related concepts

More concepts starting with M