Extend non-fitted error check to cover inverse_transform#953
Conversation
The shared estimator check only verified that transform() raises NotFittedError before fit. Every transformer that implements inverse_transform already guards it, but nothing tested that, so a regression would go unnoticed. The check now also calls inverse_transform on an unfitted clone, accepting NotImplementedError for transformers that deliberately do not support inversion. Related to feature-engine#420
|
Note on the CI failures on the first run, for whoever reviews:
|
| # Test when fit is not called prior to inverse_transform. | ||
| transformer = clone(estimator) | ||
| if hasattr(transformer, "inverse_transform"): | ||
| with pytest.raises((NotFittedError, NotImplementedError)): |
There was a problem hiding this comment.
Thanks for the PR @TemidayoA
Failing on both NotFittedError and NotImplementedError is a bit dirty, because we won't know if it fails for one or the other, so it won't detect changes that we do looking forward. If there are transformers that have the attribute inverse_transform and it's not implemented, we need to fail specifically for those with the NotImplementedError and for the rest with the NotFittedError
Address review: rather than accepting either NotFittedError or NotImplementedError, determine per transformer which one is expected and assert exactly that, so a transformer whose inverse_transform stops guarding fit is no longer hidden. A transformer's inverse_transform is treated as not implemented when a fitted clone raises NotImplementedError; those are expected to raise NotImplementedError before fit, all others NotFittedError. The classification fits on a dataframe with categorical and datetime features so every transformer reaching this check can be fitted.
|
Thanks @solegalli, that's a fair point — the tuple would have hidden exactly the regressions this check exists to catch. Fixed in the latest commit. The check now determines, per transformer, which error it should raise and asserts only that one:
I detect implementation status on a fitted clone rather than a hardcoded list, so it stays correct for the conditional cases too (e.g. I confirmed it's now strict: a transformer whose The classification fits on |
|
Thank you @TemidayoA for the quick update! |
What this does
Part of #420 (the
inverse_transformhalf —get_feature_names_outchecks were added in #519).The shared estimator check
check_raises_non_fitted_erroronly verified thattransform()raisesNotFittedErrorbeforefit(). Every transformer that implementsinverse_transformalready guards it (via_check_transform_input_and_state/check_is_fitted), but no test covered that behaviour, so a regression would go unnoticed.This extends the shared check to also call
inverse_transformon an unfitted clone. Transformers that deliberately don't support inversion (OneHotEncoder,StringSimilarityEncoder) raiseNotImplementedError, which the check accepts.Because the check runs through
check_feature_engine_estimator, all transformers across all modules are covered automatically — no per-transformer test changes needed.Verification
inverse_transformfails it; real transformers passflake8clean on the changed file