Вам передаётся сбалансированный набор объектов из 3-х различных классов (например, "cat", "dog", "bird").
Каждый объект представлен вектором признаков (list of floats). Один объект — подброшенный и не принадлежит ни одному из трёх известных классов: он отличается распределением.
Ваша задача — реализовать функцию
detect_outlier(data: List[List[float]], labels: List[str]) -> int, которая возвращает индекс выброса в списке. Гарантируется, что в labels один лейбл может быть "unknown", а может и вовсе отсутствовать — выброс может быть замаскирован.Метрика — средняя евклидова дистанция до других представителей того же класса должна быть на порядок выше, чем у нормальных объектов.
Пример данных:
data = [
[1.1, 0.9], [1.2, 1.0], [0.9, 1.1], # cat
[3.0, 3.1], [2.9, 3.0], [3.1, 2.9], # dog
[5.0, 5.1], [5.1, 5.0], [8.0, 8.0], # last — выброс
]
labels = ["cat", "cat", "cat", "dog", "dog", "dog", "bird", "bird", "bird"]
Решение задачи🔽
import numpy as np
from collections import defaultdict
def detect_outlier(data, labels):
data = np.array(data)
grouped = defaultdict(list)
for i, label in enumerate(labels):
grouped[label].append(i)
outlier_idx = -1
max_score = -1
for i, point in enumerate(data):
label = labels[i]
others = [j for j in grouped[label] if j != i]
if not others:
continue
distances = [np.linalg.norm(point - data[j]) for j in others]
avg_dist = np.mean(distances)
if avg_dist > max_score:
max_score = avg_dist
outlier_idx = i
return outlier_idx
# Тест
print(detect_outlier(data, labels)) # ➜ 8