task: metadata: install: pip install -e './skore[test,sphinx]'
patch: diff --git a/skore/src/skore/sklearn/_estimator/metrics_accessor.py b/skore/src/skore/sklearn/_estimator/metrics_accessor.py
index 0c2841b..934fc4f 100644
--- a/skore/src/skore/sklearn/_estimator/metrics_accessor.py
+++ b/skore/src/skore/sklearn/_estimator/metrics_accessor.py
@@ -52,6 +52,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         X=None,
         y=None,
         scoring=None,
+        scoring_names=None,
         pos_label=None,
         scoring_kwargs=None,
     ):
@@ -83,6 +84,10 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             same parameter name with different values), you can use scikit-learn scorers
             as provided by :func:`sklearn.metrics.make_scorer`.
 
+        scoring_names : list of str, default=None
+            Used to overwrite the default scoring names in the report. It should be of
+            the same length as the `scoring` parameter.
+
         pos_label : int, float, bool or str, default=None
             The positive class.
 
@@ -126,6 +131,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         else:
             data_source_hash = None
 
+        scoring_was_none = scoring is None
         if scoring is None:
             # Equivalent to _get_scorers_to_add
             if self._parent._ml_task == "binary-classification":
@@ -139,9 +145,26 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             else:
                 scoring = ["_r2", "_rmse"]
 
-        scores = []
+        if scoring_names is not None and len(scoring_names) != len(scoring):
+            if scoring_was_none:
+                # we raise a better error message since we decide the default scores
+                raise ValueError(
+                    "The `scoring_names` parameter should be of the same length as "
+                    "the `scoring` parameter. In your case, `scoring` was set to None "
+                    f"and you are using our default scores that are {len(scoring)}. "
+                    "The list is the following: {scoring}."
+                )
+            else:
+                raise ValueError(
+                    "The `scoring_names` parameter should be of the same length as "
+                    f"the `scoring` parameter. Got {len(scoring_names)} names for "
+                    f"{len(scoring)} scoring functions."
+                )
+        elif scoring_names is None:
+            scoring_names = [None] * len(scoring)
 
-        for metric in scoring:
+        scores = []
+        for metric_name, metric in zip(scoring_names, scoring):
             # NOTE: we have to check specifically for `_BaseScorer` first because this
             # is also a callable but it has a special private API that we can leverage
             if isinstance(metric, _BaseScorer):
@@ -210,6 +233,8 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
                     f"Invalid type of metric: {type(metric)} for {metric!r}"
                 )
 
