web-dev-qa-db-fra.com

roc_auc_score - Une seule classe présente dans y_true

Je fais un k-fold XV sur une trame de données existante, et j'ai besoin d'obtenir le score AUC. Le problème est - parfois les données de test ne contiennent que des 0 et non des 1!

J'ai essayé d'utiliser l'exemple this , mais avec des nombres différents:

import numpy as np
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 0, 0])
y_scores = np.array([1, 0, 0, 0])
roc_auc_score(y_true, y_scores)

Et je reçois cette exception:

ValueError: une seule classe présente dans y_true. Le score ROC AUC n'est pas défini dans ce cas.

Y a-t-il une solution de contournement qui peut le faire fonctionner dans de tels cas?

10
bloop

Vous pouvez utiliser try-except pour éviter l'erreur:

import numpy as np
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 0, 0])
y_scores = np.array([1, 0, 0, 0])
try:
    roc_auc_score(y_true, y_scores)
except ValueError:
    pass

Maintenant, vous pouvez également définir le roc_auc_score à zéro s'il n'y a qu'une seule classe présente. Cependant, je ne ferais pas ça. Je suppose que vos données de test sont très déséquilibrées. Je suggère d'utiliser à la place un pli en K stratifié pour que vous ayez au moins les deux classes présentes.

13
Dat Tran

Oui, c'est clairement un bug! Votre code est parfaitement correct:

import numpy as np
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 0, 0, 0])
y_scores = np.array([1, 0, 0, 0])
roc_auc_score(y_true, y_scores)

Voici ma "solution"

from sklearn.metrics import roc_auc_score, accuracy_score
def roc_auc_score_FIXED(y_true, y_pred):
    if len(np.unique(y_true)) == 1: # bug in roc_auc_score
        return accuracy_score(y_true, np.rint(y_pred))
    return roc_auc_score(y_true, y_pred)
4
Dmitry Konovalov

Modifiez simplement le code avec 0 à 1 pour le faire fonctionner

import numpy as np
from sklearn.metrics import roc_auc_score
y_true = np.array([0, 1, 0, 0])
y_scores = np.array([1, 0, 0, 0])
roc_auc_score(y_true, y_scores)

Je crois que le message d'erreur a suggéré qu'une seule classe dans y_true (tout zéro), vous devez donner 2 classes dans y_true.

0
Shark Deng

Comme le note l'erreur, si une classe n'est pas présente dans la vérité terrain d'un lot,

Le score ROC AUC n'est pas défini dans ce cas.

Je suis contre le fait de lever une exception (à propos de quoi? C'est le comportement attendu) ou de renvoyer une autre mesure (par exemple, la précision). La métrique n'est pas cassée en soi.

Je n'ai pas envie de résoudre un "problème" de déséquilibre des données avec un "correctif" métrique. Il serait probablement préférable d'utiliser un autre échantillonnage, si possible, ou simplement de joindre plusieurs lots qui satisfont aux exigences de population de la classe.

0
Diego Ferri

Je suis confronté au même problème maintenant et j'utilise try-catch ne résout pas mon problème. J'ai développé le code ci-dessous pour y faire face.

import pandas as pd
import numpy as np

class KFold(object):

    def __init__(self, folds, random_state=None):

        self.folds = folds

        self.random_state = random_state

    def split(self, x, y):

        assert len(x) == len(y), 'x and y should have the same length'

        x_, y_ = pd.DataFrame(x), pd.DataFrame(y)

        y_ = y_.sample(frac=1, random_state=self.random_state)

        x_ = x_.loc[y_.index]

        event_index, non_event_index = list(y_[y == 1].index), list(y_[y == 0].index)

        assert len(event_index) >= self.folds, 'number of folds should be less than the number of rows in x'

        assert len(non_event_index) >= self.folds, 'number of folds should be less than number of rows in y'

        indexes = []

        #
        #
        #
        step = int(np.ceil(len(non_event_index) / self.folds))

        start, end = 0, step

        while start < len(non_event_index):

            train_fold = set(non_event_index[start:end])

            valid_fold = set([k for k in non_event_index if k not in train_fold])

            indexes.append([train_fold, valid_fold])

            start, end = end, min(step + end, len(non_event_index))


        #
        #
        #
        step = int(np.ceil(len(event_index) / self.folds))

        start, end, i = 0, step, 0

        while start < len(event_index):

            train_fold = set(event_index[start:end])

            valid_fold = set([k for k in event_index if k not in train_fold])

            indexes[i][0] = list(indexes[i][0].union(train_fold))

            indexes[i][1] = list(indexes[i][1].union(valid_fold))

            indexes[i] = Tuple(indexes[i])

            start, end, i = end, min(step + end, len(event_index)), i + 1

        return indexes 

Je viens d'écrire ce code et je ne l'ai pas testé de manière exhaustive. Il a été testé uniquement pour les catégories binaires. J'espère que ce sera encore utile.

0