Evaluating Machine Learning Models on Test Data
Test data is never used to decide which features to keep.
Why Let an Algorithm Choose the Columns
When you build a machine learning model, you must decide which columns of your data to feed into the algorithm. Sometimes the right choice is obvious. Often it is not, and you are unsure which inputs carry useful signal. Feature selection answers a practical question: can an automatic method pick the useful columns for you? Recursive Feature Elimination, usually called RFE, is one such method. It works by recursively eliminating features until a specified number is reached.
In this article you will first look at the behavior of a short RFE program, then work out from that behavior how RFE makes its choices. Keep one question in mind as you read: if RFE keeps retraining a model while it removes features, what does that tell you about its cost? The answer comes back at the end of the article when we compare RFE with other selection approaches.
Watching the Elimination Loop
RFE is wrapped around an estimator, which is the model that RFE trains again and again while it decides what to remove. The loop is simple to state. Fit the estimator on the current set of features. Use the fitted model to decide which feature to drop. Remove it. Repeat until only the number you asked for is left. The argument n_features_to_select tells RFE where to stop.
The diagram shows the shape of the process. Each pass through the loop removes one feature and leaves a smaller feature set for the next pass. Notice that the loop does not stop after a single decision. It keeps going until the target count is reached, so the number of rounds is set by how many features you drop, not by how good any one feature looks.
Tracing RFE from six features down to three
A dataset has six feature columns. You ask RFE to select three. Trace what happens.
- 1Start
RFE begins with all six features. The estimator is trained on this full set.
- 2First elimination
The fitted estimator is used to decide which feature to remove. One feature is dropped, leaving five. The estimator is trained again on the five remaining features.
- 3Second elimination
Another feature is dropped, leaving four. The estimator is trained again on those four.
- 4Third elimination
A third feature is dropped, leaving three. Three drops means three rounds of retraining, which is exactly the cost the source describes.
- 5Stop
The target of three has been reached, so the loop ends and the three surviving features form the selected set.
Three features are selected. The estimator was retrained once for each feature dropped.
Reading the Selected-Feature Mask
After fit has run, the selector stores its result. The method get_support returns a mask with one entry per original feature. Each entry is True if that feature was kept and False if it was eliminated. The mask is aligned by position, so position 0 in the mask describes column 0 of your original data, position 1 describes column 1, and so on. Mapping the mask back to column names is how you learn which features survived.
A 20-item array of True and False values. The sum of the mask is 5, and the selected list contains five names such as feature_0 or feature_3, depending on the data.- RandomForestClassifier with 100 trees and
random_state42 is the estimator RFE retrains while it removes features. n_features_to_select=5 tells RFE where to stop.get_supportreturns one True or False entry per original column.- zip pairs each column name with its mask entry so the surviving names can be read directly.
The code above is an illustrative program written for this article. It uses a synthetic dataset, so the exact names that survive depend on the data, but the structure of the result is fixed. The mask will contain exactly as many True entries as you requested, and each True position corresponds to one surviving column.
In this illustrative five-column case, the mask reads True, False, True, True, False. Reading it position by position gives age, tenure, and clicks as the survivors, while income and region were eliminated. The mask is only a record of the decision. It does not explain why each feature was kept, so the reasoning lives in the elimination loop you traced earlier.
Fitting on Training Data and Transforming Test Data
Once RFE has fit, the selector can shrink your data to the chosen features. The transform method reduces a dataset to the selected columns, in the correct order. The important rule is that the selector is fitted only on training data. Test data is never used to decide which features to keep. Instead, test data is passed through transform so it has the same columns the model was trained on.
Two test accuracy values, one from the logistic regression on the reduced features and one from the random forest inside RFE on the selected features.- transform is applied to the training data and the test data, but fit is never called on the test data.
- LogisticRegression must be imported from sklearn.linear_model before use.
- model.score on the reduced test set reports test accuracy for the logistic regression.
- selector.score scores the random forest inside RFE using only the selected features.
The source follows this same logic. It transforms the training data, fits a logistic regression on the reduced training set, and scores that model on the reduced test set. It also scores the random forest inside RFE directly, which uses only the selected feature set. In the source's example, the random forest and a logistic regression on the selected features scored the same, which shows that a linear model can match the forest once the right features are chosen.
What RFE Costs Compared with Other Approaches
RFE is usually a better-performing choice than simpler selection methods in the source's experiment, but its power comes at a price. Because a random forest is trained once for each feature that is dropped, the total work grows with the number of eliminations. The source describes RFE as computationally expensive for this reason, and notes that it took significantly longer to run than the model-based selection it was compared with.
| Approach | Training effort | Result reported in the source |
|---|---|---|
| RFE with a random forest | Retrains the estimator for each dropped feature | Selected features that performed better than the other two approaches, but missed one feature |
| Model-based selection | Trains one model | Faster, but its selected features scored lower than RFE's |
| Univariate selection | Not retrained in the same loop | Selected features that scored lower than RFE's |
Trade-offs described in the source's comparison
The source also notes that RFE does not guarantee a perfect selection. In its experiment, one feature was still missed by RFE, so even its best result is not guaranteed to be ideal. RFE is therefore worth its cost when the performance gain matters, your dataset is manageable to retrain repeatedly, and you are genuinely unsure which inputs to keep. When speed matters more, or when a quick first pass is enough, a single-pass or model-based method may be the better trade.
Mistakes When Applying RFE
Common mistake Fitting RFE on the full dataset before splitting
What it looks likeselector.fit(X, y) is called on all rows, and only then is the data split into train and test.
Why it failsThe test set has influenced which features were kept, so the test score is no longer an honest estimate for unseen data.
Do this insteadSplit first, then call selector.fit(X_train, y_train) using only training rows.
Common mistake Calling fit on the test data
What it looks likeselector.fit(X_test, y_test) is run to get the test set in the right shape.
Why it failsFitting learns from the data it receives. Test data should only be transformed, never used to make decisions.
Do this insteadCall selector.transform(X_test) after fitting on training data.
Common mistake Reading the mask as column names
What it looks likeprint(selector.get_support()) is treated as a list of kept feature names.Why it failsThe mask holds True or False values aligned by position, not the names of the columns.
Do this insteadPair the mask with the original column names, for example with zip, to find which features survived.
Common mistake Forgetting that the mask is aligned with the original columns
What it looks likeThe mask is applied to a dataset whose columns were reordered after fitting.
Why it failsEach position in the mask refers to a specific original column, so reordering breaks the mapping.
Do this insteadKeep the column order identical between fitting and reading the mask.
Common mistake Choosing RFE without considering its cost
What it looks likeRFE is run on a very large dataset with a slow estimator for a quick first experiment.
Why it failsRFE retrains its estimator for each dropped feature, so runtime grows with the number of eliminations.
Do this insteadEstimate the runtime first, and compare against a faster approach if speed matters more than the expected gain.
Practice: Trace the Selection
Suppose RFE is configured with n_features_to_select=3 on a dataset with eight columns, and the mask returned by get_support has True at positions 1, 4, and 6. Before reading the answer, predict: how many times was the estimator retrained, and which column names survive if the columns are named col0 through col7? Explain your reasoning using the elimination loop.
Hints
- Count how many features must be dropped to go from eight to three.
- Each dropped feature means one retraining of the estimator.
- Mask positions are zero-indexed, so position 1 is col1.
Write the three lines of code that fit an RFE selector on training data only, transform both the training and test sets, and print the test score of a logistic regression trained on the reduced training set.
Hints
- Start with
selector.fit(X_train, y_train). - Use selector.transform on both X_train and X_test.
- Remember LogisticRegression must be imported from sklearn.linear_model.
Key Takeaways
- 1RFE repeatedly drops features, retraining its estimator after each drop, until the number you specified with
n_features_to_selectremains. - 2After fit,
get_supportreturns a True or False mask with one entry per original column, aligned by position. - 3Fit the selector on training data only, then use transform on both training and test data so they share the same selected columns.
- 4RFE often selects better features than simpler methods, but it is computationally expensive because it trains many models where model-based selection trains one, and it does not guarantee a perfect selection.
At a glance
- Recursive Feature Elimination repeatedly removes features, retraining its estimator at each step, until the specified number of features remains.
- After fitting,
get_supportreturns a mask with one entry per original feature, and mapping it back to column names reveals which features were kept. - Fit the selector on training data only, then apply transform to both training and test data so the model sees the same selected columns.
- RFE can select better features than simpler methods, but its cost grows with the number of eliminations, so it should be weighed against faster approaches.