978 lines
1.0 MiB
Plaintext
Raw Permalink Normal View History

2024-12-06 20:49:14 +04:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Датасет: Цены на акции\n",
"https://www.kaggle.com/datasets/nancyalaswad90/yamana-gold-inc-stock-Volume\n",
"##### О наборе данных: \n",
"Yamana Gold Inc. — это канадская компания, которая занимается разработкой и управлением золотыми, серебряными и медными рудниками, расположенными в Канаде, Чили, Бразилии и Аргентине. Головной офис компании находится в Торонто.\n",
"\n",
"Yamana Gold была основана в 1994 году и уже через год была зарегистрирована на фондовой бирже Торонто. В 2007 году она стала участником Нью-Йоркской фондовой биржи, а в 2020 году — Лондонской.\n",
"В 2003 году компания претерпела значительные изменения: была проведена реструктуризация, в результате которой Питер Марроне занял пост главного исполнительного директора. Кроме того, Yamana объединилась с бразильской компанией Santa Elina Mines Corporation. Благодаря этому слиянию Yamana получила доступ к капиталу, накопленному Santa Elina, что позволило ей начать разработку и эксплуатацию рудника Чапада. Затем компания объединилась с другими организациями, зарегистрированными на бирже TSX: RNC Gold, Desert Sun Mining, Viceroy Exploration, Northern Orion Resources, Meridian Gold, Osisko Mining и Extorre Gold Mines. Каждая из них внесла свой вклад в разработку месторождения или проект, который в итоге был успешно запущен.\n",
"##### Таким образом:\n",
"* Объект наблюдения - цены и объемы акций компании\n",
"* Атрибуты: 'Date', 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'\n",
"\n",
"##### Бизнес цель для задач кластеризации:\n",
"* Кластеризация дней в группы в зависимости от динамики цен на акции компании.\n",
" Позволит выявить закономерности в их поведении. \n",
" Даст возможность прогнозировать дни с похожими рыночными условиями и принимать обоснованные решения, такие как инвестирование.\n"
]
},
{
"cell_type": "code",
"execution_count": 112,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Количество колонок: 7\n",
"Колонки: Date, Open, High, Low, Close, Adj Close, Volume\n",
"\n",
"<class 'pandas.core.frame.DataFrame'>\n",
"RangeIndex: 5251 entries, 0 to 5250\n",
"Data columns (total 7 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 Date 5251 non-null datetime64[ns]\n",
" 1 Open 5251 non-null float64 \n",
" 2 High 5251 non-null float64 \n",
" 3 Low 5251 non-null float64 \n",
" 4 Close 5251 non-null float64 \n",
" 5 Adj Close 5251 non-null float64 \n",
" 6 Volume 5251 non-null int64 \n",
"dtypes: datetime64[ns](1), float64(5), int64(1)\n",
"memory usage: 287.3 KB\n"
]
},
{
"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>Date</th>\n",
" <th>Open</th>\n",
" <th>High</th>\n",
" <th>Low</th>\n",
" <th>Close</th>\n",
" <th>Adj Close</th>\n",
" <th>Volume</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>5246</th>\n",
" <td>2022-04-29</td>\n",
" <td>5.66</td>\n",
" <td>5.69</td>\n",
" <td>5.50</td>\n",
" <td>5.51</td>\n",
" <td>5.51</td>\n",
" <td>16613300</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5247</th>\n",
" <td>2022-05-02</td>\n",
" <td>5.33</td>\n",
" <td>5.39</td>\n",
" <td>5.18</td>\n",
" <td>5.30</td>\n",
" <td>5.30</td>\n",
" <td>27106700</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5248</th>\n",
" <td>2022-05-03</td>\n",
" <td>5.32</td>\n",
" <td>5.53</td>\n",
" <td>5.32</td>\n",
" <td>5.47</td>\n",
" <td>5.47</td>\n",
" <td>18914200</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5249</th>\n",
" <td>2022-05-04</td>\n",
" <td>5.47</td>\n",
" <td>5.61</td>\n",
" <td>5.37</td>\n",
" <td>5.60</td>\n",
" <td>5.60</td>\n",
" <td>20530700</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5250</th>\n",
" <td>2022-05-05</td>\n",
" <td>5.63</td>\n",
" <td>5.66</td>\n",
" <td>5.34</td>\n",
" <td>5.44</td>\n",
" <td>5.44</td>\n",
" <td>19879200</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Open High Low Close Adj Close Volume\n",
"5246 2022-04-29 5.66 5.69 5.50 5.51 5.51 16613300\n",
"5247 2022-05-02 5.33 5.39 5.18 5.30 5.30 27106700\n",
"5248 2022-05-03 5.32 5.53 5.32 5.47 5.47 18914200\n",
"5249 2022-05-04 5.47 5.61 5.37 5.60 5.60 20530700\n",
"5250 2022-05-05 5.63 5.66 5.34 5.44 5.44 19879200"
]
},
"execution_count": 112,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from sklearn.metrics import mean_squared_error, r2_score\n",
"from sklearn.discriminant_analysis import StandardScaler\n",
"\n",
"from sklearn.model_selection import train_test_split\n",
"from sklearn.pipeline import Pipeline, FeatureUnion\n",
"from sklearn.compose import ColumnTransformer\n",
"from sklearn.impute import SimpleImputer\n",
"from sklearn.preprocessing import StandardScaler, OneHotEncoder\n",
"from sklearn.base import BaseEstimator, TransformerMixin\n",
"\n",
"from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor\n",
"from sklearn.neural_network import MLPRegressor\n",
"\n",
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from scipy.cluster.hierarchy import dendrogram, linkage, fcluster\n",
"from sklearn.cluster import KMeans\n",
"from sklearn.decomposition import PCA\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.metrics import silhouette_score\n",
"\n",
"df = pd.read_csv(\".//static//csv//Stocks.csv\", sep=\",\")\n",
"print('Количество колонок: ' + str(df.columns.size)) \n",
"print('Колонки: ' + ', '.join(df.columns)+'\\n')\n",
"df['Date'] = pd.to_datetime(df['Date'], errors='coerce')\n",
"\n",
"\n",
"df.info()\n",
"df.tail()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Подготовка данных:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 1. Получение сведений о пропущенных данных\n",
"Типы пропущенных данных:\n",
"\n",
"- None - представление пустых данных в Python\n",
"- NaN - представление пустых данных в Pandas\n",
"- '' - пустая строка"
]
},
{
"cell_type": "code",
"execution_count": 113,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Date 0\n",
"Open 0\n",
"High 0\n",
"Low 0\n",
"Close 0\n",
"Adj Close 0\n",
"Volume 0\n",
"dtype: int64\n",
"\n",
"Date False\n",
"Open False\n",
"High False\n",
"Low False\n",
"Close False\n",
"Adj Close False\n",
"Volume False\n",
"dtype: bool\n",
"\n",
"Количество бесконечных значений в каждом столбце:\n",
"Date 0\n",
"Open 0\n",
"High 0\n",
"Low 0\n",
"Close 0\n",
"Adj Close 0\n",
"Volume 0\n",
"dtype: int64\n",
"Date процент пустых значений: %0.00\n",
"Open процент пустых значений: %0.00\n",
"High процент пустых значений: %0.00\n",
"Low процент пустых значений: %0.00\n",
"Close процент пустых значений: %0.00\n",
"Adj Close процент пустых значений: %0.00\n",
"Volume процент пустых значений: %0.00\n"
]
}
],
"source": [
"# Количество пустых значений признаков\n",
"print(df.isnull().sum())\n",
"print()\n",
"\n",
"# Есть ли пустые значения признаков\n",
"print(df.isnull().any())\n",
"print()\n",
"\n",
"# Проверка на бесконечные значения\n",
"print(\"Количество бесконечных значений в каждом столбце:\")\n",
"print(np.isinf(df).sum())\n",
"\n",
"# Процент пустых значений признаков\n",
"for i in df.columns:\n",
" null_rate = df[i].isnull().sum() / len(df) * 100\n",
" print(f\"{i} процент пустых значений: %{null_rate:.2f}\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Таким образом, пропущенных значений не найдено."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### 2. Проверка выбросов данных и устранение их при наличии:"
]
},
{
"cell_type": "code",
"execution_count": 114,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"До устранения выбросов:\n",
"Колонка Open:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.42\n",
" 1-й квартиль (Q1): 2.857143\n",
" 3-й квартиль (Q3): 10.65\n",
"\n",
"После устранения выбросов:\n",
"Колонка Open:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.42\n",
" 1-й квартиль (Q1): 2.857143\n",
" 3-й квартиль (Q3): 10.65\n",
"\n",
"До устранения выбросов:\n",
"Колонка High:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.59\n",
" 1-й квартиль (Q1): 2.88\n",
" 3-й квартиль (Q3): 10.86\n",
"\n",
"После устранения выбросов:\n",
"Колонка High:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.59\n",
" 1-й квартиль (Q1): 2.88\n",
" 3-й квартиль (Q3): 10.86\n",
"\n",
"До устранения выбросов:\n",
"Колонка Low:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.09\n",
" 1-й квартиль (Q1): 2.81\n",
" 3-й квартиль (Q3): 10.425\n",
"\n",
"После устранения выбросов:\n",
"Колонка Low:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.09\n",
" 1-й квартиль (Q1): 2.81\n",
" 3-й квартиль (Q3): 10.425\n",
"\n",
"До устранения выбросов:\n",
"Колонка Close:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.389999\n",
" 1-й квартиль (Q1): 2.857143\n",
" 3-й квартиль (Q3): 10.64\n",
"\n",
"После устранения выбросов:\n",
"Колонка Close:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 1.142857\n",
" Максимальное значение: 20.389999\n",
" 1-й квартиль (Q1): 2.857143\n",
" 3-й квартиль (Q3): 10.64\n",
"\n",
"До устранения выбросов:\n",
"Колонка Adj Close:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 0.935334\n",
" Максимальное значение: 17.543156\n",
" 1-й квартиль (Q1): 2.537094\n",
" 3-й квартиль (Q3): 8.951944999999998\n",
"\n",
"После устранения выбросов:\n",
"Колонка Adj Close:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 0.935334\n",
" Максимальное значение: 17.543156\n",
" 1-й квартиль (Q1): 2.537094\n",
" 3-й квартиль (Q3): 8.951944999999998\n",
"\n",
"До устранения выбросов:\n",
"Колонка Volume:\n",
" Есть выбросы: Да\n",
" Количество выбросов: 95\n",
" Минимальное значение: 0\n",
" Максимальное значение: 76714000\n",
" 1-й квартиль (Q1): 2845900.0\n",
" 3-й квартиль (Q3): 13272450.0\n",
"\n",
"После устранения выбросов:\n",
"Колонка Volume:\n",
" Есть выбросы: Нет\n",
" Количество выбросов: 0\n",
" Минимальное значение: 0.0\n",
" Максимальное значение: 28912275.0\n",
" 1-й квартиль (Q1): 2845900.0\n",
" 3-й квартиль (Q3): 13272450.0\n",
"\n"
]
}
],
"source": [
"numeric_columns = ['Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume']\n",
"\n",
"for column in numeric_columns:\n",
" if pd.api.types.is_numeric_dtype(df[column]): # Проверяем, является ли колонка числовой\n",
" q1 = df[column].quantile(0.25) # Находим 1-й квартиль (Q1)\n",
" q3 = df[column].quantile(0.75) # Находим 3-й квартиль (Q3)\n",
" iqr = q3 - q1 # Вычисляем межквартильный размах (IQR)\n",
"\n",
" # Определяем границы для выбросов\n",
" lower_bound = q1 - 1.5 * iqr # Нижняя граница\n",
" upper_bound = q3 + 1.5 * iqr # Верхняя граница\n",
"\n",
" # Подсчитываем количество выбросов\n",
" outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]\n",
" outlier_count = outliers.shape[0]\n",
"\n",
" print(\"До устранения выбросов:\")\n",
" print(f\"Колонка {column}:\")\n",
" print(f\" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
" print(f\" Количество выбросов: {outlier_count}\")\n",
" print(f\" Минимальное значение: {df[column].min()}\")\n",
" print(f\" Максимальное значение: {df[column].max()}\")\n",
" print(f\" 1-й квартиль (Q1): {q1}\")\n",
" print(f\" 3-й квартиль (Q3): {q3}\\n\")\n",
"\n",
" # Устраняем выбросы: заменяем значения ниже нижней границы на саму нижнюю границу, а выше верхней — на верхнюю\n",
" if outlier_count != 0:\n",
" df[column] = df[column].apply(lambda x: lower_bound if x < lower_bound else upper_bound if x > upper_bound else x)\n",
" \n",
" # Подсчитываем количество выбросов\n",
" outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)]\n",
" outlier_count = outliers.shape[0]\n",
"\n",
" print(\"После устранения выбросов:\")\n",
" print(f\"Колонка {column}:\")\n",
" print(f\" Есть выбросы: {'Да' if outlier_count > 0 else 'Нет'}\")\n",
" print(f\" Количество выбросов: {outlier_count}\")\n",
" print(f\" Минимальное значение: {df[column].min()}\")\n",
" print(f\" Максимальное значение: {df[column].max()}\")\n",
" print(f\" 1-й квартиль (Q1): {q1}\")\n",
" print(f\" 3-й квартиль (Q3): {q3}\\n\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Визуализируем данные с учетом понимания их особенностей:\n",
"Для уменьшения размерности используем метод главных компонент (PCA), который позволит визуализировать данные в двухмерном пространстве. \n",
"Мы используем для этого числовые колонки: 'Open', 'High', 'Low', 'Close', 'Adj Close', 'Volume'. \n",
"Начнем с нормализации данных и последующего применения PCA."
]
},
{
"cell_type": "code",
"execution_count": 115,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA1kAAAJwCAYAAAB71at5AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOy9d5hkZZm/f59Qubqq8/RMTw4MDJMQUYIEA0kyCor+Fsy6i3ENu7oGwiK66LKKX3VNgCK7K3lXUUCCBAERBphxZpiceno6d+Wqk97fH6erpqururu6pzry3tc1XPSpU+e8J9bzeZ+kCCEEEolEIpFIJBKJRCKpCupUD0AikUgkEolEIpFIZhNSZEkkEolEIpFIJBJJFZEiSyKRSCQSiUQikUiqiBRZEolEIpFIJBKJRFJFpMiSSCQSiUQikUgkkioiRZZEIpFIJBKJRCKRVBEpsiQSiUQikUgkEomkikiRJZFIJBKJRCKRSCRVRIosiUQikUgkEolEIqkiUmRJJBKJRCKRSCQSSRWRIksimYXcdtttKIoy4r/Vq1dP9TAlEolEMo0444wzin4n6uvrOeGEE/jFL36B4zgl6z/xxBNceumltLS04PV6aW5u5oILLuDee+8tu/0tW7agKAp+v5/+/v4JPhqJZGrRp3oAEolk4rjuuutYsmRJyfIbbrhhCkYjkUgkkunO/PnzufHGGwHo6uril7/8JR/+8IfZtm0b3/rWtwrrfeMb3+C6665jxYoVfPzjH2fRokX09PTw4IMP8q53vYtf//rXvO997yva9h133EFLSwt9fX3cfffdfOQjH5nUY5NIJhNFCCGmehASiaS63HbbbXzwgx/khRde4I1vfGPJ52eccQbd3d1s2rRpCkYnkUgkkulIud+GdDrNypUr6evro6+vD4/Hw913381ll13Gu9/9bu688048Hk/Rdh566CFM0+T8888vLBNCsHTpUi699FJ2795NX18fjz/++KQdm0Qy2chwQYlEAoBlWVx//fUsW7YMn8/H4sWL+cpXvkIulytab/HixUU/nHk++clPoihK0TJFUbjmmmuKlt10000oisIZZ5xRWPbggw+i6zrf+c53Rvx+V1cXS5cu5fTTT8cwjMLyXC7HN77xDZYvX47P52PBggV86UtfKhm7oih88pOfLBn7+eefz+LFiwt/79mzB0VRSsYDsHr16qKxG4bB17/+dY4//nii0SihUIhTTz21rPHQ2dnJhz/8YRYuXIimaYWQnHA4XLLuUPLn/eGHH2b9+vX4/X5WrVpVEpbT29vLF77wBdasWUM4HCYSiXDuuefyyiuvFK339NNP85a3vIXGxkb8fj9Lly7ln/7pn8hms4V18mGnXq+Xrq6uou8/++yzhfH/9a9/Lfrs+eef55xzziEajRIMBjn99NN55plnita55pprUBSFrVu3cvnllxOJRGhoaOAzn/lM0Rig8usGkEql+PznP8+CBQvw+XysXLmS73znO5SbT7zjjjt405veRDAYpK6ujtNOO42HH364cL5HCrfN73eke6USHMfhe9/7HmvWrMHv99PU1MQ555xTck6HcsYZZxTdhwAvvPBCYXxjOdZKjncwlb4rwA0nG+n8DV7n7rvvHvGYf/SjH7Fu3brCc7Zu3Tp+/vOfl6z32GOPceqppxIKhaitreWiiy5iy5YtRevk77/8v5qaGt70pjdx//33F6331FNPcdlll7Fw4cLCu+Vzn/scmUymaL0PfOADZZ/ju+++G0VReOKJJwrLxnPtjj/+eAKBAPX19bz3ve9l//79Revkw/wuvvjiku9//OMfP6IQ8WAwyIknnkgqlSq8B772ta9RX1/PL37xixKBBXD22WeX/E4888wz7Nmzh/e+9728973v5cknn+TAgQPjGpNEMhOQ4YISiQSAj3zkI9x+++28+93v5vOf/zzPP/88N954I1u2bOG+++6ryj76+/sLYSiDeec738m///u/87nPfY6jjjqKCy+8sGSdXC7HxRdfjKZp3HfffXi9XsA1Ui+88EKefvppPvaxj3HMMcewceNGbr75ZrZt21ZiNFWbeDzOz372M6644go++tGPkkgk+PnPf87ZZ5/NX/7yF9avX19Y96qrruKPf/wjn/rUp1i3bh2apvGTn/yEl156qaJ9bd++nfe85z184hOf4KqrruLWW2/lsssu4w9/+ANnnnkmALt27eL+++/nsssuY8mSJXR0dPCf//mfnH766WzevJl58+YBkEgkOOaYY7j88ssJBoM8++yz/Nu//RvpdJpbbrmlaL+apnHHHXfwuc99rrDs1ltvxe/3lwiixx57jHPPPZfjjz+eb3zjG6iqyq233srb3vY2nnrqKd70pjcVrX/55ZezePFibrzxRp577jm+//3v09fXxy9/+cuKr0EeIQQXXnghjz/+OB/+8IdZv349Dz30EF/84hdpa2vj5ptvLqx77bXXcs0113DyySdz3XXX4fV6ef7553nsscc466yz+I//+A+SySTg5pF885vf5Ctf+QrHHHMMQEXCuBI+/OEPc9ttt3HuuefykY98BMuyeOqpp3juuefKeqFH4p/+6Z/KLh/tWPOsX7+ez3/+80Xf/eUvf8kjjzxStGw874rB5+4nP/kJ+/btG9OxgXvPnnXWWSxbtgwhBL/5zW/4yEc+Qm1tLe9617sA+OMf/8i5557L0qVLueaaa8hkMtxyyy2ccsopvPTSSyWi/Fe/+hUA3d3d/PCHP+Syyy5j06ZNrFy5EoC77rqLdDrN3//939PQ0MBf/vIXbrnlFg4cOMBdd9015mMYjuGu3Q033MDXvvY1Lr/8cj7ykY/Q1dXFLbfcwmmnncaGDRuora0trOv3+/nd735HZ2cnzc3NAGQyGf7nf/4Hv99/ROPbtWsXmqZRW1vL9u3b2bp1Kx/60IeoqampeBu//vWvWbZsGSeccAKrV68mGAzyX//1X3zxi188orFJJNMWIZFIZh233nqrAMQLL7xQ9vPTTz9dHHvssYW/X375ZQGIj3zkI0XrfeELXxCAeOyxxwrLFi1aJM4777ySbV599dVi6CsFEN/4xjcKf3/pS18Szc3N4vjjjxenn356yTb+4R/+QYTDYfHyyy+XfP/973+/qK+vF6+99lrRd371q18JVVXFU089VbT8xz/+sQDEM888UzSeq6++umS/5513nli0aFHh7927dwtA3HTTTSXrHnvssUVjtyxL5HK5onX6+vrEnDlzxIc+9KHCskwmI1RVFR//+MeL1r3qqqtEKBQq2c9QFi1aJABxzz33FJbFYjExd+5ccdxxxxWWZbNZYdt20Xd3794tfD6fuO6660bcxzvf+U6xevXqwt/5++iKK64Qa9asKSxPpVIiEomI973vfUX3meM4YsWKFeLss88WjuMU1k+n02LJkiXizDPPLCz7xje+IQBx4YUXFo3hH/7hHwQgXnnllcKySq/b/fffLwDxr//6r0Xrvfvd7xaKoogdO3YIIYTYvn27UFVVXHLJJSXnavC48zz++OMCEI8//njJZyPdK6Px2GOPCUB8+tOfLvms3DgGc/rppxfdhw8++KAAxDnnnFP0HFZ6rJU+12N5VwghxCOPPCIA8ac//amw7Kqrriq6bvnze9ddd414zEOxLEtEIhHxyU9+srBs/fr1orm5WfT09BSWvfLKK0JVVXHllVcWluXvv8E8/PDDAhC/+c1vCsvS6XTJfm+88UahKIrYu3dv0TGVe47vuuuuknun0mu3Z88eoWmauOGGG4q2uXHjRqHretHy/Dt97dq14jvf+U5h+a9+9Ssxf/58ceqppxa984fj9NNPF0cffbTo6uoSXV1dYsuWLeLTn/60AMQFF1wghBDigQceEIC4+eabR91eHsMwRENDg/iXf/mXwrL3ve99Yt26dRVvQyKZachwQYlEwoMPPgjAP/7jPxYtz89q/+53vytabpom3d3dRf+GejSG0tbWxi233MLXvva1Yb0A3//+9znxxBO54IILOHToUGH59ddfz5133sk999zDUUcdVfSdu+66i2OOOYajjz66aDx
"text/plain": [
"<Figure size 1000x700 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.decomposition import PCA\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"\n",
"# Нормализация данных\n",
"scaler = StandardScaler()\n",
"data_scaled = scaler.fit_transform(df[numeric_columns].dropna())\n",
"\n",
"# Понижение размерности с помощью PCA\n",
"pca = PCA(n_components=2)\n",
"reduced_data = pca.fit_transform(data_scaled)\n",
"\n",
"# Визуализация\n",
"plt.figure(figsize=(10, 7))\n",
"plt.scatter(reduced_data[:, 0], reduced_data[:, 1], alpha=0.6)\n",
"plt.title(\"Пониженная размерность с использованием PCA\")\n",
"plt.xlabel(\"Первая компонентa PCA\")\n",
"plt.ylabel(\"Вторая компонентa PCA\")\n",
"plt.grid()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"##### **Предварительные выводы по данному этапу:**\n",
"* **Два основных компонента:** График отображает проекцию данных на два основных компонента (PC1 и PC2). Эти компоненты представляют собой линейные комбинации исходных признаков, которые обеспечивают наибольшую дисперсию данных.\n",
"* **Семантика компонентов:** Хотя мы не знаем, какие именно признаки входят в каждый из компонентов, в общем смысле PC1 и PC2 могут отражать различные аспекты котировок акций, такие как:\n",
" 1. PC1 может представлять общую тенденцию цен (например, общий тренд или изменение объема).\n",
" 2. PC2 может указывать на волатильность или различия между дневными значениями (например, колебания между открытием и закрытием).\n",
"* **Скопления точек:** На графике можно увидеть несколько скоплений точек, что может свидетельствовать о наличии групп (кластеров) данных, имеющих схожие характеристики.\n",
"* **Признаки кластеризации:** Распределение точек на графике показывает разные группы ценовых движений. В дальнейшем, как и было запланировано бизнес-целью, мы сможем выявить дни с высокой волатильностью или, наоборот, с небольшой изменчивостью цен. Это позволит выявить закономерности в поведении цен и даст возможность прогнозировать дни с похожими рыночными условиями и принимать обоснованные решения, такие как инвестирование."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Выберем количество кластеров на основе оценки инерции и коэффициента силуэта\n",
"\n",
"Используем метод локтя для оценки инерции и коэффициент силуэта для выбора оптимального количества кластеров."
]
},
{
"cell_type": "code",
"execution_count": 116,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA2QAAAIjCAYAAABswtioAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAByeElEQVR4nO3dd3hUZd7G8XtmMukFAqRJi4CQ0ItCxC6dRVh7Q0BXVxZUwFVkVYq4yyu7UhSEtYG7yq5l7QWIiKASQJpKFTACSgo1lSSTzHn/CBkzpIfAmUm+n+vKFeacZ8785jzyLvf7lGMxDMMQAAAAAOC8s5pdAAAAAAA0VAQyAAAAADAJgQwAAAAATEIgAwAAAACTEMgAAAAAwCQEMgAAAAAwCYEMAAAAAExCIAMAAAAAkxDIAAAAAMAkBDIAAAAAMAmBDAAAAABMQiADAJhi6dKlslgsslgs+vrrr8ucNwxDLVq0kMVi0e9+9zsTKgQA4NwjkAEATOXv769ly5aVOb5mzRr98ssv8vPzM6EqAADODwIZAMBUQ4YM0dtvv63CwkK348uWLVPPnj0VFRVlUmUAAJx7BDIAgKluu+02HTt2TImJia5jBQUFeuedd3T77beX+x6n06l58+apY8eO8vf3V2RkpP74xz/qxIkTrjatW7d2TYks76d169autjk5OXr44YfVokUL+fn5qX379vrHP/4hwzDKfPaXX35Z4TWra/To0eW+f/r06W7tvvjiC11++eUKCgpSo0aNNHz4cO3atcutzfTp08t89urVq+Xn56f777/frU1lP19++aXr/YsWLVKnTp0UGBjo1uadd96p9ncEAFSPj9kFAAAattatWyshIUH/+c9/NHjwYEnSZ599poyMDN1666167rnnyrznj3/8o5YuXaoxY8bowQcfVHJyshYsWKCtW7fqm2++kd1u17x585SdnS1J2rVrl/72t7/pL3/5i+Li4iRJwcHBkorXql133XVavXq17rnnHnXr1k0rVqzQI488ol9//VVz584tt+4HH3xQF198sSTpX//6l1ugrI6mTZu6XXvkyJFu5z///HMNHjxYF154oaZPn65Tp07p+eefV9++fbVlyxa3QFnad999pxEjRmjIkCFauHChJOn6669X27ZtXW0mTpyouLg43Xfffa5jJfflzTff1J/+9CddddVVeuCBBxQUFOS6fwCAc8AAAMAES5YsMSQZ3377rbFgwQIjJCTEyM3NNQzDMG666Sbj6quvNgzDMFq1amUMHTrU9b6vvvrKkGS88cYbbtdbvnx5uccNwzBWr15tSDJWr15d5tz7779vSDKefvppt+M33nijYbFYjH379rkdX7lypSHJeOedd1zHxo0bZ9Tkf1LvuOMOIzY21u2YJGPatGmu1926dTMiIiKMY8eOuY599913htVqNe666y7XsWnTprk+++effzaio6ONyy67zDh16lSFn9+qVStj1KhR5Z677bbbjEaNGrm9v+T+vf3229X+jgCA6mHKIgDAdDfffLNOnTqljz/+WFlZWfr4448rnK749ttvKywsTP3799fRo0ddPz179lRwcLBWr15do8/+9NNPZbPZ9OCDD7odf/jhh2UYhj777DO343l5eZKKNyOprYKCgko3K0lJSdG2bds0evRohYeHu4536dJF/fv316efflrmPceOHdPAgQMVEhKiDz/8sNb1ZWVlKTAw8Ky+HwCg+ghkAADTNWvWTP369dOyZcv07rvvqqioSDfeeGO5bffu3auMjAxFRESoWbNmbj/Z2dlKT0+v0WcfOHBAMTExCgkJcTteMoXvwIEDbsePHj0qSQoLC6vR55R28uRJ15TJimqSpPbt25c5FxcXp6NHjyonJ8ft+O9+9zvt2bNHJ0+eLHftW3UlJCTo8OHDmj59ug4ePKijR48qIyOj1tcDAFSONWQAAI9w++23695771VqaqoGDx6sRo0aldvO6XQqIiJCb7zxRrnnmzVrdg6rlH7++WdJqnANV3WkpqaqVatWdVPQabt379Znn32mm2++WQ8//LCWLFlSq+tMnDhRe/bs0cyZMzVjxow6rREAUBYjZAAAj/D73/9eVqtV69evr3C6oiS1adNGx44dU9++fdWvX78yP127dq3R57Zq1UqHDx9WVlaW2/Hdu3e7zpe2adMmRUVFqXnz5jX6nBIOh0P79u1zjcBVVJMk7dmzp8y53bt3q2nTpgoKCnI7/uGHH2rQoEGaNWuWli5dqlWrVtWqvoCAAL300kvq2LGjLrvsMiUmJuof//hHra4FAKgagQwA4BGCg4O1aNEiTZ8+XcOGDauw3c0336yioiLNnDmzzLnCwkKdPHmyRp87ZMgQFRUVacGCBW7H586dK4vF4tr5USpep7V69Wpdd911NfqM0j744AOdOnVK11xzTYVtoqOj1a1bN7322mtu32f79u1auXKlhgwZUuY9l19+uSTpT3/6ky699FL98Y9/1KlTp2pV45QpU3Tw4EG9/vrr6tevn3r27Fmr6wAAqsaURQCAxxg1alSVba688kr98Y9/1KxZs7Rt2zYNGDBAdrtde/fu1dtvv6358+dXuP6sPMOGDdPVV1+txx9/XD///LO6du2qlStX6oMPPtCECRPUpk0bSVJSUpIee+wxnTp1Ss2aNdPrr7/uusaPP/4oSXr99df1+9//vszolSTl5uZq2rRpeuGFF3TppZdqwIABldb197//XYMHD1ZCQoLuuece17b3YWFhZZ5XVprFYtHLL7+sbt26adq0aZo9e3a174VUvN3+3Llz9e9//7vOp1UCAMoikAEAvM7ixYvVs2dP/fOf/9Rf/vIX+fj4qHXr1rrzzjvVt2/fGl3LarXqww8/1NSpU/Xmm29qyZIlat26tf7+97/r4YcfdrX75z//qbVr10qS/vrXv5Z7rZEjRyo5ObncQHbixAm9+eabuu+++zRjxgxZrZVPUunXr5+WL1+uadOmaerUqbLb7bryyiv1zDPPKDY2ttL3xsXF6fHHH9fMmTN12223qXv37lXdBknFI4CjRo3SrbfeqjvuuKNa7wEAnB2LcTZbMQEA0ECMHj1akrR06dIK21gsFiUnJ5/Vhh8AgIaFNWQAAAAAYBKmLAIAUA2XXnpplW3uuOOOSp8vBgDAmZiyCAAAAAAmYcoiAAAAAJiEQAYAAAAAJmENWR1xOp06fPiwQkJCZLFYzC4HAAAAgEkMw1BWVpZiYmKqfMwJgayOHD58WC1atDC7DAAAAAAe4tChQ2revHmlbQhkdSQkJERS8U0PDQ01uZr6y+FwaOXKlRowYIDsdrvZ5aAK9Jd3ob+8D33mXegv70OfeRdP6q/MzEy1aNHClREqQyCrIyXTFENDQwlk55DD4VBgYKBCQ0NN/4uGqtFf3oX+8j70mXehv7wPfeZdPLG/qrOUiU09AAAAAMAkBDIAAAAAMAmBDAAAAABMQiADAAAAAJMQyAAAAADAJAQyAAAAADAJgQwAAAAATEIgAwAAAACTEMgAAAAAwCQEMgAAAAAwCYEMAAAAAExCIAMAAAAAkxDIAAAAAMAkPmYXgLpV5DS0Mfm40rPyFBHir0tiw2WzWswuCwAAAEA5CGT1yPLtKZrx0U6lZOS5jkWH+WvasHgN6hRtYmUAAAAAysOUxXpi+fYUjX19i1sYk6TUjDyNfX2Llm9PMakyAAAAABUhkNUDRU5DMz7aKaOccyXHZny0U0XO8loAAAAAMAuBrB7YmHy8zMhYaYaklIw8bUw+fv6KAgAAAFAlAlk9kJ5VcRirTTsAAAAA5weBrB6ICPGv03YAAAAAzg8CWT1wSWy4osP8VdHm9hYV77Z4SWz4+SwLAAAAQBUIZPWAzWrRtGHxklRhKJs2LJ7nkQEAAAAehkBWTwzqFK1Fd/ZQVJj7tMTGgXYturMHzyEDAAAAPBAPhq5HBnWKVv/4KG1MPq55n/+oDcnHdWefVoQxAAAAwEMxQlbP2KwWJbRpooEdoyRJu1OzTK4IAAAAQEUIZPVUXHSoJGlXSqbJlQAAAACoCIGsnoo/Hch+OXFKmXkOk6s
"text/plain": [
"<Figure size 1000x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x600 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from sklearn.metrics import silhouette_score\n",
"from sklearn.cluster import KMeans\n",
"\n",
"# Метод локтя\n",
"inertias = []\n",
"clusters_range = range(1, 15)\n",
"\n",
"for i in clusters_range:\n",
" kmeans = KMeans(n_clusters=i, random_state=42)\n",
" kmeans.fit(data_scaled)\n",
" inertias.append(kmeans.inertia_)\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.plot(clusters_range, inertias, marker='o')\n",
"plt.title('Метод локтя')\n",
"plt.xlabel('Количество кластеров')\n",
"plt.ylabel('Инерция')\n",
"plt.grid()\n",
"plt.show()\n",
"\n",
"# Коэффициент силуэта\n",
"silhouette_scores = []\n",
"\n",
"for i in clusters_range[2:]:\n",
" kmeans = KMeans(n_clusters=i, random_state=42)\n",
" labels = kmeans.fit_predict(data_scaled)\n",
" silhouette_scores.append(silhouette_score(data_scaled, labels))\n",
"\n",
"plt.figure(figsize=(10, 6))\n",
"plt.plot(clusters_range[2:], silhouette_scores, marker='o')\n",
"plt.title('Коэффициенты силуэта')\n",
"plt.xlabel('Количество кластеров')\n",
"plt.ylabel('Коэффициент силуэта')\n",
"plt.grid()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Метод локтя помогает визуализировать точку, где прирост качества кластеров начинает замедляться.\n",
"* Коэффициент силуэта позволяет оценить четкость группировки, при этом значения, близкие к 1, указывают на хорошее разделение кластеров.\n",
"\n",
"**В данном случае видно, что:**\n",
"1. Оптимальное количество кластеров находится в районе 2-4.\n",
"2. В коэффициенте силуэта то же - четкость группировки наиболее близка к единице до значения 4. Т.е. для 1-4 кластеров."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Применим кластерный анализ для неирархического алгоритма кластеризации:\n",
"Неирархическая кластеризация — это способ организации данных, при котором объекты делятся на группы (в нашем случае — на k кластеров) по определённым признакам, таким как расстояние или сходство. В отличие от иерархической кластеризации, которая создаёт древовидную структуру, неиерархическая кластеризация работает с фиксированным количеством групп и сразу распределяет объекты по ним.\n",
"\n",
"K-Means — это популярный алгоритм кластеризации, который делит набор данных на K различных кластеров на основе их характеристик. Основная идея заключается в том, чтобы минимизировать дисперсию (различия) внутри кластеров и максимизировать дисперсию между ними.\n",
"\n",
"Выбираем K случайных центров кластеров (центроидов). Каждая точка данных присоединяется к ближайшему центроиду (кластеру). После того как все точки были распределены по кластерам, новые центры вычисляются как средние значения всех точек в кластер. Процесс повторяется, пока центроиды не перестанут изменяться или разница становится незначительной."
]
},
{
"cell_type": "code",
"execution_count": 117,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 1000x700 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"kmeans = KMeans(n_clusters=3, random_state=42)\n",
"kmeans_labels = kmeans.fit_predict(data_scaled)\n",
"\n",
"plt.figure(figsize=(10, 7))\n",
"sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=kmeans_labels, palette='viridis', alpha=0.6)\n",
"plt.title('KMeans Clustering')\n",
"plt.xlabel('Первая компонентa PCA')\n",
"plt.ylabel('Вторая компонентa PCA')\n",
"plt.legend(title='Кластер')\n",
"plt.grid()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Таким образом, мы разделили данные на три кластера по волатильности акций в разные дни."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Применим кластерный анализ для ирархического алгоритма кластеризации:\n",
"Иерархическая кластеризация — метод машинного обучения, предназначенный для группировки объектов (точек данных) на основе их схожести или расстояния друг от друга. Основная идея заключается в создании структуры кластеров в виде дерева (дендрограммы), которое показывает, как объекты группируются на разных уровнях. Дендрограмма имеет следующий ключевые элементы:\n",
"\n",
"* Индексы образцов: По оси X указаны индексы наблюдений (дней), которые были кластеризованы.\n",
"* Расстояние: По оси Y указаны расстояния (меры различия) между объединяемыми кластерами. Чем выше значение, тем больший различие между объединяемыми группами данных.\n",
"\n",
"Низкие значения на оси Y (ближе к нулю) указывают на то, что кластеры, объединяемые на этих уровнях, имеют очень похожие характеристики.\n",
"Высокие значения на оси Y предполагают большие различия в характеристиках между объединяемыми кластерами."
]
},
{
"cell_type": "code",
"execution_count": 118,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA1UAAAJ0CAYAAAD6eAnhAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACDFUlEQVR4nO3dd3wUdeL/8fduyqYnJCEJkdACUkUQpVoAQZqdEwueoBx4HsIhVqygp1jwxFOwnAqo8FVQxIKioIANOBQQEERAOoQAIb0n8/uD3467yaZOQjbk9Xw88oCdmZ39TNndee+njM0wDEMAAAAAgGqx13UBAAAAAKA+I1QBAAAAgAWEKgAAAACwgFAFAAAAABYQqgAAAADAAkIVAAAAAFhAqAIAAAAACwhVAAAAAGABoQoAAAAALCBUAQAAbd26VUuWLDEfb9q0SUuXLq27AgFAPUKoAs5AH3zwgWw2m8e/Tp061XXxAHihjIwM3X777Vq7dq127typf/7zn9qyZUtdFwsA6gXfui4AgNrz4IMPqn379ubjJ598sg5LA8Cb9erVy/yTpLPPPltjx46t41IBQP1AqALOYAMHDlTfvn3Nx2+88YaOHz9edwUC4NWWLFmibdu2KScnR+ecc478/f3rukgAUC/Q/A84A+Xn50uS7PbKvcVTU1M1adIkJSQkyOFwqHXr1nrmmWdUXFxsLrN3717ZbDbNmDGj1PM7derkFt6cpk6d6rEJYsll+/btq06dOunnn39W7969FRgYqJYtW+rVV18ttc7k5GSNGTNGsbGxCggI0Lnnnqt58+a5LeMsq6e/d999V5I0d+5c2Ww2ffvtt7r99tsVFRWlsLAw3XLLLTp58qTb+j7++GMNGzZM8fHxcjgcSkxM1BNPPKGioqJS22Gz2XT11VeXKvftt99eqvmlazld+7JIUm5urho1alRqn+/bt0//+Mc/1LZtWwUGBioqKkrXXXed9u7dW+o1PZkxY4Z69+6tqKgoBQYGqlu3bvrggw9KLVfW/vvXv/7lttzo0aM9Ljd16lS35b755htddNFFCg4OVkREhK666ipt377dbRnn+RITE6OCggK3ef/3f/9nrrvkDwNffPGFue7Q0FANGzZMv/76a6lyhoSE6I8//tCgQYMUHBys+Ph4Pf744zIMo8r7qKz9U/IcX7VqlWw2m1atWuX2/GHDhpXaTyXfL6GhoerevXupc8P5filpxowZstlspc6F2bNnq2PHjnI4HIqPj9f48eOVmppaap3OMnfo0EHdunXTL7/8YpalIq7Pd3ryySdlt9u1YMECt+nlvT9Lbk9lzlVJevfdd9W9e3cFBQWpUaNGuvjii/XVV19Jklq0aFHusWrRooW5nuLiYs2cOVMdO3ZUQECAYmNjdfvtt5f6TGjRooUuv/xyffXVV+rSpYsCAgLUoUMHLV682G055+eM6zEpLi5W586dZbPZNHfuXHP61KlT1aFDB4WEhCgsLEw9e/Ysdey/++47XXfddWrWrJkcDocSEhJ01113KScnx2055/lekrNpuOv5WJ1z1FVmZqbi4uI8rgNoSKipAs5AzlDlcDgqXDY7O1uXXHKJDh06pNtvv13NmjXTjz/+qClTpujIkSOaOXOm5fK88sor5hf8lClTPC5z8uRJDR06VCNGjNCNN96ohQsX6o477pC/v79uu+02SVJOTo769u2rXbt26c4771TLli21aNEijR49WqmpqfrnP//pts4bb7xRQ4cOdZvWp08ft8d33nmnIiIiNHXqVO3YsUOvvPKK9u3bZ15oSKcujEJCQjR58mSFhITom2++0aOPPqr09HQ999xzbusLCAjQ0qVLlZycrJiYGLPc77//vgICAjxue0BAgObMmeMWxhYvXqzc3NxSy65fv14//vijbrjhBjVt2lR79+7VK6+8or59+2rbtm0KCgry+BpOL774oq688kqNHDlS+fn5eu+993Tdddfps88+07Bhw9yWHThwoG655Ra3aV26dCm1zujoaL3wwgvm47/+9a9u81esWKEhQ4aoVatWmjp1qnJycvTSSy+pT58+2rBhg9tFrXSqb89nn32ma665xpw2Z84cBQQElNon77zzjkaNGqVBgwbpmWeeUXZ2tl555RVdeOGF2rhxo9u6i4qKNHjwYPXs2VPPPvusli1bpscee0yFhYV6/PHHq7SP3nnnHXP57777Tq+//rpeeOEFRUdHS5JiY2NL7Senb7/9Vp9//nmZ853rPn78uGbPnq3rrrtOW7duVdu2bct8TlmmTp2qadOmacCAAbrjjjvMc3z9+vX64Ycf5OfnV+Zz77///iq/ntOcOXP08MMP6/nnn9dNN93kcZlx48bpoosuknTqfP/oo4/c5lf2XJ02bZqmTp2q3r176/HHH5e/v7/WrVunb775RpdddplmzpypzMxMSdL27dv11FNPuTWNdg0ft99+u+bOnatbb71VEydO1J49e/Tyyy9r48aNpfbXzp07df311+vvf/+7Ro0apTlz5ui6667TsmXLNHDgwDL3zTvvvOOxr1pWVpauueYatWjRQjk5OZo7d66GDx+uNWvWqHv37pKkRYsWKTs7W3fccYeioqL0v//9Ty+99JIOHjyoRYsWlXtMqqKic9TV888/r6NHj9bYawP1lgHgjDNz5kxDkvHLL7+4Tb/kkkuMjh07uk174oknjODgYOP33393m/7AAw8YPj4+xv79+w3DMIw9e/YYkoznnnuu1Ot17NjRuOSSS0pNf/DBBw1JxvHjx8td9pJLLjEkGc8//7w5LS8vz+jSpYsRExNj5Ofnu23Xu+++ay6Xn59v9OrVywgJCTHS09MrLKvTnDlzDElGt27dzPUbhmE8++yzhiTj448/NqdlZ2eXev7tt99uBAUFGbm5uW7b0bFjR6Nz587GjBkzzOnvvPOO0bRpU+Oiiy5y2//Oct54442Gr6+vkZSUZM679NJLjZtuuqnUdngqy5o1awxJxttvv13m9pb1/Pz8fKNTp05G//793aZLMsaPH1/h+kaOHGm0bNmy1HMfe+wx87HzOJ44ccKc9ssvvxh2u9245ZZbzGmPPfaYuT8uv/xyc/q+ffsMu91u3HjjjYYk49ixY4ZhGEZGRoYRERFhjB071u31k5KSjPDwcLfpo0aNMiQZEyZMMKcVFxcbw4YNM/z9/c11VmUfOTnPpT179pSat3LlSkOSsXLlSnNajx49jCFDhpTaT87td/XVV18ZkoyFCxea0zy9jw3DMJ577jm3ciQnJxv+/v7GZZddZhQVFZnLvfzyy4Yk46233nJbp+v78vPPPzckGYMHDy5VJk9cn7906VLD19fXuPvuuz0uu3PnTkOSMW/evHK3vTLHYefOnYbdbjeuueYat200jFPHtyRPx8Ppu+++MyQZ8+fPd5u+bNmyUtObN29uSDI+/PBDc1paWprRpEkTo2vXrua0kudGbm6u0axZM/P4z5kzp1Q5nJKTkw1Jbp8lnt7/06dPN2w2m7Fv3z5z2qhRo4zg4OBSyy5atKjU9ls5R5OTk43Q0FBzWU/7FWgoaP4HnIFOnDghSWrcuHGFyy5atEgXXXSRGjVqpOPHj5t/AwYMUFFRkb799lu35bOzs92WO378eKlmcE7OWoWyamhc+fr66vbbbzcf+/v76/bbb1dycrJ+/vlnSdLnn3+uuLg43XjjjeZyfn5+mjhxojIzM7V69eoKX6ekcePGuf36fMcdd8jX19ftV9rAwEDz/xkZGTp+/LguuugiZWdn67fffiu1zltvvVVz5swxH8+ZM0ejRo0qsznmeeedp44dO5o1FPv27dPKlSs1evToUsu6lqWgoEAnTpxQ69atFRERoQ0bNlS4va7PP3nypNLS0nTRRRdV6rme5Ofnl1sjeuTIEW3atEmjR49WZGSkOb1z584aOHCgx1/Db7vtNi1btkxJSUmSpHnz5qlXr146++yz3ZZbvny5UlNTdeONN7qdjz4+PurRo4dWrlxZat133nmn+X+bzaY777xT+fn5WrFihTm9pveRq8WLF2v9+vV6+umny1zGuR3bt2/
"text/plain": [
"<Figure size 1000x700 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from scipy.cluster.hierarchy import dendrogram, linkage\n",
"\n",
"linkage_matrix = linkage(data_scaled, method='ward')\n",
"plt.figure(figsize=(10, 7))\n",
"dendrogram(linkage_matrix)\n",
"plt.title('Дендрограмма агломеративной кластеризации')\n",
"plt.xlabel('Индекс образца')\n",
"plt.ylabel('Расстояние')\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Оптимальное количество кластеров - 3-4, т.к. они предполагают бОльшие различия в характеристиках между объединяемыми кластерами."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### **Произведем оценку кластеризации:**"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**1. Показатели четкости**\n",
"Для оценки четкости кластеризации мы можем использовать следующие показатели:\n",
"\n",
"* Коэффициент разбиения (Partition Coefficient): этот коэффициент измеряет, насколько пары объектов настроены в те же кластеры. Чем больше, тем лучше.\n",
"* Модифицированный коэффициент разбиения (Modified Partition Coefficient): схож с предыдущим, но принимающий во внимание распределение экземпляров в кластерах.\n",
"* Индекс четкости ( Davies-Bouldin Index ): мера, использующая средние расстояния между кластерами, чем меньше, тем лучше."
]
},
{
"cell_type": "code",
"execution_count": 122,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Коэффициент разбиения для KMeans: 0.438\n",
"Модифицированный коэффициент разбиения для KMeans: 0.438\n",
"Индекс четкости для KMeans: 1.025\n"
]
}
],
"source": [
"from sklearn.metrics import pairwise_distances, silhouette_score\n",
"import numpy as np\n",
"\n",
"def partition_coefficient(labels):\n",
" n = len(labels)\n",
" k = len(np.unique(labels))\n",
" partitions = np.zeros(k)\n",
"\n",
" for label in np.unique(labels):\n",
" partitions[label] = np.sum(labels == label)\n",
"\n",
" coefficient = np.sum(partitions**2) / n**2\n",
" return coefficient\n",
"\n",
"def modified_partition_coefficient(labels):\n",
" return partition_coefficient(labels)\n",
"\n",
"def davies_bouldin_index(X, labels):\n",
" # Сначала находим расстояния между кластерами\n",
" unique_labels = np.unique(labels)\n",
" centroids = np.array([X[labels == k].mean(axis=0) for k in unique_labels])\n",
" \n",
" db_index = 0\n",
" for i in range(len(centroids)):\n",
" max_ratio = 0\n",
" for j in range(len(centroids)):\n",
" if i != j:\n",
" dist_ij = np.linalg.norm(centroids[i] - centroids[j])\n",
" avg_dist_i = np.mean(pairwise_distances(X[labels == unique_labels[i]]))\n",
" avg_dist_j = np.mean(pairwise_distances(X[labels == unique_labels[j]]))\n",
" ratio = (avg_dist_i + avg_dist_j) / dist_ij\n",
" if ratio > max_ratio:\n",
" max_ratio = ratio\n",
" db_index += max_ratio\n",
" \n",
" return db_index / len(centroids)\n",
"\n",
"# Оценка KMeans\n",
"kmeans_coef = partition_coefficient(kmeans_labels)\n",
"kmeans_modified_coef = modified_partition_coefficient(kmeans_labels)\n",
"kmeans_db_index = davies_bouldin_index(data_scaled, kmeans_labels)\n",
"\n",
"print(f'Коэффициент разбиения для KMeans: {kmeans_coef:.3f}')\n",
"print(f'Модифицированный коэффициент разбиения для KMeans: {kmeans_modified_coef:.3f}')\n",
"print(f'Индекс четкости для KMeans: {kmeans_db_index:.3f}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**1. Коэффициент разбиения (Partitioning Coefficient)**\n",
"\n",
"**Значение: 0.438**\n",
"\n",
"Коэффициент разбиения измеряет степень разбиения данных на кластеры. Он вычисляется как среднее значение принадлежности объектов к кластерам. Его значение находится в диапазоне от 0 до 1:\n",
"* Значение, близкое к 1, указывает на то, что объекты четко разделены на кластеры, и объекты имеют высокую степень принадлежности только к одному кластеру.\n",
"* Значение, близкое к 0, говорит о том, что объекты имеют более равномерное распределение по кластерам и не могут быть однозначно отнесены к определенному кластеру.\n",
"\n",
"В данном случае - значение 0.438 указывает на то, что структура кластеров не совсем однозначная. Это может значить, что в некоторых случаях объекты имеют небольшую степень пересечения в кластерах, т.е., некоторые объекты могут \"принадлежать\" более чем одному кластеру. Это может быть признаком того, что данные не полностью отделимы.\n",
"\n",
"**2. Модифицированный коэффициент разбиения (Modified Partitioning Coefficient)**\n",
"\n",
"**Значение: 0.438**\n",
"\n",
"Модифицированный коэффициент разбиения — это усовершенствованная версия обычного коэффициента разбиения. Он также учитывает количество кластеров и приросты между кластерами, что делает его более устойчивым к изменениям в числе кластеров.\n",
"\n",
"В данном случае, поскольку модифицированный коэффициент разбиения равен обычному коэффициенту разбиения (0.438), это говорит о том, что структура кластеров аналогична той, что дают обычный коэффициент.\n",
"\n",
"**3. Индекс четкости (Fuzzy Silhouette Index)**\n",
"\n",
"**Значение: 1.025**\n",
"\n",
"Индекс четкости (или индекс очерченности) измеряет насколько хорошо отдельный объект находится в своем кластере по сравнению с другими кластерами. Это значение также варьируется от -1 до 1:\n",
"\n",
"* Значение близкое к 1 говорит о том, что объект явно принадлежит к своему кластеру и располагается далеко от других кластеров.\n",
"* Значение, близкое к 0, может указывать на неопределенность в принадлежности.\n",
"* Негативные значения означают, что объекты, вероятно, неправильно классифицированы и должны быть в другом кластере.\n",
"\n",
"В данном случае, значение индекса четкости 1.025 говорит о том, что объекты в кластерах четко различимы и хорошо расставлены в своих кластерах. Это указывает на высокое качество кластеризации и показательно, что объекты не пересекаются в соседних кластерах.\n",
"\n",
"**Итог:**\n",
"* Коэффициенты разбиения (обычный и модифицированный) показывают, что кластеры не абсолютно раздельны, но имеют достаточно согласованную структуру.\n",
"* Индекс четкости указывает на хорошее качество кластеризации и показатель того, что объекты четко располагаются в своих кластерах."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"2. **Энтропийный критерий:**\n",
"\n",
"Энтропийный критерий используется для измерения неопределенности в кластерах.\n",
"\n",
"Энтропия разбиения (Partition Entropy) измеряет количество информации, необходимое для определения кластеров."
]
},
{
"cell_type": "code",
"execution_count": 120,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Энтропия разбиения для KMeans: 1.366\n"
]
}
],
"source": [
"from sklearn.metrics import confusion_matrix\n",
"\n",
"def partition_entropy(labels):\n",
" # Отобразим метки на оси (кластеры)\n",
" cm = confusion_matrix(labels, labels)\n",
" total_samples = np.sum(cm)\n",
" probs = cm / total_samples\n",
"\n",
" entropy = -np.sum(probs[probs > 0] * np.log2(probs[probs > 0]))\n",
" return entropy\n",
"\n",
"# Оценка KMeans\n",
"kmeans_entropy = partition_entropy(kmeans_labels)\n",
"print(f'Энтропия разбиения для KMeans: {kmeans_entropy:.3f}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**Значение: 1.366**\n",
"\n",
"1. Уровень кластеризации:\n",
"* Энтропия равная нулю (H = 0) указывает на идеальную кластеризацию, где все объекты в одном кластере только принадлежат этому кластеру и нет неопределенности — все объекты четко разделены.\n",
"* Значение выше 0 указывает на наличие некоторой неопределенности. Чем выше энтропия, тем более «размытыми» являются кластеры. \n",
"* Значение энтропии 1.366 указывает на то, что в кластерах есть разнообразие — объекты не полностью разделены и структура кластеров может быть неоднородной.\n",
"2. Степень разброса:\n",
"* Высокое значение энтропии также может указывать на то, что в один кластер попали объекты разных классов. Это связано с тем, что для разных классов объектов доли приблизительно равны, что увеличивает неопределенность. Например, если в двух кластерах содержатся объекты, принадлежащие различным классам в схожих пропорциях, это увеличит значение энтропии.\n",
"\n",
"* Низкая энтропия (близкая к 0): Отличные кластеры, объекты четко разделены.\n",
"* Средняя энтропия (приблизительно от 0.5 до 1.5): Возможны смешанные кластеры, данные не слишком четко разделены.\n",
"* Высокая энтропия (> 1.5): Признак того, что много объектов смешаны между кластерами, и структура разрозненная.\n",
"\n",
"**Итог:**\n",
"Энтропия разбиения в 1.366 указывает на то, что алгоритм K-Means создал кластеры с определенной степенью неопределенности и смешения."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"3. **Другие критерии:**\n",
"\n",
"Для оценки качества кластеризации можно использовать показатели компактности и изолированности.\n",
"\n",
"* Показатель компактности и изолированности: измеряет, как плотно расположены точки в кластере и насколько они разные друг от друга."
]
},
{
"cell_type": "code",
"execution_count": 121,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Компактность для KMeans: 4.156\n",
"Изолированность для KMeans: 1.567\n"
]
}
],
"source": [
"def compactness_isolation(X, labels):\n",
" unique_labels = np.unique(labels)\n",
" \n",
" compactness = 0\n",
" isolation = 0\n",
" \n",
" for label in unique_labels:\n",
" cluster_points = X[labels == label]\n",
" compactness += np.mean(pairwise_distances(cluster_points))\n",
" \n",
" isolation = np.mean(pairwise_distances(X)) - compactness / len(unique_labels)\n",
" \n",
" return compactness, isolation\n",
"\n",
"# Оценка KMeans\n",
"compactness, isolation = compactness_isolation(data_scaled, kmeans_labels)\n",
"print(f'Компактность для KMeans: {compactness:.3f}')\n",
"print(f'Изолированность для KMeans: {isolation:.3f}')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"**1. Показатель компактности:**\n",
"\n",
"Показатель компактности измеряет, насколько близко расположены объекты внутри одного кластера. Он определяет, насколько хорошо группа объектов, принадлежащих к одному кластеру, соседствует друг с другом.\n",
"\n",
"* Среднее расстояние между точками в кластере: Чем меньше это расстояние, тем компактнее кластер.\n",
"* Сумма квадратов отклонений: Этот показатель измеряет отклонения точек от центра кластера (центроида).\n",
"\n",
"* Высокий уровень компактности (низкие значения метрик) указывает на то, что объекты в кластере очень схожи и имеют близкие значения.\n",
"* Низкая компактность сигнализирует о разбросе объектов в кластере и о том, что кластер может содержать разные категории данных.\n",
"\n",
"В данном случае значение 4.156 - В контексте моей бизнес-цели, чем ниже значение компактности, тем более однородными являются дни с похожими ценами на акции. Это позволяет лучше выявлять закономерности в движении цен, что важно для прогнозирования будущих рыночных условий. Данное значение может быть оценено как умеренно.\n",
"\n",
"**2. Показатель изолированности:**\n",
"\n",
"Изолированность измеряет, насколько различные кластеры друг от друга. Он показывает, насколько четко отделены кластеры и насколько они различны.\n",
"\n",
"* Среднее расстояние между центроидами кластеров: Чем больше это расстояние, тем более изолированными являются кластеры.\n",
"* Индекс silhouette: Данный индекс оценивает, насколько хорошо объект присвоен своему кластеру.\n",
"\n",
"* Высокая изолированность (большие расстояния между кластерами) указывает на четкое разделение классов.\n",
"* Низкая изолированность говорит о том, что кластеры могут пересекаться или быть слишком близко расположенными друг к другу, что может приводить к ошибкам в классификации объектов.\n",
"\n",
"В моей бизнес-задаче важно, чтобы дни с различной динамикой цен были четко отделены друг от друга, что позволит избежать путаницы между динамиками в разных группах. Значение 1.567 может указывать на то, что кластеры не сильно пересекаются."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "aimenv",
"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.5"
}
},
"nbformat": 4,
"nbformat_minor": 2
}