+            if metric_name is not None:
+                metrics_kwargs["metric_name"] = metric_name
             scores.append(
                 metric_fn(
                     data_source=data_source,
@@ -387,13 +412,26 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         """
         return self._accuracy(data_source=data_source, data_source_hash=None, X=X, y=y)
 
-    def _accuracy(self, *, data_source="test", data_source_hash=None, X=None, y=None):
+    def _accuracy(
+        self,
+        *,
+        data_source="test",
+        data_source_hash=None,
+        X=None,
+        y=None,
+        metric_name=None,
+    ):
         """Private interface of `accuracy` to be able to pass `data_source_hash`.
 
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
+        if metric_name is None:
+            metric_name = f"Accuracy {self._SCORE_OR_LOSS_ICONS['accuracy']}"
+
         return self._compute_metric_scores(
             metrics.accuracy_score,
             X=X,
@@ -401,7 +439,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method="predict",
-            metric_name=f"Accuracy {self._SCORE_OR_LOSS_ICONS['accuracy']}",
+            metric_name=metric_name,
         )
 
     @available_if(
@@ -501,6 +539,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
         average=None,
         pos_label=None,
     ):
@@ -509,12 +548,17 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
         if self._parent._ml_task == "binary-classification" and pos_label is not None:
             # if `pos_label` is specified by our user, then we can safely report only
             # the statistics of the positive class
             average = "binary"
 
+        if metric_name is None:
+            metric_name = f"Precision {self._SCORE_OR_LOSS_ICONS['precision']}"
+
         return self._compute_metric_scores(
             metrics.precision_score,
             X=X,
@@ -523,7 +567,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source_hash=data_source_hash,
             response_method="predict",
             pos_label=pos_label,
-            metric_name=f"Precision {self._SCORE_OR_LOSS_ICONS['precision']}",
+            metric_name=metric_name,
             average=average,
         )
 
@@ -625,6 +669,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
         average=None,
         pos_label=None,
     ):
@@ -633,12 +678,17 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
         if self._parent._ml_task == "binary-classification" and pos_label is not None:
             # if `pos_label` is specified by our user, then we can safely report only
             # the statistics of the positive class
             average = "binary"
 
+        if metric_name is None:
+            metric_name = f"Recall {self._SCORE_OR_LOSS_ICONS['recall']}"
+
         return self._compute_metric_scores(
             metrics.recall_score,
             X=X,
@@ -647,7 +697,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source_hash=data_source_hash,
             response_method="predict",
             pos_label=pos_label,
-            metric_name=f"Recall {self._SCORE_OR_LOSS_ICONS['recall']}",
+            metric_name=metric_name,
             average=average,
         )
 
@@ -708,18 +758,29 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         )
 
     def _brier_score(
-        self, *, data_source="test", data_source_hash=None, X=None, y=None
+        self,
+        *,
+        data_source="test",
+        data_source_hash=None,
+        X=None,
+        y=None,
+        metric_name=None,
     ):
         """Private interface of `brier_score` to be able to pass `data_source_hash`.
 
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
         # The Brier score in scikit-learn request `pos_label` to ensure that the
         # integral encoding of `y_true` corresponds to the probabilities of the
         # `pos_label`. Since we get the predictions with `get_response_method`, we
         # can pass any `pos_label`, they will lead to the same result.
+        if metric_name is None:
+            metric_name = f"Brier score {self._SCORE_OR_LOSS_ICONS['brier_score']}"
+
         return self._compute_metric_scores(
             metrics.brier_score_loss,
             X=X,
@@ -727,7 +788,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method="predict_proba",
-            metric_name=f"Brier score {self._SCORE_OR_LOSS_ICONS['brier_score']}",
+            metric_name=metric_name,
             pos_label=self._parent._estimator.classes_[-1],
         )
 
@@ -834,6 +895,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
         average=None,
         multi_class="ovr",
     ):
@@ -842,7 +904,12 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
+        if metric_name is None:
+            metric_name = f"ROC AUC {self._SCORE_OR_LOSS_ICONS['roc_auc']}"
+
         return self._compute_metric_scores(
             metrics.roc_auc_score,
             X=X,
@@ -850,7 +917,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method=["predict_proba", "decision_function"],
-            metric_name=f"ROC AUC {self._SCORE_OR_LOSS_ICONS['roc_auc']}",
+            metric_name=metric_name,
             average=average,
             multi_class=multi_class,
         )
@@ -913,13 +980,19 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
     ):
         """Private interface of `log_loss` to be able to pass `data_source_hash`.
 
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
+        if metric_name is None:
+            metric_name = f"Log loss {self._SCORE_OR_LOSS_ICONS['log_loss']}"
+
         return self._compute_metric_scores(
             metrics.log_loss,
             X=X,
@@ -927,7 +1000,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method="predict_proba",
-            metric_name=f"Log loss {self._SCORE_OR_LOSS_ICONS['log_loss']}",
+            metric_name=metric_name,
         )
 
     @available_if(_check_supported_ml_task(supported_ml_tasks=["regression"]))
