Repository: scikit-learn/scikit-learn
File: sklearn/metrics/_regression.py (Python)

Code before:
    )
    _, y_true, y_pred, sample_weight, multioutput = (
        _check_reg_targets_with_floating_dtype(
            y_true, y_pred, sample_weight, multioutput, xp=xp
        )
    )

    if _num_samples(y_pred) < 2:
        msg = "D^2 score is not well-defined with less than two samples."
        warnings.warn(msg, UndefinedMetricWarning)
        return float("nan")

    numerator = mean_pinball_loss(
        y_true,
        y_pred,
        sample_weight=sample_weight,
        alpha=alpha,
        multioutput="raw_values",
    )

    if sample_weight is None:
        sample_weight = xp.ones([y_true.shape[0]], dtype=y_pred.dtype, device=device_)

    y_quantile = xp.tile(
        _weighted_percentile(
            y_true, sample_weight=sample_weight, percentile_rank=alpha * 100, xp=xp
        ),
        (y_true.shape[0], 1),
    )

    denominator = mean_pinball_loss(
        y_true,
        y_quantile,
        sample_weight=sample_weight,
        alpha=alpha,
        multioutput="raw_values",
    )

    return _assemble_r2_explained_variance(
        numerator=numerator,
        denominator=denominator,
        n_outputs=y_true.shape[1],
        multioutput=multioutput,
        force_finite=True,
        xp=xp,
        device=device_,
    )


@validate_params(
    {

Review comment (suggestion):
Let's sync this branch with `main` and pass `average=True` to this call to ensure symmetric quantile computation. This might fix discrepancies between the weighted and unweighted cases.

Code after:
    >>> d2_pinball_score(y_true, y_pred)
    0.5
    >>> d2_pinball_score(y_true, y_pred, alpha=0.9)
    0.666...
    >>> d2_pinball_score(y_true, y_pred, alpha=0.1)
    -1.999...
    >>> d2_pinball_score(y_true, y_true, alpha=0.1)
    1.0

    Creating a scorer object with :func:`~sklearn.metrics.make_scorer`:

    >>> import numpy as np
    >>> from sklearn.metrics import make_scorer
    >>> from sklearn.model_selection import GridSearchCV
    >>> from sklearn.linear_model import QuantileRegressor
    >>> X = np.array([[1], [2], [3], [4]])
    >>> y = np.array([2.5, 0.0, 2, 8])
    >>> pinball_95_scorer = make_scorer(d2_pinball_score, alpha=0.95)
    >>> grid = GridSearchCV(
    ...     QuantileRegressor(quantile=0.95),
    ...     param_grid={"fit_intercept": [True, False]},
    ...     scoring=pinball_95_scorer,
    ...     cv=2,
    ... ).fit(X, y)
    >>> grid.best_params_
    {'fit_intercept': True}
    """
    xp, _, device_ = get_namespace_and_device(
        y_true, y_pred, sample_weight, multioutput
    )
    _, y_true, y_pred, sample_weight, multioutput = (
        _check_reg_targets_with_floating_dtype(
            y_true, y_pred, sample_weight, multioutput, xp=xp
        )
    )

    if _num_samples(y_pred) < 2:
        msg = "D^2 score is not well-defined with less than two samples."
        warnings.warn(msg, UndefinedMetricWarning)
        return float("nan")

    numerator = mean_pinball_loss(
        y_true,
        y_pred,
        sample_weight=sample_weight,
        alpha=alpha,
        multioutput="raw_values",
    )

    if sample_weight is None:
        sample_weight = xp.ones([y_true.shape[0]], dtype=y_true.dtype, device=device_)