Select features inside the Pipeline, never before it
Selecting on the full dataset is leakage. Inside a Pipeline the selection is refit on each training fold, which is correct.
from sklearn.feature_selection import SelectKBest, chi2, SelectFromModel
from sklearn.pipeline import Pipeline
from sklearn.linear_model import LogisticRegression
# Univariate: score each feature against the target independently.
# chi2 needs non-negative input, so it suits TF-IDF.
pipe = Pipeline([
("tfidf", TfidfVectorizer(ngram_range=(1, 2))),
("select", SelectKBest(chi2, k=5000)), # 200k features -> 5k
("clf", LogisticRegression(max_iter=1000)),
])
# Model-based: keep what a fitted model considered important.
# L1 regularisation drives coefficients to exactly zero, so it selects.
pipe = Pipeline([
("select", SelectFromModel(
LogisticRegression(penalty="l1", solver="liblinear", C=0.1),
threshold="median")),
("clf", LogisticRegression(max_iter=1000)),
])
pipe.fit(X_train, y_train)
mask = pipe.named_steps["select"].get_support()
print(f"kept {mask.sum()} of {len(mask)} features")