@@ -1002,6 +1075,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
         multioutput="raw_values",
     ):
         """Private interface of `r2` to be able to pass `data_source_hash`.
@@ -1009,7 +1083,12 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
+        if metric_name is None:
+            metric_name = f"R² {self._SCORE_OR_LOSS_ICONS['r2']}"
+
         return self._compute_metric_scores(
             metrics.r2_score,
             X=X,
@@ -1017,7 +1096,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method="predict",
-            metric_name=f"R² {self._SCORE_OR_LOSS_ICONS['r2']}",
+            metric_name=metric_name,
             multioutput=multioutput,
         )
 
@@ -1093,6 +1172,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         data_source_hash=None,
         X=None,
         y=None,
+        metric_name=None,
         multioutput="raw_values",
     ):
         """Private interface of `rmse` to be able to pass `data_source_hash`.
@@ -1100,7 +1180,12 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
         `data_source_hash` is either an `int` when we already computed the hash
         and are able to pass it around or `None` and thus trigger its computation
         in the underlying process.
+
+        `metric_name` allows to overwrite the default name of the metric in the report.
         """
+        if metric_name is None:
+            metric_name = f"RMSE {self._SCORE_OR_LOSS_ICONS['rmse']}"
+
         return self._compute_metric_scores(
             metrics.root_mean_squared_error,
             X=X,
@@ -1108,7 +1193,7 @@ class _MetricsAccessor(_BaseAccessor, DirNamesMixin):
             data_source=data_source,
             data_source_hash=data_source_hash,
             response_method="predict",
-            metric_name=f"RMSE {self._SCORE_OR_LOSS_ICONS['rmse']}",
+            metric_name=metric_name,
             multioutput=multioutput,
         )
 

problem_statement: Add a parameter `scoring_names` to `EstimatorReport.report_metrics` to overwrite the default name
Currently there is no way to overwrite the names of score in `report_metrics` and it could annyoing when you pass your own score because they are inferred from the function names.

Instead we should provide a way to pass a list of strings to start that should overwrite the score of the columns in the resulting dataframe.
repo: probabl-ai/skore
test_cmd: pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
test_patch: diff --git a/skore/tests/unit/sklearn/test_estimator.py b/skore/tests/unit/sklearn/test_estimator.py
index bd8336d..55adf53 100644
--- a/skore/tests/unit/sklearn/test_estimator.py
+++ b/skore/tests/unit/sklearn/test_estimator.py
@@ -694,6 +694,46 @@ def test_estimator_report_report_metrics_scoring_kwargs(
     assert result.columns.names == ["Metric", "Class label"]
 
 
+@pytest.mark.parametrize(
+    "fixture_name, scoring_names, expected_columns",
+    [
+        ("regression_data", ["R2", "RMSE"], ["R2", "RMSE"]),
+        (
+            "multiclass_classification_data",
+            ["Precision", "Recall", "ROC AUC", "Log Loss"],
+            [
+                "Precision",
+                "Precision",
+                "Precision",
+                "Recall",
+                "Recall",
+                "Recall",
+                "ROC AUC",
+                "ROC AUC",
+                "ROC AUC",
+                "Log Loss",
+            ],
+        ),
+    ],
+)
+def test_estimator_report_report_metrics_overwrite_scoring_names(
+    request, fixture_name, scoring_names, expected_columns
+):
+    """Test that we can overwrite the scoring names in report_metrics."""
+    estimator, X_test, y_test = request.getfixturevalue(fixture_name)
+    report = EstimatorReport(estimator, X_test=X_test, y_test=y_test)
+    result = report.metrics.report_metrics(scoring_names=scoring_names)
+    assert result.shape == (1, len(expected_columns))
+
+    # Get level 0 names if MultiIndex, otherwise get column names
+    result_columns = (
+        result.columns.get_level_values(0).tolist()
+        if isinstance(result.columns, pd.MultiIndex)
+        else result.columns.tolist()
+    )
+    assert result_columns == expected_columns
+
+
 def test_estimator_report_interaction_cache_metrics(regression_multioutput_data):
     """Check that the cache take into account the 'kwargs' of a metric."""
     estimator, X_test, y_test = regression_multioutput_data