A scikit-learn pipeline keeps preprocessing and modeling inside one estimator object. That matters when training code scales, selects, encodes, or imputes features before fitting, because the same ordered transformations must run again before scoring or prediction.
Pipeline stores named steps in order. Every step before the last one must transform the data, while the final estimator supplies methods such as predict(), predict_proba(), or score() when it supports them.
A small Iris run chains StandardScaler, SelectKBest, and LogisticRegression. It fits only the training split, prints the fitted step names and selected features, and confirms that held-out rows reach the classifier through the pipeline.
from sklearn import __version__ as sklearn_version from sklearn.datasets import load_iris from sklearn.feature_selection import SelectKBest, f_classif from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score from sklearn.model_selection import train_test_split from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler iris = load_iris() X_train, X_test, y_train, y_test = train_test_split( iris.data, iris.target, test_size=0.25, stratify=iris.target, random_state=42, ) model = Pipeline( steps=[ ("scale", StandardScaler()), ("select", SelectKBest(score_func=f_classif, k=2)), ("classify", LogisticRegression(C=0.8, max_iter=300)), ] ) model.fit(X_train, y_train) predictions = model.predict(X_test) accuracy = accuracy_score(y_test, predictions) selected_features = model[:-1].get_feature_names_out(iris.feature_names) sample_prediction = model.predict(X_test[[0]])[0] sample_probability = model.predict_proba(X_test[[0]])[0] probability_map = { str(iris.target_names[index]): round(float(probability), 3) for index, probability in enumerate(sample_probability) } print(f"scikit-learn {sklearn_version}") print(f"pipeline steps: {list(model.named_steps)}") print(f"selected features: {selected_features.tolist()}") print(f"classifier C: {model.named_steps['classify'].C}") print(f"held-out accuracy: {accuracy:.3f}") print(f"first prediction: {iris.target_names[sample_prediction]}") print(f"first probabilities: {probability_map}")
The step names become keys in named_steps. Nested estimator parameters use the same names with double underscores, such as classify__C during tuning.
Related: How to run grid search with scikit-learn
$ python create_pipeline.py
scikit-learn 1.9.0
pipeline steps: ['scale', 'select', 'classify']
selected features: ['petal length (cm)', 'petal width (cm)']
classifier C: 0.8
held-out accuracy: 0.921
first prediction: setosa
first probabilities: {'setosa': 0.962, 'versicolor': 0.038, 'virginica': 0.0}
scale transforms the raw feature values, select keeps the two strongest features, and classify receives the transformed rows for fitting and prediction.
model[:-1] slices off the classifier and asks the preprocessing portion for output feature names. The two petal features are the columns passed into LogisticRegression.
The held-out accuracy and probability map come from model.predict() and model.predict_proba(), so the scaler and selector were applied before the classifier made the decision.
$ rm create_pipeline.py