Expand source code
from copy import deepcopy
from typing import List
import numpy as np
from sklearn.base import BaseEstimator, ClassifierMixin
from sklearn.model_selection import cross_val_score
from imodels.tree.hierarchical_shrinkage import HSTreeRegressor, HSTreeClassifier
from imodels.util.tree import compute_tree_complexity
from imodels.util.introspection import RuleInspectionMixin
from imodels.util.arguments import explicit_get_params, explicit_set_params
class DecisionTreeCCPClassifier(RuleInspectionMixin, ClassifierMixin, BaseEstimator):
def __init__(self, estimator_: BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args,
**kwargs):
self.desired_complexity = desired_complexity
self.estimator_ = estimator_
self.complexity_measure = complexity_measure
#: __init__ takes *args/**kwargs, which sklearn's introspection rejects,
#: so the parameters are spelled out here instead
_PARAM_NAMES = ("estimator_", "desired_complexity", "complexity_measure")
def get_params(self, deep=True):
return explicit_get_params(self, self._PARAM_NAMES, deep=deep)
def set_params(self, **params):
return explicit_set_params(self, self._PARAM_NAMES, **params)
def _copy_fitted_attributes(self):
"""Mirror the wrapped estimator's fitted sklearn attributes onto self."""
for attr in ("classes_", "n_features_in_", "feature_names_in_"):
if hasattr(self.estimator_, attr):
setattr(self, attr, getattr(self.estimator_, attr))
def _get_alpha(self, X, y, sample_weight=None, *args, **kwargs):
path = self.estimator_.cost_complexity_pruning_path(
X, y, sample_weight=sample_weight)
ccp_alphas = path.ccp_alphas
low = 0
high = len(ccp_alphas) - 1
cur = 0
while low <= high:
cur = (high + low) // 2
est_params = self.estimator_.get_params()
est_params['ccp_alpha'] = ccp_alphas[cur]
copied_estimator = deepcopy(self.estimator_).set_params(**est_params)
copied_estimator.fit(X, y, sample_weight=sample_weight)
if self._get_complexity(copied_estimator, self.complexity_measure) < self.desired_complexity:
high = cur - 1
elif self._get_complexity(copied_estimator, self.complexity_measure) > self.desired_complexity:
low = cur + 1
else:
break
self.alpha = ccp_alphas[cur]
# for alpha in ccp_alphas:
# est_params = self.estimator_.get_params()
# est_params['ccp_alpha'] = alpha
# copied_estimator = deepcopy(self.estimator_).set_params(**est_params)
# copied_estimator.fit(X, y)
# complexities[alpha] = self._get_complexity(copied_estimator,self.complexity_measure)
# closest_alpha, closest_leaves = min(complexities.items(), key=lambda x: abs(self.desired_complexity - x[1]))
# self.alpha = closest_alpha
def fit(self, X, y, sample_weight=None, *args, **kwargs):
# fit a copy, so the estimator passed to __init__ is left untouched and
# two models sharing one estimator cannot interfere with each other
self.estimator_ = deepcopy(self.estimator_)
params_for_fitting = self.estimator_.get_params()
self._get_alpha(X, y, sample_weight, *args, **kwargs)
params_for_fitting['ccp_alpha'] = self.alpha
self.estimator_.set_params(**params_for_fitting)
self.estimator_.fit(X, y, *args, sample_weight=sample_weight, **kwargs)
self._copy_fitted_attributes()
return self
@property
def feature_importances_(self):
"""Mean decrease in impurity of the pruned tree, as in sklearn."""
return self.estimator_.feature_importances_
def _get_complexity(self, BaseEstimator, complexity_measure):
return compute_tree_complexity(BaseEstimator.tree_, complexity_measure)
def predict_proba(self, X, *args, **kwargs):
if hasattr(self.estimator_, 'predict_proba'):
return self.estimator_.predict_proba(X, *args, **kwargs)
else:
return NotImplemented
def predict(self, X, *args, **kwargs):
return self.estimator_.predict(X, *args, **kwargs)
def score(self, X, y, *args, **kwargs):
if hasattr(self.estimator_, 'score'):
return self.estimator_.score(X, y, *args, **kwargs)
else:
return NotImplemented
class DecisionTreeCCPRegressor(RuleInspectionMixin, BaseEstimator):
def __init__(self, estimator_: BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args,
**kwargs):
self.desired_complexity = desired_complexity
self.estimator_ = estimator_
self.alpha = 0.0
self.complexity_measure = complexity_measure
#: __init__ takes *args/**kwargs, which sklearn's introspection rejects,
#: so the parameters are spelled out here instead
_PARAM_NAMES = ("estimator_", "desired_complexity", "complexity_measure")
def get_params(self, deep=True):
return explicit_get_params(self, self._PARAM_NAMES, deep=deep)
def set_params(self, **params):
return explicit_set_params(self, self._PARAM_NAMES, **params)
def _copy_fitted_attributes(self):
"""Mirror the wrapped estimator's fitted sklearn attributes onto self."""
for attr in ("n_features_in_", "feature_names_in_"):
if hasattr(self.estimator_, attr):
setattr(self, attr, getattr(self.estimator_, attr))
def _get_alpha(self, X, y, sample_weight=None):
path = self.estimator_.cost_complexity_pruning_path(
X, y, sample_weight=sample_weight)
ccp_alphas = path.ccp_alphas
low = 0
high = len(ccp_alphas) - 1
cur = 0
while low <= high:
cur = (high + low) // 2
est_params = self.estimator_.get_params()
est_params['ccp_alpha'] = ccp_alphas[cur]
copied_estimator = deepcopy(self.estimator_).set_params(**est_params)
copied_estimator.fit(X, y, sample_weight=sample_weight)
if self._get_complexity(copied_estimator, self.complexity_measure) < self.desired_complexity:
high = cur - 1
elif self._get_complexity(copied_estimator, self.complexity_measure) > self.desired_complexity:
low = cur + 1
else:
break
self.alpha = ccp_alphas[cur]
# path = self.estimator_.cost_complexity_pruning_path(X,y)
# ccp_alphas, impurities = path.ccp_alphas, path.impurities
# complexities = {}
# for alpha in ccp_alphas:
# est_params = self.estimator_.get_params()
# est_params['ccp_alpha'] = alpha
# copied_estimator = deepcopy(self.estimator_).set_params(**est_params)
# copied_estimator.fit(X, y)
# complexities[alpha] = self._get_complexity(copied_estimator,self.complexity_measure)
# closest_alpha, closest_leaves = min(complexities.items(), key=lambda x: abs(self.desired_complexity - x[1]))
# self.alpha = closest_alpha
def fit(self, X, y, sample_weight=None):
# fit a copy (see DecisionTreeCCPClassifier.fit)
self.estimator_ = deepcopy(self.estimator_)
params_for_fitting = self.estimator_.get_params()
self._get_alpha(X, y, sample_weight)
params_for_fitting['ccp_alpha'] = self.alpha
self.estimator_.set_params(**params_for_fitting)
self.estimator_.fit(X, y, sample_weight=sample_weight)
self._copy_fitted_attributes()
return self
@property
def feature_importances_(self):
"""Mean decrease in impurity of the pruned tree, as in sklearn."""
return self.estimator_.feature_importances_
def _get_complexity(self, BaseEstimator, complexity_measure):
return compute_tree_complexity(BaseEstimator.tree_, self.complexity_measure)
def predict(self, X, *args, **kwargs):
return self.estimator_.predict(X, *args, **kwargs)
def score(self, X, y, *args, **kwargs):
if hasattr(self.estimator_, 'score'):
return self.estimator_.score(X, y, *args, **kwargs)
else:
return NotImplemented
class HSDecisionTreeCCPRegressorCV(HSTreeRegressor):
def __init__(self, estimator_: BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500],
desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs):
super().__init__(estimator_=estimator_, reg_param=None)
self.reg_param_list = np.array(reg_param_list)
self.cv = cv
self.scoring = scoring
self.desired_complexity = desired_complexity
def fit(self, X, y, sample_weight=None, *args, **kwargs):
m = DecisionTreeCCPRegressor(self.estimator_, desired_complexity=self.desired_complexity)
m.fit(X, y, sample_weight, *args, **kwargs)
self.scores_ = []
for reg_param in self.reg_param_list:
est = HSTreeRegressor(deepcopy(m.estimator_), reg_param)
cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring)
self.scores_.append(np.mean(cv_scores))
self.reg_param = self.reg_param_list[np.argmax(self.scores_)]
return super().fit(X=X, y=y)
class HSDecisionTreeCCPClassifierCV(HSTreeClassifier):
def __init__(self, estimator_: BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500],
desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs):
super().__init__(estimator_=estimator_, reg_param=None)
self.reg_param_list = np.array(reg_param_list)
self.cv = cv
self.scoring = scoring
self.desired_complexity = desired_complexity
def fit(self, X, y, sample_weight=None, *args, **kwargs):
m = DecisionTreeCCPClassifier(self.estimator_, desired_complexity=self.desired_complexity)
m.fit(X, y, sample_weight, *args, **kwargs)
self.scores_ = []
for reg_param in self.reg_param_list:
est = HSTreeClassifier(deepcopy(m.estimator_), reg_param)
cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring)
self.scores_.append(np.mean(cv_scores))
self.reg_param = self.reg_param_list[np.argmax(self.scores_)]
return super().fit(X=X, y=y)
Classes
class DecisionTreeCCPClassifier (estimator_: sklearn.base.BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args, **kwargs)-
Both of the above, for tree-based models that support each.
Expand source code
class DecisionTreeCCPClassifier(RuleInspectionMixin, ClassifierMixin, BaseEstimator): def __init__(self, estimator_: BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args, **kwargs): self.desired_complexity = desired_complexity self.estimator_ = estimator_ self.complexity_measure = complexity_measure #: __init__ takes *args/**kwargs, which sklearn's introspection rejects, #: so the parameters are spelled out here instead _PARAM_NAMES = ("estimator_", "desired_complexity", "complexity_measure") def get_params(self, deep=True): return explicit_get_params(self, self._PARAM_NAMES, deep=deep) def set_params(self, **params): return explicit_set_params(self, self._PARAM_NAMES, **params) def _copy_fitted_attributes(self): """Mirror the wrapped estimator's fitted sklearn attributes onto self.""" for attr in ("classes_", "n_features_in_", "feature_names_in_"): if hasattr(self.estimator_, attr): setattr(self, attr, getattr(self.estimator_, attr)) def _get_alpha(self, X, y, sample_weight=None, *args, **kwargs): path = self.estimator_.cost_complexity_pruning_path( X, y, sample_weight=sample_weight) ccp_alphas = path.ccp_alphas low = 0 high = len(ccp_alphas) - 1 cur = 0 while low <= high: cur = (high + low) // 2 est_params = self.estimator_.get_params() est_params['ccp_alpha'] = ccp_alphas[cur] copied_estimator = deepcopy(self.estimator_).set_params(**est_params) copied_estimator.fit(X, y, sample_weight=sample_weight) if self._get_complexity(copied_estimator, self.complexity_measure) < self.desired_complexity: high = cur - 1 elif self._get_complexity(copied_estimator, self.complexity_measure) > self.desired_complexity: low = cur + 1 else: break self.alpha = ccp_alphas[cur] # for alpha in ccp_alphas: # est_params = self.estimator_.get_params() # est_params['ccp_alpha'] = alpha # copied_estimator = deepcopy(self.estimator_).set_params(**est_params) # copied_estimator.fit(X, y) # complexities[alpha] = self._get_complexity(copied_estimator,self.complexity_measure) # closest_alpha, closest_leaves = min(complexities.items(), key=lambda x: abs(self.desired_complexity - x[1])) # self.alpha = closest_alpha def fit(self, X, y, sample_weight=None, *args, **kwargs): # fit a copy, so the estimator passed to __init__ is left untouched and # two models sharing one estimator cannot interfere with each other self.estimator_ = deepcopy(self.estimator_) params_for_fitting = self.estimator_.get_params() self._get_alpha(X, y, sample_weight, *args, **kwargs) params_for_fitting['ccp_alpha'] = self.alpha self.estimator_.set_params(**params_for_fitting) self.estimator_.fit(X, y, *args, sample_weight=sample_weight, **kwargs) self._copy_fitted_attributes() return self @property def feature_importances_(self): """Mean decrease in impurity of the pruned tree, as in sklearn.""" return self.estimator_.feature_importances_ def _get_complexity(self, BaseEstimator, complexity_measure): return compute_tree_complexity(BaseEstimator.tree_, complexity_measure) def predict_proba(self, X, *args, **kwargs): if hasattr(self.estimator_, 'predict_proba'): return self.estimator_.predict_proba(X, *args, **kwargs) else: return NotImplemented def predict(self, X, *args, **kwargs): return self.estimator_.predict(X, *args, **kwargs) def score(self, X, y, *args, **kwargs): if hasattr(self.estimator_, 'score'): return self.estimator_.score(X, y, *args, **kwargs) else: return NotImplementedAncestors
- RuleInspectionMixin
- RulesMixin
- LeavesMixin
- sklearn.base.ClassifierMixin
- sklearn.base.BaseEstimator
- sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
- sklearn.utils._metadata_requests._MetadataRequester
Instance variables
var feature_importances_-
Mean decrease in impurity of the pruned tree, as in sklearn.
Expand source code
@property def feature_importances_(self): """Mean decrease in impurity of the pruned tree, as in sklearn.""" return self.estimator_.feature_importances_
Methods
def fit(self, X, y, sample_weight=None, *args, **kwargs)-
Expand source code
def fit(self, X, y, sample_weight=None, *args, **kwargs): # fit a copy, so the estimator passed to __init__ is left untouched and # two models sharing one estimator cannot interfere with each other self.estimator_ = deepcopy(self.estimator_) params_for_fitting = self.estimator_.get_params() self._get_alpha(X, y, sample_weight, *args, **kwargs) params_for_fitting['ccp_alpha'] = self.alpha self.estimator_.set_params(**params_for_fitting) self.estimator_.fit(X, y, *args, sample_weight=sample_weight, **kwargs) self._copy_fitted_attributes() return self def get_params(self, deep=True)-
Get parameters for this estimator.
Parameters
deep:bool, default=True- If True, will return the parameters for this estimator and contained subobjects that are estimators.
Returns
params:dict- Parameter names mapped to their values.
Expand source code
def get_params(self, deep=True): return explicit_get_params(self, self._PARAM_NAMES, deep=deep) def predict(self, X, *args, **kwargs)-
Expand source code
def predict(self, X, *args, **kwargs): return self.estimator_.predict(X, *args, **kwargs) def predict_proba(self, X, *args, **kwargs)-
Expand source code
def predict_proba(self, X, *args, **kwargs): if hasattr(self.estimator_, 'predict_proba'): return self.estimator_.predict_proba(X, *args, **kwargs) else: return NotImplemented def score(self, X, y, *args, **kwargs)-
Return the mean accuracy on the given test data and labels.
In multi-label classification, this is the subset accuracy which is a harsh metric since you require for each sample that each label set be correctly predicted.
Parameters
X:array-likeofshape (n_samples, n_features)- Test samples.
y:array-likeofshape (n_samples,)or(n_samples, n_outputs)- True labels for
X. sample_weight:array-likeofshape (n_samples,), default=None- Sample weights.
Returns
score:float- Mean accuracy of
self.predict(X)w.r.t.y.
Expand source code
def score(self, X, y, *args, **kwargs): if hasattr(self.estimator_, 'score'): return self.estimator_.score(X, y, *args, **kwargs) else: return NotImplemented def set_fit_request(self: DecisionTreeCCPClassifier, *, sample_weight: bool | str | None = '$UNCHANGED$') ‑> DecisionTreeCCPClassifier-
Request metadata passed to the
fitmethod.Note that this method is only relevant if
enable_metadata_routing=True(see :func:sklearn.set_config). Please see :ref:User Guide <metadata_routing>on how the routing mechanism works.The options for each parameter are:
-
True: metadata is requested, and passed tofitif provided. The request is ignored if metadata is not provided. -
False: metadata is not requested and the meta-estimator will not pass it tofit. -
None: metadata is not requested, and the meta-estimator will raise an error if the user provides it. -
str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version: 1.3
Note
This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a :class:
~sklearn.pipeline.Pipeline. Otherwise it has no effect.Parameters
sample_weight:str, True, False,orNone, default=sklearn.utils.metadata_routing.UNCHANGED- Metadata routing for
sample_weightparameter infit.
Returns
self:object- The updated object.
Expand source code
def func(*args, **kw): """Updates the request for provided parameters This docstring is overwritten below. See REQUESTER_DOC for expected functionality """ if not _routing_enabled(): raise RuntimeError( "This method is only available when metadata routing is enabled." " You can enable it using" " sklearn.set_config(enable_metadata_routing=True)." ) if self.validate_keys and (set(kw) - set(self.keys)): raise TypeError( f"Unexpected args: {set(kw) - set(self.keys)} in {self.name}. " f"Accepted arguments are: {set(self.keys)}" ) # This makes it possible to use the decorated method as an unbound method, # for instance when monkeypatching. # https://github.com/scikit-learn/scikit-learn/issues/28632 if instance is None: _instance = args[0] args = args[1:] else: _instance = instance # Replicating python's behavior when positional args are given other than # `self`, and `self` is only allowed if this method is unbound. if args: raise TypeError( f"set_{self.name}_request() takes 0 positional argument but" f" {len(args)} were given" ) requests = _instance._get_metadata_request() method_metadata_request = getattr(requests, self.name) for prop, alias in kw.items(): if alias is not UNCHANGED: method_metadata_request.add_request(param=prop, alias=alias) _instance._metadata_request = requests return _instance -
def set_params(self, **params)-
Set the parameters of this estimator.
The method works on simple estimators as well as on nested objects (such as :class:
~sklearn.pipeline.Pipeline). The latter have parameters of the form<component>__<parameter>so that it's possible to update each component of a nested object.Parameters
**params:dict- Estimator parameters.
Returns
self:estimator instance- Estimator instance.
Expand source code
def set_params(self, **params): return explicit_set_params(self, self._PARAM_NAMES, **params)
Inherited members
class DecisionTreeCCPRegressor (estimator_: sklearn.base.BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args, **kwargs)-
Both of the above, for tree-based models that support each.
Expand source code
class DecisionTreeCCPRegressor(RuleInspectionMixin, BaseEstimator): def __init__(self, estimator_: BaseEstimator, desired_complexity: int = 1, complexity_measure='max_rules', *args, **kwargs): self.desired_complexity = desired_complexity self.estimator_ = estimator_ self.alpha = 0.0 self.complexity_measure = complexity_measure #: __init__ takes *args/**kwargs, which sklearn's introspection rejects, #: so the parameters are spelled out here instead _PARAM_NAMES = ("estimator_", "desired_complexity", "complexity_measure") def get_params(self, deep=True): return explicit_get_params(self, self._PARAM_NAMES, deep=deep) def set_params(self, **params): return explicit_set_params(self, self._PARAM_NAMES, **params) def _copy_fitted_attributes(self): """Mirror the wrapped estimator's fitted sklearn attributes onto self.""" for attr in ("n_features_in_", "feature_names_in_"): if hasattr(self.estimator_, attr): setattr(self, attr, getattr(self.estimator_, attr)) def _get_alpha(self, X, y, sample_weight=None): path = self.estimator_.cost_complexity_pruning_path( X, y, sample_weight=sample_weight) ccp_alphas = path.ccp_alphas low = 0 high = len(ccp_alphas) - 1 cur = 0 while low <= high: cur = (high + low) // 2 est_params = self.estimator_.get_params() est_params['ccp_alpha'] = ccp_alphas[cur] copied_estimator = deepcopy(self.estimator_).set_params(**est_params) copied_estimator.fit(X, y, sample_weight=sample_weight) if self._get_complexity(copied_estimator, self.complexity_measure) < self.desired_complexity: high = cur - 1 elif self._get_complexity(copied_estimator, self.complexity_measure) > self.desired_complexity: low = cur + 1 else: break self.alpha = ccp_alphas[cur] # path = self.estimator_.cost_complexity_pruning_path(X,y) # ccp_alphas, impurities = path.ccp_alphas, path.impurities # complexities = {} # for alpha in ccp_alphas: # est_params = self.estimator_.get_params() # est_params['ccp_alpha'] = alpha # copied_estimator = deepcopy(self.estimator_).set_params(**est_params) # copied_estimator.fit(X, y) # complexities[alpha] = self._get_complexity(copied_estimator,self.complexity_measure) # closest_alpha, closest_leaves = min(complexities.items(), key=lambda x: abs(self.desired_complexity - x[1])) # self.alpha = closest_alpha def fit(self, X, y, sample_weight=None): # fit a copy (see DecisionTreeCCPClassifier.fit) self.estimator_ = deepcopy(self.estimator_) params_for_fitting = self.estimator_.get_params() self._get_alpha(X, y, sample_weight) params_for_fitting['ccp_alpha'] = self.alpha self.estimator_.set_params(**params_for_fitting) self.estimator_.fit(X, y, sample_weight=sample_weight) self._copy_fitted_attributes() return self @property def feature_importances_(self): """Mean decrease in impurity of the pruned tree, as in sklearn.""" return self.estimator_.feature_importances_ def _get_complexity(self, BaseEstimator, complexity_measure): return compute_tree_complexity(BaseEstimator.tree_, self.complexity_measure) def predict(self, X, *args, **kwargs): return self.estimator_.predict(X, *args, **kwargs) def score(self, X, y, *args, **kwargs): if hasattr(self.estimator_, 'score'): return self.estimator_.score(X, y, *args, **kwargs) else: return NotImplementedAncestors
- RuleInspectionMixin
- RulesMixin
- LeavesMixin
- sklearn.base.BaseEstimator
- sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
- sklearn.utils._metadata_requests._MetadataRequester
Instance variables
var feature_importances_-
Mean decrease in impurity of the pruned tree, as in sklearn.
Expand source code
@property def feature_importances_(self): """Mean decrease in impurity of the pruned tree, as in sklearn.""" return self.estimator_.feature_importances_
Methods
def fit(self, X, y, sample_weight=None)-
Expand source code
def fit(self, X, y, sample_weight=None): # fit a copy (see DecisionTreeCCPClassifier.fit) self.estimator_ = deepcopy(self.estimator_) params_for_fitting = self.estimator_.get_params() self._get_alpha(X, y, sample_weight) params_for_fitting['ccp_alpha'] = self.alpha self.estimator_.set_params(**params_for_fitting) self.estimator_.fit(X, y, sample_weight=sample_weight) self._copy_fitted_attributes() return self def get_params(self, deep=True)-
Get parameters for this estimator.
Parameters
deep:bool, default=True- If True, will return the parameters for this estimator and contained subobjects that are estimators.
Returns
params:dict- Parameter names mapped to their values.
Expand source code
def get_params(self, deep=True): return explicit_get_params(self, self._PARAM_NAMES, deep=deep) def predict(self, X, *args, **kwargs)-
Expand source code
def predict(self, X, *args, **kwargs): return self.estimator_.predict(X, *args, **kwargs) def score(self, X, y, *args, **kwargs)-
Expand source code
def score(self, X, y, *args, **kwargs): if hasattr(self.estimator_, 'score'): return self.estimator_.score(X, y, *args, **kwargs) else: return NotImplemented def set_fit_request(self: DecisionTreeCCPRegressor, *, sample_weight: bool | str | None = '$UNCHANGED$') ‑> DecisionTreeCCPRegressor-
Request metadata passed to the
fitmethod.Note that this method is only relevant if
enable_metadata_routing=True(see :func:sklearn.set_config). Please see :ref:User Guide <metadata_routing>on how the routing mechanism works.The options for each parameter are:
-
True: metadata is requested, and passed tofitif provided. The request is ignored if metadata is not provided. -
False: metadata is not requested and the meta-estimator will not pass it tofit. -
None: metadata is not requested, and the meta-estimator will raise an error if the user provides it. -
str: metadata should be passed to the meta-estimator with this given alias instead of the original name.
The default (
sklearn.utils.metadata_routing.UNCHANGED) retains the existing request. This allows you to change the request for some parameters and not others.Added in version: 1.3
Note
This method is only relevant if this estimator is used as a sub-estimator of a meta-estimator, e.g. used inside a :class:
~sklearn.pipeline.Pipeline. Otherwise it has no effect.Parameters
sample_weight:str, True, False,orNone, default=sklearn.utils.metadata_routing.UNCHANGED- Metadata routing for
sample_weightparameter infit.
Returns
self:object- The updated object.
Expand source code
def func(*args, **kw): """Updates the request for provided parameters This docstring is overwritten below. See REQUESTER_DOC for expected functionality """ if not _routing_enabled(): raise RuntimeError( "This method is only available when metadata routing is enabled." " You can enable it using" " sklearn.set_config(enable_metadata_routing=True)." ) if self.validate_keys and (set(kw) - set(self.keys)): raise TypeError( f"Unexpected args: {set(kw) - set(self.keys)} in {self.name}. " f"Accepted arguments are: {set(self.keys)}" ) # This makes it possible to use the decorated method as an unbound method, # for instance when monkeypatching. # https://github.com/scikit-learn/scikit-learn/issues/28632 if instance is None: _instance = args[0] args = args[1:] else: _instance = instance # Replicating python's behavior when positional args are given other than # `self`, and `self` is only allowed if this method is unbound. if args: raise TypeError( f"set_{self.name}_request() takes 0 positional argument but" f" {len(args)} were given" ) requests = _instance._get_metadata_request() method_metadata_request = getattr(requests, self.name) for prop, alias in kw.items(): if alias is not UNCHANGED: method_metadata_request.add_request(param=prop, alias=alias) _instance._metadata_request = requests return _instance -
def set_params(self, **params)-
Set the parameters of this estimator.
The method works on simple estimators as well as on nested objects (such as :class:
~sklearn.pipeline.Pipeline). The latter have parameters of the form<component>__<parameter>so that it's possible to update each component of a nested object.Parameters
**params:dict- Estimator parameters.
Returns
self:estimator instance- Estimator instance.
Expand source code
def set_params(self, **params): return explicit_set_params(self, self._PARAM_NAMES, **params)
Inherited members
class HSDecisionTreeCCPClassifierCV (estimator_: sklearn.base.BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500], desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs)-
Hierarchical shrinkage: post-hoc regularization for any decision tree or tree ensemble.
SHAP values
shap.TreeExplainerdispatches on the model class, so it does not recognize this wrapper. Pass the shrunk estimator it wraps:import shap model = HSTreeClassifier(...).fit(X, y) explainer = shap.TreeExplainer(model.estimator_) # not model itself shap_values = explainer.shap_values(X)Shrinkage rewrites the node values of that tree in place, so the explainer sees the shrunk model: the SHAP values differ from the unshrunk tree's and sum, with the expected value, to
model.predict_proba(X). This reproduces the SHAP summary plots in the paper.Expand source code
class HSDecisionTreeCCPClassifierCV(HSTreeClassifier): def __init__(self, estimator_: BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500], desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs): super().__init__(estimator_=estimator_, reg_param=None) self.reg_param_list = np.array(reg_param_list) self.cv = cv self.scoring = scoring self.desired_complexity = desired_complexity def fit(self, X, y, sample_weight=None, *args, **kwargs): m = DecisionTreeCCPClassifier(self.estimator_, desired_complexity=self.desired_complexity) m.fit(X, y, sample_weight, *args, **kwargs) self.scores_ = [] for reg_param in self.reg_param_list: est = HSTreeClassifier(deepcopy(m.estimator_), reg_param) cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring) self.scores_.append(np.mean(cv_scores)) self.reg_param = self.reg_param_list[np.argmax(self.scores_)] return super().fit(X=X, y=y)Ancestors
- HSTreeClassifier
- sklearn.base.ClassifierMixin
- HSTree
- RuleInspectionMixin
- RulesMixin
- LeavesMixin
- sklearn.base.BaseEstimator
- sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
- sklearn.utils._metadata_requests._MetadataRequester
Methods
def fit(self, X, y, sample_weight=None, *args, **kwargs)-
Expand source code
def fit(self, X, y, sample_weight=None, *args, **kwargs): m = DecisionTreeCCPClassifier(self.estimator_, desired_complexity=self.desired_complexity) m.fit(X, y, sample_weight, *args, **kwargs) self.scores_ = [] for reg_param in self.reg_param_list: est = HSTreeClassifier(deepcopy(m.estimator_), reg_param) cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring) self.scores_.append(np.mean(cv_scores)) self.reg_param = self.reg_param_list[np.argmax(self.scores_)] return super().fit(X=X, y=y)
Inherited members
class HSDecisionTreeCCPRegressorCV (estimator_: sklearn.base.BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500], desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs)-
Hierarchical shrinkage: post-hoc regularization for any decision tree or tree ensemble.
SHAP values
shap.TreeExplainerdispatches on the model class, so it does not recognize this wrapper. Pass the shrunk estimator it wraps:import shap model = HSTreeRegressor(...).fit(X, y) explainer = shap.TreeExplainer(model.estimator_) # not model itself shap_values = explainer.shap_values(X)Shrinkage rewrites the node values of that tree in place, so the explainer sees the shrunk model: the SHAP values differ from the unshrunk tree's and sum, with the expected value, to
model.predict(X). This reproduces the SHAP summary plots in the paper.Expand source code
class HSDecisionTreeCCPRegressorCV(HSTreeRegressor): def __init__(self, estimator_: BaseEstimator, reg_param_list: List[float] = [0.1, 1, 10, 50, 100, 500], desired_complexity: int = 1, cv: int = 3, scoring=None, *args, **kwargs): super().__init__(estimator_=estimator_, reg_param=None) self.reg_param_list = np.array(reg_param_list) self.cv = cv self.scoring = scoring self.desired_complexity = desired_complexity def fit(self, X, y, sample_weight=None, *args, **kwargs): m = DecisionTreeCCPRegressor(self.estimator_, desired_complexity=self.desired_complexity) m.fit(X, y, sample_weight, *args, **kwargs) self.scores_ = [] for reg_param in self.reg_param_list: est = HSTreeRegressor(deepcopy(m.estimator_), reg_param) cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring) self.scores_.append(np.mean(cv_scores)) self.reg_param = self.reg_param_list[np.argmax(self.scores_)] return super().fit(X=X, y=y)Ancestors
- HSTreeRegressor
- sklearn.base.RegressorMixin
- HSTree
- RuleInspectionMixin
- RulesMixin
- LeavesMixin
- sklearn.base.BaseEstimator
- sklearn.utils._estimator_html_repr._HTMLDocumentationLinkMixin
- sklearn.utils._metadata_requests._MetadataRequester
Methods
def fit(self, X, y, sample_weight=None, *args, **kwargs)-
Expand source code
def fit(self, X, y, sample_weight=None, *args, **kwargs): m = DecisionTreeCCPRegressor(self.estimator_, desired_complexity=self.desired_complexity) m.fit(X, y, sample_weight, *args, **kwargs) self.scores_ = [] for reg_param in self.reg_param_list: est = HSTreeRegressor(deepcopy(m.estimator_), reg_param) cv_scores = cross_val_score(est, X, y, cv=self.cv, scoring=self.scoring) self.scores_.append(np.mean(cv_scores)) self.reg_param = self.reg_param_list[np.argmax(self.scores_)] return super().fit(X=X, y=y)
Inherited members