ROC-AUC после дублирования классов
Есть бинарные метки и score классификатора.
Каждый positive объект дублируется positive_copies раз, каждый negative объект дублируется negative_copies раз. Score у копий такой же, как у исходного объекта.
Нужно вернуть ROC-AUC на выборке после такого дублирования, не материализуя все копии.
Считайте ROC-AUC как долю positive-negative пар, где score positive выше score negative. Tie дает 0.5.
Сигнатура
def roc_auc_after_duplication(
y_true: list[int],
scores: list[float],
positive_copies: int,
negative_copies: int,
) -> float:Решение прямо на странице
Напишите код, запустите проверки и только потом открывайте разбор.
Нажмите «Запустить проверки» или Ctrl+Enter.
Показать разбор
Подсказки
- Pairwise view
ROC-AUC считайте через пары positive-negative, а не через пороговую кривую.
- Множители сокращаются
Каждая исходная пара после дублирования появляется positive_copies * negative_copies раз.
Идея решения
ROC-AUC можно понимать как вероятность, что случайный positive объект получит score выше случайного negative.
Если каждый positive продублировать a раз, а каждый negative b раз, каждая исходная positive-negative пара повторится a * b раз. Числитель и знаменатель ROC-AUC умножатся на один и тот же коэффициент, поэтому значение не изменится.
Эталонный код
def roc_auc_after_duplication(
y_true: list[int],
scores: list[float],
positive_copies: int,
negative_copies: int,
) -> float:
if len(y_true) != len(scores):
raise ValueError('y_true and scores must have the same length')
if positive_copies <= 0 or negative_copies <= 0:
raise ValueError('copy counts must be positive')
positive_scores = []
negative_scores = []
for label, score in zip(y_true, scores):
if label == 1:
positive_scores.append(score)
elif label == 0:
negative_scores.append(score)
else:
raise ValueError('labels must be 0 or 1')
if not positive_scores or not negative_scores:
raise ValueError('both classes are required')
wins = 0.0
for positive_score in positive_scores:
for negative_score in negative_scores:
if positive_score > negative_score:
wins += 1.0
elif positive_score == negative_score:
wins += 0.5
return wins / (len(positive_scores) * len(negative_scores))