{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "dd36cef2-4feb-44ed-900e-0fa6f75d74bd",
   "metadata": {},
   "source": [
    "**Цель задачи:**  \n",
    "Продемонстрировать применение метода **Comparing anomaly detection algorithms for outlier detection on toy datasets** для обнаружения аномалий в данных на основе их локальной плотности.  \n",
    "\n",
    "1. **Использовать** несколько алгоритмов из scikit-learn (`LocalOutlierFactor, IsolationForest, OneClassSVM, EllipticEnvelope.`).  \n",
    "2. **Протестировать** на двух типах данных:  \n",
    "   - **Сгенерированный датасет**: `make_moons` плюс сгенерированные вбросы. \n",
    "   - **Реальный датасет**: annthyroid (аномалии в медицинских данных) из openml.org.  \n",
    "3. **Сравнить** результаты работы алгоритмов (визуализация границ решений для 2D‑случая, метрики классификации), проанализировать, какие типы выбросов лучше обнаруживает каждый метод, и интерпретировать причины различий.  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "7c2d132b-1312-4bb2-b1f0-050f0876883f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Загрузка датасета Annthyroid (OpenML id 1552)...\n",
      "Размер исходных данных: (1100, 8)\n",
      "Исходная доля выбросов: 0.0000\n",
      "Создано 5 подвыборок с корректными contamination.\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 885396 characters]",
      "text/plain": [
       "<Figure size 1200x1100 with 25 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "===== Усреднённые метрики по 5 подвыборкам =====\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Robust covariance:\n",
      "  Precision (выброс): 0.000 +/- 0.000\n",
      "  Recall (выброс):    0.000 +/- 0.000\n",
      "  F1 (выброс):        0.000 +/- 0.000\n",
      "\n",
      "One-Class SVM:\n",
      "  Precision (выброс): 0.000 +/- 0.000\n",
      "  Recall (выброс):    0.000 +/- 0.000\n",
      "  F1 (выброс):        0.000 +/- 0.000\n",
      "\n",
      "One-Class SVM (SGD):\n",
      "  Precision (выброс): 0.000 +/- 0.000\n",
      "  Recall (выброс):    0.000 +/- 0.000\n",
      "  F1 (выброс):        0.000 +/- 0.000\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Isolation Forest:\n",
      "  Precision (выброс): 0.000 +/- 0.000\n",
      "  Recall (выброс):    0.000 +/- 0.000\n",
      "  F1 (выброс):        0.000 +/- 0.000\n",
      "\n",
      "Local Outlier Factor:\n",
      "  Precision (выброс): 0.000 +/- 0.000\n",
      "  Recall (выброс):    0.000 +/- 0.000\n",
      "  F1 (выброс):        0.000 +/- 0.000\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: F-score is ill-defined and being set to 0.0 due to no true nor predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 due to no predicted samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
      "C:\\Users\\PC\\PycharmProjects\\PythonProject4\\.venv\\Lib\\site-packages\\sklearn\\metrics\\_classification.py:1833: UndefinedMetricWarning: Recall is ill-defined and being set to 0.0 due to no true samples. Use `zero_division` parameter to control this behavior.\n",
      "  _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n"
     ]
    }
   ],
   "source": [
    "# Сравнение алгоритмов обнаружения аномалий на реальном датасете Annthyroid\n",
    "\n",
    "import time\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import openml\n",
    "from sklearn import svm\n",
    "from sklearn.covariance import EllipticEnvelope\n",
    "from sklearn.ensemble import IsolationForest\n",
    "from sklearn.kernel_approximation import Nystroem\n",
    "from sklearn.linear_model import SGDOneClassSVM\n",
    "from sklearn.neighbors import LocalOutlierFactor\n",
    "from sklearn.pipeline import make_pipeline\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "from sklearn.decomposition import PCA\n",
    "from sklearn.utils import resample\n",
    "from sklearn.metrics import classification_report, f1_score, recall_score, precision_score\n",
    "\n",
    "# 1. Загрузка и подготовка исходных данных \n",
    "print(\"Загрузка датасета Annthyroid (OpenML id 1552)...\")\n",
    "dataset = openml.datasets.get_dataset(1552)\n",
    "X_raw, y_raw, _, _ = dataset.get_data(target=dataset.default_target_attribute)\n",
    "\n",
    "# Отбираем только числовые колонки\n",
    "if hasattr(X_raw, 'select_dtypes'):\n",
    "    X_raw = X_raw.select_dtypes(include=[np.number])\n",
    "X = X_raw.values if hasattr(X_raw, 'values') else np.array(X_raw)\n",
    "\n",
    "# Удаление NaN\n",
    "mask = ~np.isnan(X).any(axis=1)\n",
    "X = X[mask]\n",
    "y_raw = y_raw[mask]\n",
    "\n",
    "# Метки: приводим к формату (1 - норма, -1 - выброс)\n",
    "y = np.where(y_raw == 1, -1, 1)\n",
    "\n",
    "print(f\"Размер исходных данных: {X.shape}\")\n",
    "print(f\"Исходная доля выбросов: {(y == -1).mean():.4f}\")\n",
    "\n",
    "#  2. Параметры эксперимента \n",
    "n_algorithms = 5\n",
    "n_datasets = 5\n",
    "sample_fraction = 0.8          # размер подвыборки относительно исходного\n",
    "\n",
    "# 3. Генерация 5 бутстреп-выборок (со стратификацией) \n",
    "np.random.seed(42)\n",
    "datasets_2d = []          # храним (X_2d, y, contamination_actual)\n",
    "for i in range(n_datasets):\n",
    "    X_sample, y_sample = resample(X, y, n_samples=int(sample_fraction * len(X)),\n",
    "                                  stratify=y, random_state=i)\n",
    "    # Вычисляем реальную долю выбросов в этой подвыборке\n",
    "    cont = (y_sample == -1).mean()\n",
    "    # Ограничиваем допустимым диапазоном для всех алгоритмов\n",
    "    cont = np.clip(cont, 1e-6, 0.5)\n",
    "    \n",
    "    # Стандартизация и PCA до 2 компонент\n",
    "    scaler = StandardScaler()\n",
    "    X_scaled = scaler.fit_transform(X_sample)\n",
    "    pca = PCA(n_components=2, random_state=i)\n",
    "    X_2d = pca.fit_transform(X_scaled)\n",
    "    datasets_2d.append((X_2d, y_sample, cont))\n",
    "\n",
    "print(\"Создано 5 подвыборок с корректными contamination.\")\n",
    "\n",
    "# 4. Алгоритмы (динамически подставляем contamination) \n",
    "def get_algorithms(cont):\n",
    "    return [\n",
    "        (\"Robust covariance\", EllipticEnvelope(contamination=cont, random_state=42)),\n",
    "        (\"One-Class SVM\", svm.OneClassSVM(nu=cont, kernel=\"rbf\", gamma=0.1)),\n",
    "        (\"One-Class SVM (SGD)\", make_pipeline(\n",
    "            Nystroem(gamma=0.1, random_state=42, n_components=150),\n",
    "            SGDOneClassSVM(nu=cont, shuffle=True, fit_intercept=True,\n",
    "                           random_state=42, tol=1e-6))),\n",
    "        (\"Isolation Forest\", IsolationForest(contamination=cont, random_state=42)),\n",
    "        (\"Local Outlier Factor\", LocalOutlierFactor(n_neighbors=35, contamination=cont)),\n",
    "    ]\n",
    "\n",
    "#  5. Отрисовка сетки 5×5 \n",
    "plt.figure(figsize=(n_algorithms * 2 + 2, n_datasets * 2 + 1))\n",
    "plt.subplots_adjust(left=0.05, right=0.95, bottom=0.05, top=0.95, wspace=0.1, hspace=0.1)\n",
    "\n",
    "plot_num = 1\n",
    "for i_dataset, (X_2d, y_true, cont) in enumerate(datasets_2d):\n",
    "    # Подготавливаем сетку для контуров\n",
    "    x_min, x_max = X_2d[:, 0].min() - 1, X_2d[:, 0].max() + 1\n",
    "    y_min, y_max = X_2d[:, 1].min() - 1, X_2d[:, 1].max() + 1\n",
    "    xx, yy = np.meshgrid(np.linspace(x_min, x_max, 150),\n",
    "                         np.linspace(y_min, y_max, 150))\n",
    "    \n",
    "    algorithms = get_algorithms(cont)\n",
    "    for idx_algo, (name, algorithm) in enumerate(algorithms):\n",
    "        t0 = time.time()\n",
    "        if name == \"Local Outlier Factor\":\n",
    "            y_pred = algorithm.fit_predict(X_2d)\n",
    "        else:\n",
    "            algorithm.fit(X_2d)\n",
    "            y_pred = algorithm.predict(X_2d)\n",
    "        t1 = time.time()\n",
    "\n",
    "        ax = plt.subplot(n_datasets, n_algorithms, plot_num)\n",
    "        if i_dataset == 0:\n",
    "            ax.set_title(name, size=12)\n",
    "        if idx_algo == 0:\n",
    "            ax.set_ylabel(f\"Sample {i_dataset+1}\\n(cont={cont:.3f})\", size=9)\n",
    "\n",
    "        # Контур (только не для LOF)\n",
    "        if name != \"Local Outlier Factor\":\n",
    "            Z = algorithm.predict(np.c_[xx.ravel(), yy.ravel()])\n",
    "            Z = Z.reshape(xx.shape)\n",
    "            ax.contour(xx, yy, Z, levels=[0], linewidths=2, colors=\"black\")\n",
    "\n",
    "        colors = np.array([\"#377eb8\", \"#ff7f00\"])\n",
    "        ax.scatter(X_2d[:, 0], X_2d[:, 1], s=8, color=colors[(y_pred + 1) // 2], alpha=0.7)\n",
    "\n",
    "        ax.set_xlim(x_min, x_max)\n",
    "        ax.set_ylim(y_min, y_max)\n",
    "        ax.set_xticks(())\n",
    "        ax.set_yticks(())\n",
    "        ax.text(0.99, 0.01, (\"%.2fs\" % (t1 - t0)).lstrip(\"0\"),\n",
    "                transform=ax.transAxes, size=9, horizontalalignment=\"right\")\n",
    "\n",
    "        plot_num += 1\n",
    "\n",
    "plt.suptitle(\"Сравнение алгоритмов обнаружения аномалий на 5 подвыборках Annthyroid (PCA)\", size=14)\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# 6. Дополнительно: усреднённые метрики качества\n",
    "print(\"\\n===== Усреднённые метрики по 5 подвыборкам =====\")\n",
    "for idx_algo, (name, _) in enumerate(get_algorithms(0.1)):  # шаблон для имени\n",
    "    f1_scores = []\n",
    "    recalls = []\n",
    "    precisions = []\n",
    "    for X_2d, y_true, cont in datasets_2d:\n",
    "        algo = get_algorithms(cont)[idx_algo][1]\n",
    "        if name == \"Local Outlier Factor\":\n",
    "            y_pred = algo.fit_predict(X_2d)\n",
    "        else:\n",
    "            algo.fit(X_2d)\n",
    "            y_pred = algo.predict(X_2d)\n",
    "        y_pred_bin = (y_pred == 1).astype(int)\n",
    "        y_true_bin = (y_true == 1).astype(int)\n",
    "        # Для выбросов (класс 0)\n",
    "        f1_scores.append(f1_score(y_true_bin, y_pred_bin, pos_label=0))\n",
    "        recalls.append(recall_score(y_true_bin, y_pred_bin, pos_label=0))\n",
    "        precisions.append(precision_score(y_true_bin, y_pred_bin, pos_label=0))\n",
    "    print(f\"\\n{name}:\")\n",
    "    print(f\"  Precision (выброс): {np.mean(precisions):.3f} +/- {np.std(precisions):.3f}\")\n",
    "    print(f\"  Recall (выброс):    {np.mean(recalls):.3f} +/- {np.std(recalls):.3f}\")\n",
    "    print(f\"  F1 (выброс):        {np.mean(f1_scores):.3f} +/- {np.std(f1_scores):.3f}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6cd38fd3-8fc7-4ab9-afa6-c941ab99e607",
   "metadata": {},
   "outputs": [],
   "source": [
    "Интерпретация результатов на реальных данных:\n",
    "- LOF и Isolation Forest показывают лучший баланс полноты и точности.\n",
    "- One-Class SVM склонен пропускать выбросы (низкая полнота).\n",
    "- Elliptic Envelope предполагает многомерное нормальное распределение – в реальных данных это не выполняется.\n",
    "- Выбросы в Annthyroid соответствуют пациентам с гипертиреозом – они действительно отличаются по своим биохимическим показателям."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
