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

Code before:
        )

    xp, _ = get_namespace(y_prob)
    if xp.max(y_prob) > 1:
        raise ValueError(f"y_prob contains values greater than 1: {xp.max(y_prob)}")
    if xp.min(y_prob) < 0:
        raise ValueError(f"y_prob contains values less than 0: {xp.min(y_prob)}")

    # check that pos_label is consistent with y_true
    try:
        pos_label = _check_pos_label_consistency(pos_label, y_true)
    except ValueError:
        classes = np.unique(y_true)
        if classes.dtype.kind not in ("O", "U", "S"):
            # for backward compatibility, if classes are not string then
            # `pos_label` will correspond to the greater label
            pos_label = classes[-1]
        else:
            raise

    xp_y_true, _ = get_namespace(y_true)
    # convert (n_samples,) to (n_samples, 2) shape
    y_true = xp_y_true.asarray(y_true == pos_label, dtype=xp_y_true.int64)
    transformed_labels = xp_y_true.stack((1 - y_true, y_true), axis=1)
    y_prob = xp.stack((1 - y_prob, y_prob), axis=1)
    transformed_labels = ensure_common_namespace_device(y_prob, transformed_labels)[0]

    return transformed_labels, y_prob


@validate_params(
    {
        "y_true": ["array-like"],
        "y_proba": ["array-like"],
        "sample_weight": ["array-like", None],
        "pos_label": [Real, str, "boolean", None],
        "labels": ["array-like", None],
        "scale_by_half": ["boolean", StrOptions({"auto"})],
    },
    prefer_skip_nested_validation=True,
)
def brier_score_loss(
    y_true,
    y_proba,
    *,
    sample_weight=None,
    pos_label=None,
    labels=None,
    scale_by_half="auto",
):
    r"""Compute the Brier score loss.

Review comment (suggestion):
We can probably move to the y_prob device a bit earlier:

```suggestion
    y_true_pos = xp_y_true.asarray(y_true == pos_label, dtype=xp_y_true.int64)
    y_true_pos, = ensure_common_namespace_device(y_prob, y_true_pos)
    transformed_labels = xp.stack((1 - y_true_pos, y_true_pos), axis=1)
    y_prob = xp.stack((1 - y_prob, y_prob), axis=1)
```

Code after:
    pos_label : int, float, bool or str, default=None
        Label of the positive class. If None, `pos_label` will be inferred
        in the following manner:

        * if `y_true` in {-1, 1} or {0, 1}, `pos_label` defaults to 1;
        * else if `y_true` contains string, an error will be raised and
          `pos_label` should be explicitly specified;
        * otherwise, `pos_label` defaults to the greater label,
          i.e. `np.unique(y_true)[-1]`.

    Returns
    -------
    transformed_labels : array of shape (n_samples, 2)

    y_prob : array of shape (n_samples, 2)
    """
    # sanity checks on y_true and y_prob
    y_true = column_or_1d(y_true)
    y_prob = column_or_1d(y_prob)

    assert_all_finite(y_true)
    assert_all_finite(y_prob)

    check_consistent_length(y_prob, y_true, sample_weight)
    if sample_weight is not None:
        _check_sample_weight(sample_weight, y_prob, force_float_dtype=False)

    y_type = type_of_target(y_true, input_name="y_true")
    if y_type != "binary":
        raise ValueError(
            f"The type of the target inferred from y_true is {y_type} but should be "
            "binary according to the shape of y_prob."
        )

    xp, _, device_ = get_namespace_and_device(y_prob)
    if xp.max(y_prob) > 1:
        raise ValueError(f"y_prob contains values greater than 1: {xp.max(y_prob)}")
    if xp.min(y_prob) < 0:
        raise ValueError(f"y_prob contains values less than 0: {xp.min(y_prob)}")

    # check that pos_label is consistent with y_true
    try:
        pos_label = _check_pos_label_consistency(pos_label, y_true)
    except ValueError:
        classes = np.unique(y_true)
        if classes.dtype.kind not in ("O", "U", "S"):
            # for backward compatibility, if classes are not string then
            # `pos_label` will correspond to the greater label
            pos_label = classes[-1]
        else:
            raise