1.6 MiB
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.model_selection import train_test_split
from sklearn import set_config
set_config(transform_output="pandas")
random_state = 42
# Подключим датафрейм и выгрузим данные
df = pd.read_csv("data/house_data.csv")
df.columns
Удалим ненужные столбцы¶
columns_to_drop = ['id', 'date', 'grade', 'yr_renovated', 'sqft_living15', 'lat', 'long', 'sqft_lot15', 'sqft_above', 'zipcode']
df_cleaned = df[['sqft_living', 'price', 'floors', 'bathrooms', 'bedrooms', 'sqft_basement']]
df_cleaned.head(10)
Визуализация данных¶
sns.set(style="whitegrid")
plt.figure(figsize=(16, 12))
#Отношение цены к спальным комнатам
plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['bedrooms'], color="brown")
plt.title('Price vs Bedrooms')
plt.xlabel('Price')
plt.ylabel('Bedrooms')
#Отношение цены к квадратному футу жилплощади
plt.subplot(2, 2, 2)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_living'], color="pink")
plt.title('Price vs sqft_living')
plt.xlabel('Price')
plt.ylabel('sqft_living')
#Отношение цены к количеству этажей
plt.subplot(2, 2, 3)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['floors'], color="green")
plt.title('Price vs floors')
plt.xlabel('Price')
plt.ylabel('floors')
#Отношение цены к размеру подвала
plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_basement'], color="orange")
plt.title('Price vs sqft_basement')
plt.xlabel('Price')
plt.ylabel('sqft_basement')
Иерархическая агломеративная кластеризация¶
from scipy.cluster.hierarchy import dendrogram, linkage
linkage_matrix = linkage(df_cleaned, method='ward')
plt.figure(figsize=(10, 7))
dendrogram(linkage_matrix)
plt.title('Дендрограмма агломеративной кластеризации')
plt.xlabel('Индекс образца')
plt.ylabel('Расстояние')
plt.show()
Получение результатов иерархической кластеризации¶
from scipy.cluster import hierarchy
#Получение результатов
result = hierarchy.fcluster(linkage_matrix, 10, criterion="distance")
display(result)
display(df_cleaned)
sns.set(style="whitegrid")
plt.figure(figsize=(16, 24))
#Вывод до кластеризации и после
#Отношение цены к спальным комнатам
plt.subplot(4, 2, 1)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['bedrooms'], color="brown")
plt.title('Price vs Bedrooms')
plt.xlabel('Price')
plt.ylabel('Bedrooms')
plt.subplot(4, 2, 2)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['bedrooms'], color="brown", hue=result)
plt.title('Price vs Bedrooms')
plt.xlabel('Price')
plt.ylabel('Bedrooms')
#Отношение цены к квадратному футу жилплощади
plt.subplot(4, 2, 3)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_living'], color="pink")
plt.title('Price vs sqft_living')
plt.xlabel('Price')
plt.ylabel('sqft_living')
plt.subplot(4, 2, 4)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_living'], color="pink", hue=result)
plt.title('Price vs sqft_living')
plt.xlabel('Price')
plt.ylabel('sqft_living')
#Отношение цены к количеству этажей
plt.subplot(4, 2, 5)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['floors'], color="green")
plt.title('Price vs floors')
plt.xlabel('Price')
plt.ylabel('floors')
plt.subplot(4, 2, 6)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['floors'], color="green", hue=result)
plt.title('Price vs floors')
plt.xlabel('Price')
plt.ylabel('floors')
#Отношение цены к размеру подвала
plt.subplot(4, 2, 7)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_basement'], color="orange")
plt.title('Price vs sqft_basement')
plt.xlabel('Price')
plt.ylabel('sqft_basement')
plt.subplot(4, 2, 8)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_basement'], color="orange", hue=result)
plt.title('Price vs sqft_basement')
plt.xlabel('Price')
plt.ylabel('sqft_basement')
Неиерархическая четкая кластеризация (k-means)¶
Выделим признаки, нужные для кластеризации
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
features_used = ['sqft_living', 'price', 'floors', 'bathrooms', 'bedrooms', 'sqft_basement']
data_to_scale = df_cleaned
random_state = 42
kmeans = KMeans(n_clusters=4, random_state=random_state)
labels = kmeans.fit_predict(data_to_scale)
centers = kmeans.cluster_centers_
plt.figure(figsize=(16, 12))
plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['bedrooms'], hue=labels,)
plt.scatter(centers[:, 1], centers[:, 4], s=40, color='red')
plt.title('KMeans Clustering: bedrooms vs Price')
plt.subplot(2, 2, 2)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_living'], hue=labels,)
plt.scatter(centers[:, 1], centers[:, 0], s=40, color='red')
plt.title('KMeans Clustering: sqft_living vs Price')
plt.subplot(2, 2, 3)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['floors'], hue=labels,)
plt.scatter(centers[:, 1], centers[:, 2], s=40, color='red')
plt.title('KMeans Clustering: floors vs Price')
plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned['price'], y=df_cleaned['sqft_basement'], hue=labels,)
plt.scatter(centers[:, 1], centers[:, 5], s=40, color='red')
plt.title('KMeans Clustering: sqft_basement vs Price')
display(centers)
Понижение размерности¶
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(data_to_scale)
# Визуализация сокращенных данных
plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
sns.scatterplot(x=reduced_data.iloc[:, 0], y=reduced_data.iloc[:, 1], hue=result)
plt.title('PCA reduced data: Agglomerative Clustering')
plt.subplot(1, 2, 2)
sns.scatterplot(x=reduced_data.iloc[:, 0], y=reduced_data.iloc[:, 1], hue=labels)
plt.title('PCA reduced data: KMeans Clustering')
Выбор количества кластеров на основе инерции¶
import math
from sklearn import cluster
max_clusters = int(math.sqrt(len(df)))
clusters_range = range(1, 11)
kmeans_per_k = [
cluster.KMeans(n_clusters=k, random_state=random_state).fit(data_to_scale)
for k in clusters_range
]
inertias = [model.inertia_ for model in kmeans_per_k]
plt.figure(figsize=(10, 6))
plt.plot(clusters_range, inertias, marker='o')
plt.xlabel('Количество кластеров')
plt.ylabel('Инерция')
plt.grid(True)
plt.show()
Расчет коэффициентов силуэта¶
Средний коэффициент силуэта (silhouette score) используется для оценки качества кластеризации. Его значение лежит в диапазоне от -1 до 1. Что означают различные значения:
0.7–1.0 : Кластеры хорошо разделены и компактны. Это отличный результат кластеризации.
0.5-0.7 : Кластеры четко различимы, но есть некоторое пересечение между ними. Это хороший результат.
0.25-0.5 : Кластеры перекрываются, что указывает на менее четкую границу между группами. Качество кластеризации удовлетворительное, но может потребоваться уточнение числа кластеров или доработка данных.
Близко к 0.0: Кластеры сильно перекрываются или распределение данных не позволяет выделить четкие группы. В этом случае нужно пересмотреть выбор числа кластеров, алгоритм или исходные данные.
Меньше 0.0: Плохая кластеризация: точки ближе к центрам чужих кластеров, чем к своим. Это сигнал о том, что данные плохо структурированы для текущей кластеризации.
from sklearn.metrics import silhouette_score
silhouette_scores = []
for i in clusters_range[1:]:
kmeans = KMeans(n_clusters=i, random_state=random_state)
labels = kmeans.fit_predict(data_to_scale)
score = silhouette_score(data_to_scale, labels)
silhouette_scores.append(score)
plt.figure(figsize=(10, 6))
plt.plot(clusters_range[1:], silhouette_scores, marker='o')
plt.xlabel('Количество кластеров')
plt.ylabel('Инерция')
plt.grid(True)
plt.show()
# Применение K-Means
kmeans = KMeans(n_clusters=4, random_state=42)
df_clusters = kmeans.fit_predict(data_to_scale)
# Оценка качества кластеризации
silhouette_avg = silhouette_score(data_to_scale, df_clusters)
print(f'Средний коэффициент силуэта: {silhouette_avg:.3f}')
# Визуализация кластеров
pca = PCA(n_components=2)
df_pca = pca.fit_transform(data_to_scale)
plt.figure(figsize=(10, 7))
sns.scatterplot(x=df_pca.iloc[:, 0], y=df_pca.iloc[:, 1], hue=df_clusters, palette='viridis', alpha=0.7)
plt.title('Визуализация кластеров с помощью K-Means')
plt.xlabel('Первая компонентa PCA')
plt.ylabel('Вторая компонентa PCA')
В нашем случае средний коэффициент силуэта: 0.593 указывает на то, что кластеры хорошо разлечимы, но есть небольшие пересечения. Данный результат соответствует хорошему состоянию.