scikit-learn's entire API is built around one consistent pattern. Whether you're using a linear regression, a random forest, a scaler, or a text vectorizer — they all follow the same interface. This is called the Estimator API.
There are three types of objects you need to know:
| Type | What it does | Key methods | Examples |
|---|---|---|---|
| Estimator | Learns from data — fits a model to training examples | fit(X, y) |
LinearRegression, RandomForest, KMeans |
| Transformer | Changes data — scales, encodes, reduces dimensions | fit(X) then transform(X) |
StandardScaler, OneHotEncoder, PCA |
| Pipeline | Chains transformers + an estimator into one object | fit(X, y) + predict(X) |
Pipeline([scaler, model]) |
An estimator is any object with a fit() method. During fit(), it looks at your training data and learns something — the weights of a regression, the split rules of a tree, the cluster centers of K-Means.
fit()). After the lecture, the student can answer exam questions (predict()). The student doesn't re-read the notes every time they answer — the knowledge is stored internally, just like a fitted estimator stores its learned parameters.
from sklearn.linear_model import LinearRegression import numpy as np # Training data X_train = np.array([[1], [2], [3], [4]]) y_train = np.array([2, 4, 6, 8]) # Step 1: create the estimator (nothing learned yet) model = LinearRegression() # Step 2: fit() — the model learns from training data model.fit(X_train, y_train) # After fitting, parameters are stored on the object print(model.coef_) # [2.] — the slope it learned print(model.intercept_) # 0.0 — the intercept it learned # Step 3: predict() — use learned parameters on new data X_new = np.array([[5], [10]]) print(model.predict(X_new)) # [10. 20.]
predict() before fit() raises a NotFittedError. The model has nothing to predict with yet. Always fit first.
A transformer has two methods: fit() and transform(). The fit() step learns statistics from the training data. The transform() step applies those statistics to any data — train or test.
fit(X_train) — learn from training datatransform(X) — apply to any datafit_transform(X_train) — shortcut for trainingfrom sklearn.preprocessing import StandardScaler import numpy as np X_train = np.array([[10, 200], [20, 400], [30, 600]]) X_test = np.array([[15, 300], [25, 500]]) scaler = StandardScaler() # Fit on TRAIN only — learns mean=[20,400] std=[8.16,163.3] scaler.fit(X_train) # Transform TRAIN using learned stats X_train_scaled = scaler.transform(X_train) # Transform TEST using the same learned stats (NOT re-fitted) X_test_scaled = scaler.transform(X_test) print(scaler.mean_) # [20. 400.] — stored from fit() print(scaler.scale_) # [8.16 163.3] — stored from fit()
scaler.fit(X_test) or scaler.fit_transform(X_test) makes the scaler learn statistics from test data. This means your model indirectly "sees" the test set during training — it's data leakage. Always fit on train, transform on both.
A Pipeline chains one or more transformers followed by a final estimator. When you call pipeline.fit(X, y), it:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.linear_model import LogisticRegression from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split X, y = load_iris(return_X_y=True) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) # Build the pipeline pipe = Pipeline([ ('scaler', StandardScaler()), ('model', LogisticRegression(max_iter=200)) ]) # fit() triggers: scaler.fit_transform(X_train) → model.fit() pipe.fit(X_train, y_train) # predict() triggers: scaler.transform(X_test) → model.predict() print(pipe.predict(X_test)) # Score the whole pipeline in one call print(pipe.score(X_test, y_test)) # 0.9667 # Access individual steps print(pipe.named_steps['scaler'].mean_) print(pipe.named_steps['model'].coef_.shape)
You could do it manually — fit the scaler, transform the data, fit the model. But pipelines give you four things manually chaining doesn't:
| Method | What it does | When to call | On what data |
|---|---|---|---|
fit(X, y) |
Learn parameters from data. Returns self. | Once, during training | Training set only |
transform(X) |
Apply learned transformation to data | After fit(). Call on train AND test. | Any data, using train stats |
fit_transform(X) |
fit() then transform() in one step | Shortcut for training data only | Training set only |
predict(X) |
Generate predictions from fitted model | After fit(). Inference time. | Any data |
score(X, y) |
Evaluate model performance | After fit(). Evaluation. | Test or validation set |
model.coef_, scaler.mean_, pca.components_. Parameters you set before fitting don't have trailing underscores: StandardScaler(with_mean=True). This is how you know what's learned vs configured.
Here is a complete, real-world-style workflow using Pipeline with proper train/test splitting, cross-validation, and scoring:
from sklearn.pipeline import Pipeline from sklearn.preprocessing import StandardScaler from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split, cross_val_score from sklearn.datasets import load_breast_cancer import numpy as np # 1. Load data X, y = load_breast_cancer(return_X_y=True) # 2. Split — hold out test set until the very end X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42, stratify=y ) # 3. Build pipeline pipe = Pipeline([ ('scaler', StandardScaler()), ('clf', RandomForestClassifier(n_estimators=100, random_state=42)) ]) # 4. Cross-validation on training set # Pipeline prevents leakage: scaler is re-fit inside each fold cv_scores = cross_val_score(pipe, X_train, y_train, cv=5, scoring='accuracy') print(f"CV accuracy: {cv_scores.mean():.3f} ± {cv_scores.std():.3f}") # CV accuracy: 0.964 ± 0.012 # 5. Final fit on all training data pipe.fit(X_train, y_train) # 6. Evaluate on held-out test set (only touch this ONCE) test_score = pipe.score(X_test, y_test) print(f"Test accuracy: {test_score:.3f}") # Test accuracy: 0.965
scaler.fit_transform(X_test) instead of scaler.transform(X_test). What problem does this cause?fit_transform calls fit() first, which overwrites the statistics learned from training data with test-set statistics. Now your model indirectly knows the test distribution — this is data leakage. The test score will be optimistically biased.coef_, mean_, components_). Configuration you set before fitting does not (n_estimators, with_mean). This is how you distinguish what you configured vs what the model discovered.pipe.fit(X_train, y_train) on a Pipeline with two steps, what happens internally?fit(X, y) and predict with predict(X)coef_, mean_, scale_fit on training data only. transform on anything. Never fit on test.