Skip to content

Commit 6d2c9f2

Browse files
gguiomarjeremiedbb
andauthored
FIX Add validation for FeatureUnion transformer outputs (scikit-learn#31318) (scikit-learn#31559)
Co-authored-by: Jérémie du Boisberranger <jeremie@probabl.ai>
1 parent a64b6b2 commit 6d2c9f2

3 files changed

Lines changed: 32 additions & 4 deletions

File tree

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
- :class:`pipeline.FeatureUnion` now validates that all transformers return 2D outputs
2+
and raises an informative error when transformers return 1D outputs, preventing
3+
silent failures that previously produced meaningless concatenated results.
4+
By :user:`gguiomar <gguiomar>`.

sklearn/pipeline.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2038,15 +2038,23 @@ def transform(self, X, **params):
20382038
return self._hstack(Xs)
20392039

20402040
def _hstack(self, Xs):
2041+
# Check if Xs dimensions are valid
2042+
for X, (name, _) in zip(Xs, self.transformer_list):
2043+
if hasattr(X, "shape") and len(X.shape) != 2:
2044+
raise ValueError(
2045+
f"Transformer '{name}' returned an array or dataframe with "
2046+
f"{len(X.shape)} dimensions, but expected 2 dimensions "
2047+
"(n_samples, n_features)."
2048+
)
2049+
20412050
adapter = _get_container_adapter("transform", self)
20422051
if adapter and all(adapter.is_supported_container(X) for X in Xs):
20432052
return adapter.hstack(Xs)
20442053

20452054
if any(sparse.issparse(f) for f in Xs):
2046-
Xs = sparse.hstack(Xs).tocsr()
2047-
else:
2048-
Xs = np.hstack(Xs)
2049-
return Xs
2055+
return sparse.hstack(Xs).tocsr()
2056+
2057+
return np.hstack(Xs)
20502058

20512059
def _update_transformer_list(self, transformers):
20522060
transformers = iter(transformers)

sklearn/tests/test_pipeline.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,22 @@ def test_feature_union_feature_names_in_():
19001900
assert not hasattr(union, "feature_names_in_")
19011901

19021902

1903+
def test_feature_union_1d_output():
1904+
"""Test that FeatureUnion raises error for 1D transformer outputs."""
1905+
X = np.arange(6).reshape(3, 2)
1906+
1907+
with pytest.raises(
1908+
ValueError,
1909+
match="Transformer 'b' returned an array or dataframe with 1 dimensions",
1910+
):
1911+
FeatureUnion(
1912+
[
1913+
("a", FunctionTransformer(lambda X: X)),
1914+
("b", FunctionTransformer(lambda X: X[:, 1])),
1915+
]
1916+
).fit_transform(X)
1917+
1918+
19031919
# transform_input tests
19041920
# =====================
19051921

0 commit comments

Comments
 (0)