8000 FIX Pipeline steps incorrectly updated with passthrough step (#12659) · scikit-learn/scikit-learn@1b90237 · GitHub
[go: up one dir, main page]

Skip to content

Commit 1b90237

Browse files
thomasjpfanjnothman
authored andcommitted
FIX Pipeline steps incorrectly updated with passthrough step (#12659)
1 parent 6f9b1b6 commit 1b90237

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

doc/whats_new/v0.20.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ enhancements to features released in 0.20.0.
1515
Changelog
1616
---------
1717

18+
:mod:`sklearn.pipeline`
19+
.......................
1820

21+
- |Fix| Fixed a regression in :class:`pipeline.Pipeline` where the ``steps``
22+
parameter may not have been updated correctly when a step is set to ``None``
23+
or ``'passthrough'``. :user:`Thomas Fan <thomasjpfan>`.
1924

2025

2126
.. _changes_0_20_1:

sklearn/pipeline.py

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ def _iter(self, with_final=True):
184184
if not with_final:
185185
stop -= 1
186186

187-
for name, trans in islice(self.steps, 0, stop):
187+
for idx, (name, trans) in enumerate(islice(self.steps, 0, stop)):
188188
if trans is not None and trans != 'passthrough':
189-
yield name, trans
189+
yield idx, name, trans
190190

191191
@property
192192
def _estimator_type(self):
@@ -219,8 +219,7 @@ def _fit(self, X, y=None, **fit_params):
219219
step, param = pname.split('__', 1)
220220
fit_params_steps[step][param] = pval
221221
Xt = X
222-
for step_idx, (name, transformer) in enumerate(
223-
self._iter(with_final=False)):
222+
for step_idx, name, transformer in self._iter(with_final=False):
224223
if hasattr(memory, 'location'):
225224
# joblib >= 0.12
226225
if memory.location is None:
@@ -341,7 +340,7 @@ def predict(self, X, **predict_params):
341340
y_pred : array-like
342341
"""
343342
Xt = X
344-
for name, transform in self._iter(with_final=False):
343+
for _, name, transform in self._iter(with_final=False):
345344
Xt = transform.transform(Xt)
346345
return self.steps[-1][-1].predict(Xt, **predict_params)
347346

@@ -390,7 +389,7 @@ def predict_proba(self, X):
390389
y_proba : array-like, shape = [n_samples, n_classes]
391390
"""
392391
Xt = X
393-
for name, transform in self._iter(with_final=False):
392+
for _, name, transform in self._iter(with_final=False):
394393
Xt = transform.transform(Xt)
395394
return self.steps[-1][-1].predict_proba(Xt)
396395

@@ -409,7 +408,7 @@ def decision_function(self, X):
409408
y_score : array-like, shape = [n_samples, n_classes]
410409
"""
411410
Xt = X
412-
for name, transform in self._iter(with_final=False):
411+
for _, name, transform in self._iter(with_final=False):
413412
Xt = transform.transform(Xt)
414413
return self.steps[-1][-1].decision_function(Xt)
415414

@@ -428,7 +427,7 @@ def predict_log_proba(self, X):
428427
y_score : array-like, shape = [n_samples, n_classes]
429428
"""
430429
Xt = X
431-
for name, transform in self._iter(with_final=False):
430+
for _, name, transform in self._iter(with_final=False):
432431
Xt = transform.transform(Xt)
433432
return self.steps[-1][-1].predict_log_proba(Xt)
434433

@@ -457,7 +456,7 @@ def transform(self):
457456

458457
def _transform(self, X):
459458
Xt = X
460-
for _, transform in self._iter():
459+
for _, _, transform in self._iter():
461460
Xt = transform.transform(Xt)
462461
return Xt
463462

@@ -481,14 +480,14 @@ def inverse_transform(self):
481480
"""
482481
# raise AttributeError if necessary for hasattr behaviour
483482
# XXX: Handling the None case means we can't use if_delegate_has_method
484-
for _, transform in self._iter():
483+
for _, _, transform in self._iter():
485484
transform.inverse_transform
486485
return self._inverse_transform
487486

488487
def _inverse_transform(self, X):
489488
Xt = X
490489
reverse_iter = reversed(list(self._iter()))
491-
for _, transform in reverse_iter:
490+
for _, _, transform in reverse_iter:
492491
Xt = transform.inverse_transform(Xt)
493492
return Xt
494493

@@ -515,7 +514,7 @@ def score(self, X, y=None, sample_weight=None):
515514
score : float
516515
"""
517516
Xt = X
518-
for name, transform in self._iter(with_final=False):
517+
for _, name, transform in self._iter(with_final=False):
519518
Xt = transform.transform(Xt)
520519
score_params = {}
521520
if sample_weight is not None:

sklearn/tests/test_pipeline.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -574,6 +574,27 @@ def test_pipeline_named_steps():
574574
assert pipeline.named_steps.mult is mult2
575575

576576

577+
@pytest.mark.parametrize('passthrough', [None, 'passthrough'])
578+
def test_pipeline_correctly_adjusts_steps(passthrough):
579+
X = np.array([[1]])
580+
y = np.array([1])
581+
mult2 = Mult(mult=2)
582+
mult3 = Mult(mult=3)
583+
mult5 = Mult(mult=5)
584+
585+
pipeline = Pipeline([
586+
('m2', mult2),
587+
('bad', passthrough),
588+
('m3', mult3),
589+
('m5', mult5)
590+
])
591+
592+
pipeline.fit(X, y)
593+
expected_names = ['m2', 'bad', 'm3', 'm5']
594+
actual_names = [name for name, _ in pipeline.steps]
595+
assert expected_names == actual_names
596+
597+
577598
@pytest.mark.parametrize('passthrough', [None, 'passthrough'])
578599
def test_set_pipeline_step_passthrough(passthrough):
579600
X = np.array([[1]])

0 commit comments

Comments
 (0)
0