17
17
)
18
18
from ..exceptions import ConvergenceWarning
19
19
from ..utils import check_consistent_length , check_random_state
20
+ from ..utils ._bunch import Bunch
20
21
from ..utils ._param_validation import (
21
22
HasMethods ,
22
23
Interval ,
25
26
StrOptions ,
26
27
)
27
28
from ..utils .metadata_routing import (
28
- _raise_for_unsupported_routing ,
29
- _RoutingNotSupportedMixin ,
29
+ MetadataRouter ,
30
+ MethodMapping ,
31
+ _raise_for_params ,
32
+ _routing_enabled ,
33
+ process_routing ,
30
34
)
31
35
from ..utils .random import sample_without_replacement
32
- from ..utils .validation import _check_sample_weight , check_is_fitted , has_fit_parameter
36
+ from ..utils .validation import (
37
+ _check_method_params ,
38
+ _check_sample_weight ,
39
+ _deprecate_positional_args ,
40
+ check_is_fitted ,
41
+ has_fit_parameter ,
42
+ )
33
43
from ._base import LinearRegression
34
44
<
10000
td data-grid-cell-id="diff-fa70c71c755e7824bda9d9fe5e1a8c2c48d80ff23033ce51196867f134c24c68-34-44-2" data-line-anchor="diff-fa70c71c755e7824bda9d9fe5e1a8c2c48d80ff23033ce51196867f134c24c68R44" data-selected="false" role="gridcell" style="background-color:var(--bgColor-default);padding-right:24px" tabindex="-1" valign="top" class="focusable-grid-cell diff-text-cell right-side-diff-cell left-side">
35
45
_EPSILON = np .spacing (1 )
@@ -70,7 +80,6 @@ def _dynamic_max_trials(n_inliers, n_samples, min_samples, probability):
70
80
71
81
72
82
class RANSACRegressor (
73
- _RoutingNotSupportedMixin ,
74
83
MetaEstimatorMixin ,
75
84
RegressorMixin ,
76
85
MultiOutputMixin ,
@@ -306,7 +315,11 @@ def __init__(
306
315
# RansacRegressor.estimator is not validated yet
307
316
prefer_skip_nested_validation = False
308
317
)
309
- def fit (self , X , y , sample_weight = None ):
318
+ # TODO(1.7): remove `sample_weight` from the signature after deprecation
319
+ # cycle; for backwards compatibility: pop it from `fit_params` before the
320
+ # `_raise_for_params` check and reinsert it after the check
321
+ @_deprecate_positional_args (version = "1.7" )
322
+ def fit (self , X , y , * , sample_weight = None , ** fit_params ):
310
323
"""Fit estimator using RANSAC algorithm.
311
324
312
325
Parameters
@@ -324,6 +337,17 @@ def fit(self, X, y, sample_weight=None):
324
337
325
338
.. versionadded:: 0.18
326
339
340
+ **fit_params : dict
341
+ Parameters routed to the `fit` method of the sub-estimator via the
342
+ metadata routing API.
343
+
344
+ .. versionadded:: 1.5
345
+
346
+ Only available if
347
+ `sklearn.set_config(enable_metadata_routing=True)` is set. See
348
+ :ref:`Metadata Routing User Guide <metadata_routing>` for more
349
+ details.
350
+
327
351
Returns
328
352
-------
329
353
self : object
@@ -336,10 +360,10 @@ def fit(self, X, y, sample_weight=None):
336
360
`is_data_valid` and `is_model_valid` return False for all
337
361
`max_trials` randomly chosen sub-samples.
338
362
"""
339
- _raise_for_unsupported_routing (self , "fit" , sample_weight = sample_weight )
340
363
# Need to validate separately here. We can't pass multi_output=True
341
364
# because that would allow y to be csr. Delay expensive finiteness
342
365
# check to the estimator's own input validation.
366
+ _raise_for_params (fit_params , self , "fit" )
343
367
check_X_params = dict (accept_sparse = "csr" , force_all_finite = False )
344
368
check_y_params = dict (ensure_2d = False )
345
369
X , y = self ._validate_data (
@@ -404,12 +428,22 @@ def fit(self, X, y, sample_weight=None):
404
428
estimator_name = type (estimator ).__name__
405
429
if sample_weight is not None and not estimator_fit_has_sample_weight :
406
430
raise ValueError (
407
- "%s does not support sample_weight. Samples "
431
+ "%s does not support sample_weight. Sample "
408
432
" weights are only used for the calibration"
409
433
" itself." % estimator_name
410
434
)
435
+
411
436
if sample_weight is not None :
412
- sample_weight = _check_sample_weight (sample_weight , X )
437
+ fit_params ["sample_weight" ] = sample_weight
438
+
439
+ if _routing_enabled ():
440
+ routed_params = process_routing (self , "fit" , ** fit_params )
441
+ else :
442
+ routed_params = Bunch ()
443
+ routed_params .estimator = Bunch (fit = {}, predict = {}, score = {})
444
+ if sample_weight is not None :
445
+ sample_weight = _check_sample_weight (sa
1241
mple_weight , X )
446
+ routed_params .estimator .fit = {"sample_weight" : sample_weight }
413
447
414
448
n_inliers_best = 1
415
449
score_best = - np .inf
@@ -451,13 +485,13 @@ def fit(self, X, y, sample_weight=None):
451
485
self .n_skips_invalid_data_ += 1
452
486
continue
453
487
488
+ # cut `fit_params` down to `subset_idxs`
489
+ fit_params_subset = _check_method_params (
490
+ X , params = routed_params .estimator .fit , indices = subset_idxs
491
+ )
492
+
454
493
# fit model for current random sample set
455
- if sample_weight is None :
456
- estimator .fit (X_subset , y_subset )
457
- else :
458
- estimator .fit (
459
- X_subset , y_subset , sample_weight = sample_weight [subset_idxs ]
460
- )
494
+ estimator .fit (X_subset , y_subset , ** fit_params_subset )
461
495
462
496
# check if estimated model is valid
463
497
if self .is_model_valid is not None and not self .is_model_valid (
@@ -484,8 +518,17 @@ def fit(self, X, y, sample_weight=None):
484
518
X_inlier_subset = X [inlier_idxs_subset ]
485
519
y_inlier_subset = y [inlier_idxs_subset ]
486
520
521
+ # cut `fit_params` down to `inlier_idxs_subset`
522
+ score_params_inlier_subset = _check_method_params (
523
+ X , params = routed_params .estimator .score , indices = inlier_idxs_subset
524
+ )
525
+
487
526
# score of inlier data set
488
- score_subset = estimator .score (X_inlier_subset , y_inlier_subset )
527
+ score_subset = estimator .score (
528
+ X_inlier_subset ,
529
+ y_inlier_subset ,
530
+ ** score_params_inlier_subset ,
531
+ )
489
532
490
533
# same number of inliers but worse score -> skip current random
491
534
# sample
@@ -549,20 +592,17 @@ def fit(self, X, y, sample_weight=None):
549
592
)
550
593
551
594
# estimate final model using all inliers
552
- if sample_weight is None :
553
- estimator .fit (X_inlier_best , y_inlier_best )
554
- else :
555
- estimator .fit (
556
- X_inlier_best ,
557
- y_inlier_best ,
558
- sample_weight = sample_weight [inlier_best_idxs_subset ],
559
- )
595
+ fit_params_best_idxs_subset = _check_method_params (
596
+ X , params = routed_params .estimator .fit , indices = inlier_best_idxs_subset
597
+ )
598
+
599
+ estimator .fit (X_inlier_best , y_inlier_best , ** fit_params_best_idxs_subset )
560
600
561
601
self .estimator_ = estimator
562
602
self .inlier_mask_ = inlier_mask_best
563
603
return self
564
604
565
- def predict (self , X ):
605
+ def predict (self , X , ** params ):
566
606
"""Predict using the estimated model.
567
607
568
608
This is a wrapper for `estimator_.predict(X)`.
@@ -572,6 +612,17 @@ def predict(self, X):
572
612
X : {array-like or sparse matrix} of shape (n_samples, n_features)
573
613
Input data.
574
614
615
+ **params : dict
616
+ Parameters routed to the `predict` method of the sub-estimator via
617
+ the metadata routing API.
618
+
619
+ .. versionadded:: 1.5
620
+
621
+ Only available if
622
+ `sklearn.set_config(enable_metadata_routing=True)` is set. See
623
+ :ref:`Metadata Routing User Guide <metadata_routing>` for more
624
+ details.
625
+
575
626
Returns
576
627
-------
577
628
y : array, shape = [n_samples] or [n_samples, n_targets]
@@ -584,9 +635,19 @@ def predict(self, X):
584
635
accept_sparse = True ,
585
636
reset = False ,
586
637
)
587
- return self .estimator_ .predict (X )
588
638
589
- def score (self , X , y ):
639
+ _raise_for_params (params , self , "predict" )
640
+
641
+ if _routing_enabled ():
642
+ predict_params = process_routing (self , "predict" , ** params ).estimator [
643
+ "predict"
644
+ ]
645
+ else :
646
+ predict_params = {}
647
+
648
+ return self .estimator_ .predict (X , ** predict_params )
649
+
650
+ def score (self , X , y , ** params ):
590
651
"""Return the score of the prediction.
591
652
592
653
This is a wrapper for `estimator_.score(X, y)`.
@@ -599,6 +660,17 @@ def score(self, X, y):
599
660
y : array-like of shape (n_samples,) or (n_samples, n_targets)
600
661
Target values.
601
662
663
+ **params : dict
664
+ Parameters routed to the `score` method of the sub-estimator via
665
+ the metadata routing API.
666
+
667
+ .. versionadded:: 1.5
668
+
669
+ Only available if
670
+ `sklearn.set_config(enable_metadata_routing=True)` is set. See
671
+ :ref:`Metadata Routing User Guide <metadata_routing>` for more
672
+ details.
673
+
602
674
Returns
603
675
-------
604
676
z : float
@@ -611,7 +683,38 @@ def score(self, X, y):
611
683
accept_sparse = True ,
612
684
reset = False ,
613
685
)
614
- return self .estimator_ .score (X , y )
686
+
687
+ _raise_for_params (params , self , "score" )
688
+ if _routing_enabled ():
689
+ score_params = process_routing (self , "score" , ** params ).estimator ["score" ]
690
+ else :
691
+ score_params = {}
692
+
693
+ return self .estimator_ .score (X , y , ** score_params )
694
+
695
+ def get_metadata_routing (self ):
696
+ """Get metadata routing of this object.
697
+
698
+ Please check :ref:`User Guide <metadata_routing>` on how the routing
699
+ mechanism works.
700
+
701
+ .. versionadded:: 1.5
702
+
703
+ Returns
C2EE
span>
704
+ -------
705
+ routing : MetadataRouter
706
+ A :class:`~sklearn.utils.metadata_routing.MetadataRouter` encapsulating
707
+ routing information.
708
+ """
709
+ router = MetadataRouter (owner = self .__class__ .__name__ ).add (
710
+ estimator = self .estimator ,
711
+ method_mapping = MethodMapping ()
712
+ .add (caller = "fit" , callee = "fit" )
713
+ .add (caller = "fit" , callee = "score" )
714
+ .add (caller = "score" , callee = "score" )
715
+ .add (caller = "predict" , callee = "predict" ),
716
+ )
717
+ return router
615
718
616
719
def _more_tags (self ):
617
720
return {
0 commit comments