{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "4da9f8d6-b42c-4155-903f-b6d27291f421",
   "metadata": {},
   "source": [
    "**Цель задачи:**\n",
    "Продемонстрировать применение метода **K-Nearest Neighbors (KNN)** для классификации данных.\n",
    "\n",
    "1. Использовать алгоритм KNN из scikit-learn (`sklearn.neighbors.KNeighborsClassifier`).\n",
    "2. Протестировать на двух типах данных:\n",
    "   - Встроенный датасет: Iris (набор данных об ирисах Фишера).\n",
    "   - Реальный датасет: (будет загружен из openml.org.).\n",
    "3. Сравнить результаты, визуализировать границы решений и интерпретировать, почему KNN классифицирует объекты."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "394082d8-55cd-451e-b682-3d839e21868e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from sklearn.datasets import load_iris\n",
    "from sklearn.model_selection import train_test_split\n",
    "from sklearn.neighbors import KNeighborsClassifier\n",
    "from sklearn.pipeline import Pipeline\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.inspection import DecisionBoundaryDisplay"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a796ba9b-c325-4143-be8b-76aa47e80e05",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Загружаем датасет Iris в виде pandas DataFrame\n",
    "iris = load_iris(as_frame=True)\n",
    "\n",
    "# Берём только два признака для удобной 2D-визуализации\n",
    "X = iris.data[[\"sepal length (cm)\", \"sepal width (cm)\"]]\n",
    "y = iris.target\n",
    "\n",
    "# Разделяем на обучающую (train) и тестовую (test) выборки\n",
    "# stratify=y - сохраняем пропорцию классов\n",
    "X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5923b3d7-803f-4065-ad90-eeb4d32afd73",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Создаём пайплайн: сначала StandardScaler, затем KNN с 11 соседями\n",
    "clf = Pipeline(\n",
    "    steps=[(\"scaler\", StandardScaler()), (\"knn\", KNeighborsClassifier(n_neighbors=11))]\n",
    ")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b2c9fbf1-54e3-4667-9b6a-3e45f42bec22",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<function matplotlib.pyplot.show(close=None, block=None)>"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 70428 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(8, 6))\n",
    "scatter = plt.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, edgecolors='k', cmap='viridis')\n",
    "plt.xlabel(iris.feature_names[0])\n",
    "plt.ylabel(iris.feature_names[1])\n",
    "plt.title(\"Исходные данные Iris (2 признака)\")\n",
    "plt.legend(scatter.legend_elements()[0], iris.target_names, loc=\"upper left\")\n",
    "plt.show"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "b261ad0e-b02d-4354-b9ab-b8806dc15b69",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 128800 characters]",
      "text/plain": [
       "<Figure size 1200x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Обучаем и визуализируем для двух вариантов весов\n",
    "_, axs = plt.subplots(ncols=2, figsize=(12, 5))\n",
    "\n",
    "for ax, weights in zip(axs, (\"uniform\", \"distance\")):\n",
    "    # Устанавливаем параметр weights и обучаем модель\n",
    "    clf.set_params(knn__weights=weights).fit(X_train, y_train)\n",
    "    \n",
    "    # Отображаем границы решений\n",
    "    disp = DecisionBoundaryDisplay.from_estimator(\n",
    "        clf,\n",
    "        X_test,\n",
    "        response_method=\"predict\",\n",
    "        plot_method=\"pcolormesh\",\n",
    "        xlabel=iris.feature_names[0],\n",
    "        ylabel=iris.feature_names[1],\n",
    "        shading=\"auto\",\n",
    "        alpha=0.5,\n",
    "        ax=ax,\n",
    "    )\n",
    "    \n",
    "    # Отображаем точки данных\n",
    "    scatter = disp.ax_.scatter(X.iloc[:, 0], X.iloc[:, 1], c=y, edgecolors=\"k\")\n",
    "    disp.ax_.legend(\n",
    "        scatter.legend_elements()[0],\n",
    "        iris.target_names,\n",
    "        loc=\"lower left\",\n",
    "        title=\"Classes\",\n",
    "    )\n",
    "    \n",
    "    # Добавляем заголовок с параметрами\n",
    "    _ = disp.ax_.set_title(\n",
    "        f\"3-Class classification\\n(k={clf[-1].n_neighbors}, weights={weights!r})\"\n",
    "    )\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c4601660-81de-4fed-b93c-7bef64c03586",
   "metadata": {},
   "source": [
    "**5. Интерпретация результатов**\n",
    "- **Визуальный анализ**\n",
    "  - KNN успешно разделяет три класса ирисов.\n",
    "  - Границы решений зависят от параметра `weights`: при `uniform` границы более гладкие, при `distance` - более изогнутые.\n",
    "\n",
    "- **Почему KNN нашел эти границы?**\n",
    "  - KNN вычисляет расстояния до k ближайших соседей (k=11).\n",
    "  - Класс новой точки определяется голосованием соседей.\n",
    "\n",
    "- **Как улучшить?**\n",
    "  - Подобрать оптимальное `n_neighbors` (меньше - переобучение, больше - сглаживание).\n",
    "  - Использовать больше признаков (в примере взяты только 2 для визуализации).\n",
    "  - Попробовать другие метрики расстояния.\n",
    "\n",
    "**Вывод:**  \n",
    "KNN - простой и эффективный алгоритм для классификации небольших датасетов, хорошо интерпретируемый через визуализацию границ решений"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "1a881b6f-20b0-4c17-902e-ef4ef1b1a443",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "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.14.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
