{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Цель работы- разделить данные на кластеры\n",
    "## Кластеры будут содержать миллиардеров с похожими данными о себе, возможно это поможет тем, кто анализирует данные об этих людях"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Загрузка датасета "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>Rank</th>\n",
       "      <th>Name</th>\n",
       "      <th>Networth</th>\n",
       "      <th>Age</th>\n",
       "      <th>Country</th>\n",
       "      <th>Source</th>\n",
       "      <th>Industry</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>1</td>\n",
       "      <td>Elon Musk</td>\n",
       "      <td>219.0</td>\n",
       "      <td>50</td>\n",
       "      <td>United States</td>\n",
       "      <td>Tesla, SpaceX</td>\n",
       "      <td>Automotive</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>2</td>\n",
       "      <td>Jeff Bezos</td>\n",
       "      <td>171.0</td>\n",
       "      <td>58</td>\n",
       "      <td>United States</td>\n",
       "      <td>Amazon</td>\n",
       "      <td>Technology</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>3</td>\n",
       "      <td>Bernard Arnault &amp; family</td>\n",
       "      <td>158.0</td>\n",
       "      <td>73</td>\n",
       "      <td>France</td>\n",
       "      <td>LVMH</td>\n",
       "      <td>Fashion &amp; Retail</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>4</td>\n",
       "      <td>Bill Gates</td>\n",
       "      <td>129.0</td>\n",
       "      <td>66</td>\n",
       "      <td>United States</td>\n",
       "      <td>Microsoft</td>\n",
       "      <td>Technology</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>5</td>\n",
       "      <td>Warren Buffett</td>\n",
       "      <td>118.0</td>\n",
       "      <td>91</td>\n",
       "      <td>United States</td>\n",
       "      <td>Berkshire Hathaway</td>\n",
       "      <td>Finance &amp; Investments</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2595</th>\n",
       "      <td>2578</td>\n",
       "      <td>Jorge Gallardo Ballart</td>\n",
       "      <td>1.0</td>\n",
       "      <td>80</td>\n",
       "      <td>Spain</td>\n",
       "      <td>pharmaceuticals</td>\n",
       "      <td>Healthcare</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2596</th>\n",
       "      <td>2578</td>\n",
       "      <td>Nari Genomal</td>\n",
       "      <td>1.0</td>\n",
       "      <td>82</td>\n",
       "      <td>Philippines</td>\n",
       "      <td>apparel</td>\n",
       "      <td>Fashion &amp; Retail</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2597</th>\n",
       "      <td>2578</td>\n",
       "      <td>Ramesh Genomal</td>\n",
       "      <td>1.0</td>\n",
       "      <td>71</td>\n",
       "      <td>Philippines</td>\n",
       "      <td>apparel</td>\n",
       "      <td>Fashion &amp; Retail</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2598</th>\n",
       "      <td>2578</td>\n",
       "      <td>Sunder Genomal</td>\n",
       "      <td>1.0</td>\n",
       "      <td>68</td>\n",
       "      <td>Philippines</td>\n",
       "      <td>garments</td>\n",
       "      <td>Fashion &amp; Retail</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2599</th>\n",
       "      <td>2578</td>\n",
       "      <td>Horst-Otto Gerberding</td>\n",
       "      <td>1.0</td>\n",
       "      <td>69</td>\n",
       "      <td>Germany</td>\n",
       "      <td>flavors and fragrances</td>\n",
       "      <td>Food &amp; Beverage</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>2600 rows × 7 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "      Rank                        Name  Networth  Age        Country  \\\n",
       "0         1                 Elon Musk      219.0   50  United States   \n",
       "1         2                Jeff Bezos      171.0   58  United States   \n",
       "2         3  Bernard Arnault & family      158.0   73         France   \n",
       "3         4                Bill Gates      129.0   66  United States   \n",
       "4         5            Warren Buffett      118.0   91  United States   \n",
       "...     ...                        ...       ...  ...            ...   \n",
       "2595   2578    Jorge Gallardo Ballart        1.0   80          Spain   \n",
       "2596   2578              Nari Genomal        1.0   82    Philippines   \n",
       "2597   2578            Ramesh Genomal        1.0   71    Philippines   \n",
       "2598   2578            Sunder Genomal        1.0   68    Philippines   \n",
       "2599   2578     Horst-Otto Gerberding        1.0   69        Germany   \n",
       "\n",
       "                      Source                Industry  \n",
       "0              Tesla, SpaceX             Automotive   \n",
       "1                     Amazon             Technology   \n",
       "2                       LVMH       Fashion & Retail   \n",
       "3                  Microsoft             Technology   \n",
       "4         Berkshire Hathaway  Finance & Investments   \n",
       "...                      ...                     ...  \n",
       "2595         pharmaceuticals             Healthcare   \n",
       "2596                 apparel       Fashion & Retail   \n",
       "2597                 apparel       Fashion & Retail   \n",
       "2598                garments       Fashion & Retail   \n",
       "2599  flavors and fragrances        Food & Beverage   \n",
       "\n",
       "[2600 rows x 7 columns]"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "df = pd.read_csv(\"C://Users//annal//aim//static//csv//Forbes_Billionaires.csv\")\n",
    "df"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Подготовим данные. Удалим неинформативные столбцы, преобразуем категориальные столбцы в числовые и нормализуем"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Удаляем неинформативные столбцы\n",
    "df = df.drop(columns=[\"Rank \", \"Name\"])\n",
    "\n",
    "# Преобразование категориальных данных в числовые с помощью one-hot encoding\n",
    "df = pd.get_dummies(df, drop_first=True)\n",
    "\n",
    "# Нормализация числовых данных\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "\n",
    "scaler = StandardScaler()\n",
    "df_scaled = scaler.fit_transform(df)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Визуализируем данные при помощи PCA\n",
    "## Метод PCA уменьшает количество измерений до двух, но сохраняет максимально возможное количество информации. Он находит новые оси в данных и потом проецирует их на эти оси. Вдоль этих осей разброс данных максимальный"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 193308 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Импортируем PCA и визуализируем данные\n",
    "from sklearn.decomposition import PCA\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Применяем PCA для снижения размерности до 2\n",
    "pca = PCA(n_components=2)\n",
    "df_pca = pca.fit_transform(df_scaled)\n",
    "\n",
    "# Визуализация\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.scatter(df_pca[:, 0], df_pca[:, 1], c='red', edgecolor='k', alpha=0.6)\n",
    "plt.title(\"PCA: Визуализация данных после снижения размерности\")\n",
    "plt.xlabel(\"Главная компонента 1\")\n",
    "plt.ylabel(\"Главная компонента 2\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Определим количество кластеров\n",
    "## Это важно, потому что оптимальное количество кластеров позволит разделить данные так, чтобы не были упущенны важные моменты. Кроме того, это сделает данные понятными и полезными, повлияет и на способность обощения модели. Если кластеров слишком много, то модель будет обращать излишнее внимание на шум, если кластеров слишком мало, то модель будет игнорировать важные моменты"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Для разделения на кластеры воспользуемся методом локтя и коэффициентом силуэта\n",
    "## Метод локтя измеряет инерцию, то есть размерность ошибок внутри кластеров. Инерция — это сумма квадратов расстояний от точек до центроидов их кластеров. Чем меньше будет значение, тем лучше\n",
    "## Коэффициет силуэта покажет, насколько хорошо распределены объекты по кластерам. Он оценивает плотность кластеров и принимает значения от -1 до 1. Чем ближе результат к 1, тем лучше."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 46504 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 46372 characters]",
      "text/plain": [
       "<Figure size 800x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Метод локтя\n",
    "from sklearn.cluster import KMeans\n",
    "\n",
    "border_l = 2\n",
    "border_r = 5\n",
    "\n",
    "inertia = []\n",
    "for k in range(border_l, border_r):\n",
    "    kmeans = KMeans(n_clusters=k, random_state=42)\n",
    "    kmeans.fit(df_scaled)\n",
    "    inertia.append(kmeans.inertia_)\n",
    "\n",
    "# Визуализация метода локтя\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.plot(range(border_l, border_r), inertia, marker='o')\n",
    "plt.title('Метод локтя для выбора количества кластеров')\n",
    "plt.xlabel('Количество кластеров')\n",
    "plt.ylabel('Инерция')\n",
    "plt.show()\n",
    "\n",
    "# Коэффициент силуэта\n",
    "from sklearn.metrics import silhouette_score\n",
    "\n",
    "silhouette_scores = []\n",
    "for k in range(border_l, border_r):\n",
    "    kmeans = KMeans(n_clusters=k, random_state=42)\n",
    "    kmeans.fit(df_scaled)\n",
    "    score = silhouette_score(df_scaled, kmeans.labels_)\n",
    "    silhouette_scores.append(score)\n",
    "\n",
    "# Визуализация коэффициента силуэта\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.plot(range(border_l, border_r), silhouette_scores, marker='o')\n",
    "plt.title('Коэффициент силуэта для различных кластеров')\n",
    "plt.xlabel('Количество кластеров')\n",
    "plt.ylabel('Коэффициент силуэта')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Кластеризируем с помощью K-means\n",
    "## K-means — это алгоритм кластеризации, который группирует данные вокруг центров (центроидов) кластеров. Я выбираю количество кластеров, равное 2, исходя из коэффициента силуэта на диаграмме выше"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 226188 characters]",
      "text/plain": [
       "<Figure size 800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Кластеризация с помощью K-means\n",
    "optimal_clusters = 2\n",
    "kmeans = KMeans(n_clusters=optimal_clusters, random_state=42)\n",
    "df['Cluster'] = kmeans.fit_predict(df_scaled)\n",
    "\n",
    "# Визуализация кластеров с использованием PCA\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.scatter(df_pca[:, 0], df_pca[:, 1], c=df['Cluster'], cmap='viridis', edgecolor='k', alpha=0.6)\n",
    "plt.title(\"Кластеры, определенные K-means (PCA)\")\n",
    "plt.xlabel(\"Главная компонента 1\")\n",
    "plt.ylabel(\"Главная компонента 2\")\n",
    "plt.colorbar(label='Кластер')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Приступим к иерархической кластеризации\n",
    "## Иерархическая кластеризация — метод, который строит древовидную структуру кластеров (дендрограмму). Применим её для сравнения с K-means"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 81980 characters]",
      "text/plain": [
       "<Figure size 1200x800 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 206656 characters]",
      "text/plain": [
       "<Figure size 800x600 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from sklearn.cluster import AgglomerativeClustering\n",
    "from scipy.cluster.hierarchy import dendrogram\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Применение иерархической кластеризации\n",
    "hierarchical = AgglomerativeClustering(n_clusters=optimal_clusters, compute_distances=True)\n",
    "df['Hierarchical Cluster'] = hierarchical.fit_predict(df_scaled)\n",
    "\n",
    "# Функция для получения матрицы linkage\n",
    "def get_linkage_matrix(model: AgglomerativeClustering) -> np.ndarray:\n",
    "    counts = np.zeros(model.children_.shape[0])  # type: ignore\n",
    "    n_samples = len(model.labels_)\n",
    "    for i, merge in enumerate(model.children_):  # type: ignore\n",
    "        current_count = 0\n",
    "        for child_idx in merge:\n",
    "            if child_idx < n_samples:\n",
    "                current_count += 1\n",
    "            else:\n",
    "                current_count += counts[child_idx - n_samples]\n",
    "        counts[i] = current_count\n",
    "\n",
    "    return np.column_stack([model.children_, model.distances_, counts]).astype(float)\n",
    "\n",
    "# Построение дендрограммы\n",
    "linkage_matrix = get_linkage_matrix(hierarchical)\n",
    "plt.figure(figsize=(12, 8))\n",
    "dendrogram(linkage_matrix)\n",
    "plt.title(\"Дендограмма, восстановленная из модели AgglomerativeClustering\")\n",
    "plt.xlabel(\"Индексы объектов\")\n",
    "plt.ylabel(\"Евклидово расстояние\")\n",
    "plt.show()\n",
    "\n",
    "\n",
    "# Визуализация кластеров\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.scatter(df_pca[:, 0], df_pca[:, 1], c=df['Hierarchical Cluster'], cmap='viridis', edgecolor='k', alpha=0.6)\n",
    "plt.title(\"Кластеры, определенные иерархической кластеризацией (PCA)\")\n",
    "plt.xlabel(\"Главная компонента 1\")\n",
    "plt.ylabel(\"Главная компонента 2\")\n",
    "plt.colorbar(label='Кластер')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Оценим коэффициенты силуэтов двух методов кластеризации"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Коэффициент силуэта для K-means: 0.0405\n",
      "Коэффициент силуэта для иерархической кластеризации: 0.3230\n"
     ]
    }
   ],
   "source": [
    "# Оценка качества\n",
    "silhouette_kmeans = silhouette_score(df_scaled, df['Cluster'])\n",
    "silhouette_hierarchical = silhouette_score(df_scaled, df['Hierarchical Cluster'])\n",
    "\n",
    "print(f\"Коэффициент силуэта для K-means: {silhouette_kmeans:.4f}\")\n",
    "print(f\"Коэффициент силуэта для иерархической кластеризации: {silhouette_hierarchical:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Из полученных данных видно, что коэффициент силуэта для иерархической кластеризации лучше, он ближе к 1. А вот результат для K-means значительно хуже. Границы кластеров размыты сильнее, чем в иерархическом методе. В ходе экспериментов с количеством кластеров для k-meansвыяснилосб, что 0.0405- лучшее значение"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
