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

Code before:

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

    y_prob : array of shape (n_samples, n_classes)
    """
    xp, _, device_ = get_namespace_and_device(y_prob)
    xp_y_true, is_y_true_array_api = get_namespace(y_true)

    y_prob = check_array(
        y_prob, ensure_2d=False, dtype=supported_float_dtypes(xp, device=device_)
    )

    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 lower than 0: {xp.min(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)

    lb = LabelBinarizer()
    if is_y_true_array_api:
        y_true = _convert_to_numpy(y_true, xp=xp_y_true)

    if labels is not None:
        lb = lb.fit(labels)
        # LabelBinarizer does not respect the order implied by labels, which
        # can be misleading.
        if not np.all(lb.classes_ == labels):
            warnings.warn(
                f"Labels passed were {labels}. But this function "
                "assumes labels are ordered lexicographically. "
                f"Pass the ordered labels={lb.classes_.tolist()} and ensure that "
                "the columns of y_prob correspond to this ordering.",
                UserWarning,
            )
        if not np.isin(y_true, labels).all():
            undeclared_labels = set(y_true) - set(labels)
            raise ValueError(
                f"y_true contains values {undeclared_labels} not belonging "
                f"to the passed labels {labels}."
            )

    else:
        lb = lb.fit(y_true)

    if len(lb.classes_) == 1:
        if labels is None:

Review comment (suggestion):
```suggestion
    # For classification metrics, both array API compatible and non array
    # API compatible inputs are allowed for y_true: in particular arrays
    # that store class labels as Python string with an object dtype cannot
    # be represented with non-NumPy namespaces. To avoid having to maintain
    # two code branches, we always convert y_true to NumPy and move the
    # integer encoded output of LabelBinarizer.transform back to the y_prob
    # namespace, irrespective of the original y_true namespace.
    if is_y_true_array_api:
        y_true = _convert_to_numpy(y_true, xp=xp_y_true)
```

Code after:
            undeclared_labels = set(y_true) - set(labels)
            raise ValueError(
                f"y_true contains values {undeclared_labels} not belonging "
                f"to the passed labels {labels}."
            )

    else:
        lb = lb.fit(y_true)

    if len(lb.classes_) == 1:
        if labels is None:
            raise ValueError(
                "y_true contains only one label ({0}). Please "
                "provide the list of all expected class labels explicitly through the "
                "labels argument.".format(lb.classes_[0])
            )
        else:
            raise ValueError(
                "The labels array needs to contain at least two "
                "labels, got {0}.".format(lb.classes_)
            )

    transformed_labels = lb.transform(y_true)
    transformed_labels = target_xp.asarray(transformed_labels, device=target_device)
    if transformed_labels.shape[1] == 1:
        transformed_labels = target_xp.concat(
            (1 - transformed_labels, transformed_labels), axis=1
        )
    return transformed_labels, lb.classes_


def _validate_multiclass_probabilistic_prediction(
    y_true, y_prob, sample_weight, labels
):
    r"""Convert y_true and y_prob to shape (n_samples, n_classes)

    1. Verify that y_true, y_prob, and sample_weights have the same first dim
    2. Ensure 2 or more classes in y_true i.e. valid classification task. The
       classes are provided by the labels argument, or inferred using y_true.
       When inferring y_true is assumed binary if it has shape (n_samples, ).
    3. Validate y_true, and y_prob have the same number of classes. Convert to
       shape (n_samples, n_classes)

    Parameters
    ----------
    y_true : array-like or label indicator matrix
        Ground truth (correct) labels for n_samples samples.

    y_prob : array-like of float, shape=(n_samples, n_classes) or (n_samples,)
        Predicted probabilities, as returned by a classifier's
        predict_proba method. If `y_prob.shape = (n_samples,)`