machine learning
sklearn
custom transformers
FunctionTransformer
TransformerMixin

Sklearn custom transformers difference between using FunctionTransformer and subclassing TransformerMixin

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

In Scikit-learn, FunctionTransformer and a custom class based on TransformerMixin solve similar problems at different levels of complexity. FunctionTransformer is ideal for small stateless transformations, while subclassing BaseEstimator and TransformerMixin is the better choice when the transformer needs learned state, custom parameters, validation logic, or richer integration with pipelines and model selection.

Use FunctionTransformer for Simple Stateless Logic

If the transformation is just “apply this function to X” and there is nothing to learn during fit, FunctionTransformer is usually enough:

python
1import numpy as np
2from sklearn.preprocessing import FunctionTransformer
3
4
5def log1p_transform(X):
6    return np.log1p(X)
7
8
9transformer = FunctionTransformer(log1p_transform)
10
11X = np.array([[0.0, 1.0], [2.0, 3.0]])
12print(transformer.fit_transform(X))

This is concise and pipeline-friendly. It works well for transformations such as:

  • log scaling
  • clipping
  • columnwise arithmetic
  • simple deterministic reshaping

The main limitation is that there is no learned internal state unless you build that logic elsewhere.

Subclass When the Transformer Needs fit

If the transformer must learn something from the training data, a custom class is the right tool. For example, a transformer that stores column medians during fit and uses them during transform:

python
1import numpy as np
2from sklearn.base import BaseEstimator, TransformerMixin
3
4
5class MedianCenterer(BaseEstimator, TransformerMixin):
6    def fit(self, X, y=None):
7        X = np.asarray(X, dtype=float)
8        self.medians_ = np.median(X, axis=0)
9        return self
10
11    def transform(self, X):
12        X = np.asarray(X, dtype=float)
13        return X - self.medians_
14
15
16X = np.array([[1.0, 10.0], [2.0, 20.0], [100.0, 30.0]])
17transformer = MedianCenterer()
18print(transformer.fit_transform(X))

This is not a good fit for FunctionTransformer, because the behavior depends on learned statistics from the dataset.

Why BaseEstimator and TransformerMixin Matter

The typical Scikit-learn custom transformer inherits from both BaseEstimator and TransformerMixin:

  • 'BaseEstimator gives you parameter handling that works with get_params, set_params, cloning, and grid search'
  • 'TransformerMixin gives you a default fit_transform'

That makes the custom transformer behave like a normal Scikit-learn component inside Pipeline, ColumnTransformer, and parameter search tools.

Parameterization Is Easier with a Class

Once the transformer has meaningful configuration, a class becomes easier to manage. For example:

python
1import numpy as np
2from sklearn.base import BaseEstimator, TransformerMixin
3
4
5class Clipper(BaseEstimator, TransformerMixin):
6    def __init__(self, lower=0.0, upper=1.0):
7        self.lower = lower
8        self.upper = upper
9
10    def fit(self, X, y=None):
11        return self
12
13    def transform(self, X):
14        X = np.asarray(X, dtype=float)
15        return np.clip(X, self.lower, self.upper)

This integrates naturally with grid search because the parameters are explicit and discoverable by Scikit-learn.

Feature Names and Validation

Custom classes also make it easier to add input validation, preserve metadata, or expose feature-name behavior. If your transformer interacts with DataFrames, selected columns, or feature engineering that changes column count, a dedicated class often becomes much easier to maintain than a wrapped free function.

That is especially true once the transformer is more than one or two lines long. FunctionTransformer stays elegant for tiny logic; beyond that, it can become opaque.

Common Pitfalls

The most common mistake is using FunctionTransformer for a transformation that really needs learned state from fit. That often leads to awkward global variables or duplicated logic.

Another pitfall is writing a custom transformer class but forgetting BaseEstimator, which makes parameter inspection and cloning less reliable inside Scikit-learn utilities.

It is also easy to underestimate how quickly a “simple function” grows into real transformer logic with parameters, validation, and metadata concerns. At that point, forcing it to stay a plain function usually hurts readability.

Finally, do not choose the class approach only because it looks more official. If the transformation is a small stateless function, FunctionTransformer is often the cleanest answer.

Summary

  • Use FunctionTransformer for small stateless transformations.
  • Subclass BaseEstimator and TransformerMixin when the transformer needs learned state or richer behavior.
  • Custom classes integrate more naturally with parameter search and complex pipeline logic.
  • 'FunctionTransformer is concise, but it becomes awkward once the logic grows.'
  • The right choice depends less on style and more on whether fit actually needs to learn something.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design