Skip to content

fix(binning): make get_feature_names_out follow the sklearn contract #36

Description

@ChrisW09

Summary

CustomBinTransformer.get_feature_names_out() raises when called with no arguments and
returns a plain list rather than an ndarray. Both break the scikit-learn contract, and the
first makes Pipeline.get_feature_names_out() fail for any pipeline containing the
transformer.

The package already fixed exactly this for three sibling transformers — see the docstring of
tests/test_feature_names_out.py ("get_feature_names_out(None) defaults to generated
x0, x1, ... names instead of raising, for NoTransformer, ToFloatTransformer and
ContinuousOrdinalTransformer") — but CustomBinTransformer was left out.

Reproduction

import numpy as np
from sklearn.pipeline import Pipeline
from pretab.transformers import CustomBinTransformer, NoTransformer

X = np.linspace(0, 1, 20).reshape(-1, 1)
t = CustomBinTransformer(output_dim=4).fit(X)

t.get_feature_names_out()
InvalidParamError: input_features must be specified
print(type(t.get_feature_names_out(["f"])).__name__)                     # list
print(type(NoTransformer().fit(X).get_feature_names_out()).__name__)     # ndarray

Pipeline([("bin", CustomBinTransformer(output_dim=4))]).fit(X).get_feature_names_out()
InvalidParamError: input_features must be specified

Expected

Consistent with every other transformer in the package and with scikit-learn:
get_feature_names_out() works with no arguments, generating x0, x1, ..., and always
returns an ndarray of strings.

Actual

Raises without arguments; returns a list with them.

Root cause

pretab/transformers/binning/binning.py:142-157:

def get_feature_names_out(self, input_features=None):
    if input_features is None:
        raise InvalidParamError("input_features must be specified")
    return input_features

It also does not guard on being fitted, unlike the siblings which call check_is_fitted.

Suggested fix

Mirror NoTransformer.get_feature_names_out:

def get_feature_names_out(self, input_features=None):
    check_is_fitted(self, "n_features_in_")
    if input_features is None:
        input_features = [f"x{i}" for i in range(self.n_features_in_)]
    return np.asarray(input_features, dtype=object)

Note this changes two existing tests that pin the current behaviour:
test_custom_bin_transformer_feature_names_out_raises (asserts the no-argument call raises)
and test_custom_bin_transformer_feature_names_out (asserts a list is returned, via
names == ["feature1"]). Both encode the defect rather than a desired guarantee.

Environment

  • pretab 0.1.0 (main @ 51c3043)
  • Python 3.11.15, numpy 2.4.6, pandas 2.3.3, scikit-learn 1.9.0, scipy 1.17.1
  • macOS (darwin 25.5.0)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions