Repository: scikit-learn/scikit-learn
File: examples/miscellaneous/plot_metadata_routing.py (Python)

Code before:
# %%
# Note that the above example is calling our utility function
# `check_metadata()` via the `ExampleClassifier`. It checks that
# ``sample_weight`` is correctly passed to it. If it is not, like in the
# following example, it would print that ``sample_weight`` is ``None``:

meta_est.fit(X, y)

# %%
# If we pass an unknown metadata, an error is raised:
try:
    meta_est.fit(X, y, test=my_weights)
except TypeError as e:
    print(e)

# %%
# And if we pass a metadata which is not explicitly requested:
try:
    meta_est.fit(X, y, sample_weight=my_weights).predict(X, groups=my_groups)
except ValueError as e:
    print(e)

# %%
# Also, if we explicitly set it as not requested, but it is provided:
meta_est = MetaClassifier(
    estimator=ExampleClassifier()
    .set_fit_request(sample_weight=True)
    .set_predict_request(groups=False)
)
try:
    meta_est.fit(X, y, sample_weight=my_weights).predict(X[:3, :], groups=my_groups)
except TypeError as e:
    print(e)

# %%
# Another concept to introduce is **aliased metadata**. This is when an
# estimator requests a metadata with a different variable name than the default
# variable name. For instance, in a setting where there are two estimators in a
# pipeline, one could request ``sample_weight1`` and the other
# ``sample_weight2``. Note that this doesn't change what the estimator expects,
# it only tells the meta-estimator how to map the provided metadata to what is
# required. Here's an example, where we pass ``aliased_sample_weight`` to the
# meta-estimator, but the meta-estimator understands that
# ``aliased_sample_weight`` is an alias for ``sample_weight``, and passes it as
# ``sample_weight`` to the underlying estimator:
meta_est = MetaClassifier(
    estimator=ExampleClassifier().set_fit_request(sample_weight="aliased_sample_weight")
)
meta_est.fit(X, y, aliased_sample_weight=my_weights)

# %%

Review comment (suggestion):
For instance at the beginning of the section, we need to explain that third party developer only need to implement `get_metadata_routing` and use `process_routing`, maybe something like:
https://github.com/scikit-learn/scikit-learn/blob/5f32b894190628be14601ee5495cc8971468aba3/examples/miscellaneous/plot_metadata_routing.py#L157-L159
```
# To make the meta-estimator a router, you only need to: 
# 1. define its `get_metadata_routing` method, which returns a `MetadataRouter` instance in charge of configuring the metadata routing
# 2. use `process_routing` inside its methods (`fit`, `predict`, ...) to  properly route the metadata from the meta-estimator to its children.
```

Code after:
        self.classes_ = self.estimator_.classes_
        return self

    def predict(self, X, **predict_params):
        check_is_fitted(self)
        # As in `fit`, we get information on all the metadata that should be routed and
        # pass the metadata that is stored in `routed_params.estimator.predict` to the
        # sub-estimator's predict method.
        routed_params = process_routing(self, "predict", **predict_params)
        return self.estimator_.predict(X, **routed_params.estimator.predict)


# %%
# Let's break down different parts of the above code.
#
# In each method, we use the ``process_routing`` function to construct a
# :class:`~utils.Bunch` of the form ``{"object_name": {"method_name": {"metadata":
# value}}}`` to pass to the underlying estimator's method. The ``object_name``
# (``estimator`` in ``routed_params.estimator.fit``) is the same as the `estimator`
# added in the ``get_metadata_routing``. ``process_routing`` also validates the input
# metadata: it makes sure all given metadata are requested to avoid silent bugs.

# %%
# Next, we illustrate the different behaviors and notably the type of errors
# raised.

meta_est = MetaClassifier(
    estimator=ExampleClassifier().set_fit_request(sample_weight=True)
)
meta_est.fit(X, y, sample_weight=my_weights)

# %%
# Note that the above example is calling our utility function
# `check_metadata()` via the `ExampleClassifier`. It checks that
# ``sample_weight`` is correctly passed to it. If it is not, like in the
# following example, it would print that ``sample_weight`` is ``None``:

meta_est.fit(X, y)

# %%
# If we pass an unknown metadata, an error is raised:
try:
    meta_est.fit(X, y, test=my_weights)
except TypeError as e:
    print(e)

# %%
# And if we pass a metadata which is not explicitly requested:
try:
    meta_est.fit(X, y, sample_weight=my_weights).predict(X, groups=my_groups)
except ValueError as e: