818 KiB
Raw Blame History

Цель работы

Мы будем кластеризовать автомобили, основываясь на их характеристиках, с целью выделения групп автомобилей с похожими свойствами. Это может быть полезно, например, для автосалонов или производителей для сегментации рынка.

загрузим датасет

In [1]:
import pandas as pd
df = pd.read_csv("..//static//csv//car_price_prediction.csv", sep=",")
df
Out[1]:
ID Price Levy Manufacturer Model Prod. year Category Leather interior Fuel type Engine volume Mileage Cylinders Gear box type Drive wheels Doors Wheel Color Airbags
0 45654403 13328 1399 LEXUS RX 450 2010 Jeep Yes Hybrid 3.5 186005 km 6.0 Automatic 4x4 04-May Left wheel Silver 12
1 44731507 16621 1018 CHEVROLET Equinox 2011 Jeep No Petrol 3 192000 km 6.0 Tiptronic 4x4 04-May Left wheel Black 8
2 45774419 8467 - HONDA FIT 2006 Hatchback No Petrol 1.3 200000 km 4.0 Variator Front 04-May Right-hand drive Black 2
3 45769185 3607 862 FORD Escape 2011 Jeep Yes Hybrid 2.5 168966 km 4.0 Automatic 4x4 04-May Left wheel White 0
4 45809263 11726 446 HONDA FIT 2014 Hatchback Yes Petrol 1.3 91901 km 4.0 Automatic Front 04-May Left wheel Silver 4
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
19232 45798355 8467 - MERCEDES-BENZ CLK 200 1999 Coupe Yes CNG 2.0 Turbo 300000 km 4.0 Manual Rear 02-Mar Left wheel Silver 5
19233 45778856 15681 831 HYUNDAI Sonata 2011 Sedan Yes Petrol 2.4 161600 km 4.0 Tiptronic Front 04-May Left wheel Red 8
19234 45804997 26108 836 HYUNDAI Tucson 2010 Jeep Yes Diesel 2 116365 km 4.0 Automatic Front 04-May Left wheel Grey 4
19235 45793526 5331 1288 CHEVROLET Captiva 2007 Jeep Yes Diesel 2 51258 km 4.0 Automatic Front 04-May Left wheel Black 4
19236 45813273 470 753 HYUNDAI Sonata 2012 Sedan Yes Hybrid 2.4 186923 km 4.0 Automatic Front 04-May Left wheel White 12

19237 rows × 18 columns

In [ ]:

Предобработка данных

Мы удалим неинформативные столбцы, такие как ID, преобразуем категориальные переменные в числовые (one-hot encoding), а также нормализуем данные для дальнейшего анализа.

In [2]:
# Удаляем неинформативный столбец ID
df = df.drop(columns=["ID"])

# Преобразование категориальных данных в числовые с помощью one-hot encoding
df = pd.get_dummies(df, drop_first=True)

# Нормализация числовых данных
from sklearn.preprocessing import StandardScaler

scaler = StandardScaler()
df_scaled = scaler.fit_transform(df)

Визуализация данных с помощью PCA (снижение размерности)

Для визуализации мы применим метод PCA, который уменьшит количество измерений до двух, сохраняя при этом максимальное количество информации.
Ключевые термины:

  • PCA (Principal Component Analysis) — метод снижения размерности, который находит новые оси в данных, вдоль которых разброс максимален, и проецирует данные на эти оси.
  • Снижение размерности — процесс упрощения данных за счёт уменьшения числа признаков.
In [3]:
# Импортируем PCA и визуализируем данные
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt

# Применяем PCA для снижения размерности до 2
pca = PCA(n_components=2)
df_pca = pca.fit_transform(df_scaled)

# Визуализация
plt.figure(figsize=(8, 6))
plt.scatter(df_pca[:, 0], df_pca[:, 1], c='blue', edgecolor='k', alpha=0.6)
plt.title("PCA: Визуализация данных после снижения размерности")
plt.xlabel("Главная компонента 1")
plt.ylabel("Главная компонента 2")
plt.show()
No description has been provided for this image

Количество кластеров

Количество кластеров напрямую влияет на результаты кластеризации, так как оно определяет, сколько групп или сегментов будет выделено в данных. Оптимальный выбор количества кластеров важен, чтобы обеспечить баланс между точностью кластеризации и интерпретируемостью результатов.

Зачем выбирать количество кластеров?

Оптимальная сегментация данных

Разное количество кластеров может приводить к слишком мелкому делению (много мелких кластеров) или слишком крупному (слишком обобщённые кластеры). -Слишком мало кластеров: важные различия в данных могут быть упущены. -Слишком много кластеров: анализ становится сложным, и кластеры могут быть избыточно раздроблены.

Интерпретируемость результатов

Оптимальное количество кластеров делает результаты понятными и полезными. Например, выделение 3-5 кластеров может быть удобно для анализа, тогда как 15-20 кластеров усложнят интерпретацию.

Избежание переобучения или недообучения

Количество кластеров влияет на обобщающую способность модели. Слишком большое количество кластеров может привести к переобучению (модель подстраивается под шум), а слишком малое — к упрощению и игнорированию важных данных.

Практическая применимость

В бизнес-задачах обычно требуется понятное разделение данных. Например, если мы сегментируем клиентов, 3-5 кластеров проще использовать для таргетинга, чем 20.

Определение оптимального количества кластеров

Для выбора количества кластеров мы применим:

  • Метод локтя — измеряет инерцию (размерность ошибок внутри кластеров).
  • Коэффициент силуэта — показывает, насколько хорошо объекты распределены между кластерами.

Ключевые термины:

  • Инерция — сумма квадратов расстояний от точек до центроидов их кластеров. Чем меньше, тем лучше.
  • Коэффициент силуэта — оценивает плотность внутри кластеров и разницу между ними (от -1 до 1).
In [4]:
# Метод локтя
from sklearn.cluster import KMeans

border_l = 2
border_r = 5

inertia = []
for k in range(border_l, border_r):
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(df_scaled)
    inertia.append(kmeans.inertia_)

# Визуализация метода локтя
plt.figure(figsize=(8, 6))
plt.plot(range(border_l, border_r), inertia, marker='o')
plt.title('Метод локтя для выбора количества кластеров')
plt.xlabel('Количество кластеров')
plt.ylabel('Инерция')
plt.show()

# Коэффициент силуэта
from sklearn.metrics import silhouette_score

silhouette_scores = []
for k in range(border_l, border_r):
    kmeans = KMeans(n_clusters=k, random_state=42)
    kmeans.fit(df_scaled)
    score = silhouette_score(df_scaled, kmeans.labels_)
    silhouette_scores.append(score)

# Визуализация коэффициента силуэта
plt.figure(figsize=(8, 6))
plt.plot(range(border_l, border_r), silhouette_scores, marker='o')
plt.title('Коэффициент силуэта для различных кластеров')
plt.xlabel('Количество кластеров')
plt.ylabel('Коэффициент силуэта')
plt.show()
No description has been provided for this image
No description has been provided for this image

Кластеризация с помощью K-means

После выбора оптимального числа кластеров (например, 3), мы применим K-means для кластеризации и визуализируем результаты.
Ключевой термин:

  • K-means — алгоритм кластеризации, который группирует данные вокруг центров (центроидов) кластеров.
In [5]:
# Кластеризация с помощью K-means
optimal_clusters = 3
kmeans = KMeans(n_clusters=optimal_clusters, random_state=42)
df['Cluster'] = kmeans.fit_predict(df_scaled)

# Визуализация кластеров с использованием PCA
plt.figure(figsize=(8, 6))
plt.scatter(df_pca[:, 0], df_pca[:, 1], c=df['Cluster'], cmap='viridis', edgecolor='k', alpha=0.6)
plt.title("Кластеры, определенные K-means (PCA)")
plt.xlabel("Главная компонента 1")
plt.ylabel("Главная компонента 2")
plt.colorbar(label='Кластер')
plt.show()
No description has been provided for this image

Иерархическая кластеризация

Применяем иерархическую кластеризацию для сравнения. Также строим дендрограмму.
Ключевой термин:

  • Иерархическая кластеризация — метод, который строит древовидную структуру кластеров (дендрограмму).
In [6]:
from sklearn.cluster import AgglomerativeClustering
from scipy.cluster.hierarchy import dendrogram
import numpy as np
import matplotlib.pyplot as plt

# Применение иерархической кластеризации
hierarchical = AgglomerativeClustering(n_clusters=optimal_clusters, compute_distances=True)
df['Hierarchical Cluster'] = hierarchical.fit_predict(df_scaled)

# Функция для получения матрицы linkage
def get_linkage_matrix(model: AgglomerativeClustering) -> np.ndarray:
    counts = np.zeros(model.children_.shape[0])  # type: ignore
    n_samples = len(model.labels_)
    for i, merge in enumerate(model.children_):  # type: ignore
        current_count = 0
        for child_idx in merge:
            if child_idx < n_samples:
                current_count += 1
            else:
                current_count += counts[child_idx - n_samples]
        counts[i] = current_count

    return np.column_stack([model.children_, model.distances_, counts]).astype(float)

# Построение дендрограммы
linkage_matrix = get_linkage_matrix(hierarchical)
plt.figure(figsize=(12, 8))
dendrogram(linkage_matrix)
plt.title("Дендограмма, восстановленная из модели AgglomerativeClustering")
plt.xlabel("Индексы объектов")
plt.ylabel("Евклидово расстояние")
plt.show()


# Визуализация кластеров
plt.figure(figsize=(8, 6))
plt.scatter(df_pca[:, 0], df_pca[:, 1], c=df['Hierarchical Cluster'], cmap='viridis', edgecolor='k', alpha=0.6)
plt.title("Кластеры, определенные иерархической кластеризацией (PCA)")
plt.xlabel("Главная компонента 1")
plt.ylabel("Главная компонента 2")
plt.colorbar(label='Кластер')
plt.show()
No description has been provided for this image
No description has been provided for this image

Оценка качества кластеризации

Оценим качество кластеров, сравнив коэффициенты силуэта для двух методов.

In [7]:
# Оценка качества
silhouette_kmeans = silhouette_score(df_scaled, df['Cluster'])
silhouette_hierarchical = silhouette_score(df_scaled, df['Hierarchical Cluster'])

print(f"Коэффициент силуэта для K-means: {silhouette_kmeans:.4f}")
print(f"Коэффициент силуэта для иерархической кластеризации: {silhouette_hierarchical:.4f}")
Коэффициент силуэта для K-means: 0.0203
Коэффициент силуэта для иерархической кластеризации: -0.1579

Выводы

  1. На основании коэффициента силуэта можно определить, какой метод лучше подходит для кластеризации.
  2. K-means и иерархическая кластеризация дают схожие результаты, но могут отличаться по точности в зависимости от структуры данных.

Как интерпретировать значения?

Коэффициент силуэта находится в диапазоне от -1 до 1:

  • Близко к 1: Отличные кластеры (чётко разделённые).
  • Около 0: Кластеры пересекаются или слабо разделены.
  • Меньше 0: Объекты часто попадают не в свои кластеры.

Коэффициент силуэта для K-means: 0.0203

Это значение очень низкое, почти около нуля. Низкое значение силуэта указывает на то, что границы между кластерами размыты.
Причины низкого значения:

  • Число кластеров выбрано неверно (слишком мало или слишком много).
  • Данные имеют сложную структуру и не разделяются чётко.
  • Возможно, метод K-means плохо подходит для данных с такой структурой.

Коэффициент силуэта для иерархической кластеризации: -0.1579

Отрицательное значение коэффициента силуэта — это плохой результат. Объекты часто ближе к центру соседних кластеров, чем к центру своего собственного.
Причины отрицательного значения:

  • Структура данных не подходит для выбранного метода кластеризации.
  • Иерархическая кластеризация с данным количеством кластеров не смогла эффективно сгруппировать данные.