2.5 MiB
Начинаем работу ...¶
Датафрейм: Продажа домов в округе Кинг (вариант-6)
https://www.kaggle.com/datasets/harlfoxem/housesalesprediction
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.cluster import KMeans
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
# Подключим датафрейм и выгрузим данные
df = pd.read_csv(".//static//csv//kc_house_data.csv")
print(df.columns)
df.head()
df.describe()
# Процент пропущенных значений признаков
for i in df.columns:
null_rate = df[i].isnull().sum() / len(df) * 100
if null_rate > 0:
print(f'{i} Процент пустых значений: %{null_rate:.2f}')
print(df.isnull().sum())
print(df.isnull().any())
# Проверка типов столбцов
df.dtypes
Атрибуты¶
id - уникальный идентификатор дома
date - Дата продажи дома
price - Цена дома в долларах США
bedrooms - Количество спален в доме
bathrooms - Количество ванных комнат, включая дробные значения (н, 2.5 означает 2 ванны и 1 туалет)
sqft_living - Жилая площадь дома (в кв. футах)
sqft_lot - Общая площадь участка
floors - Количество этажей в доме
waterfront - Есть ли вид на воду (1 - да, 0 - нет)
view - Оценка вида дома (0-4)
condition - Оценка состояния дома (1 - плохое, 5 - отличное)
grade - Оценка качество дома по архитектурным и строительным стандартам (1-13)
sqft_basement - Площадь подвала дома.
yr_built - Год постройки дома
yr_renovated - Год послежней реновации дома (0, если реновация не проводилась)
zipcode - Почтовый индекс местоположения дома
sqft_living15 - Средняя жилая площадб домов в 15 ближайших соседях
price_category - Категория цены дома (low, medium, high)
Цель: Кластеризация домов на группы для определения схожих ценовых категорий и характеристик.
К примеру, Группировка домов для анализа рыночных трендов. Определение похожих групп домов для маркетинговых или инвестиционных целей.
Очистка данных¶
# Удалим несущественные столбцы
columns_to_drop = ['id', 'date', 'grade', 'yr_renovated', 'sqft_living15', 'lat', 'long', 'sqft_lot15', 'sqft_above', 'zipcode']
df_cleaned = df.drop(columns=columns_to_drop)
print(df_cleaned.head()) # Вывод очищенного DataFrame
Визуализация парных взаимосвязей¶
# Настройка стиля графиков
sns.set(style="whitegrid")
# Создание фигуры
plt.figure(figsize=(16, 12))
# График 1: Площадь vs Цена
plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned['sqft_living'], y=df_cleaned['price'], alpha=0.6, color='blue')
plt.title('Living Area (sqft) vs Price')
plt.xlabel('Living Area (sqft)')
plt.ylabel('Price')
# График 2: Количество спален vs Цена
plt.subplot(2, 2, 2)
sns.scatterplot(x=df_cleaned['floors'], y=df_cleaned['price'], alpha=0.6, color='green')
plt.title('Floors vs Price')
plt.xlabel('Floors')
plt.ylabel('Price')
# График 3: Количество ванных комнат vs Цена
plt.subplot(2, 2, 3)
sns.scatterplot(x=df_cleaned['bathrooms'], y=df_cleaned['price'], alpha=0.6, color='red')
plt.title('Bathrooms vs Price')
plt.xlabel('Bathrooms')
plt.ylabel('Price')
# График 4: Площадь участка vs Цена
plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned['sqft_lot'], y=df_cleaned['price'], alpha=0.6, color='purple')
plt.title('Lot Area (sqft) vs Price')
plt.xlabel('Lot Area (sqft)')
plt.ylabel('Price')
# Упорядочиваем графики
plt.tight_layout()
plt.show()
Стандартизация данных для кластеризации¶
# Нормализация данных
scaler = StandardScaler()
data_scaled = scaler.fit_transform(df_cleaned)
# Преобразование в DataFrame для удобства
df_scaled = pd.DataFrame(data_scaled, columns=df_cleaned.columns)
# Понижение размерности до 2 компонент
pca = PCA(n_components=2)
kc_pca = pca.fit_transform(df_scaled)
# Визуализация
plt.figure(figsize=(8, 6))
plt.scatter(kc_pca[:, 0], kc_pca[:, 1], alpha=0.6)
plt.title("PCA Visualization of Housing Data")
plt.xlabel("Principal Component 1")
plt.ylabel("Principal Component 2")
plt.show()
Агломеративная (иерархическая) кластеризация¶
# Построение дендрограммы
linkage_matrix = linkage(data_scaled, method='ward')
plt.figure(figsize=(10, 7))
dendrogram(linkage_matrix)
plt.title('Дендрограмма агломеративной кластеризации')
plt.xlabel('Индекс образца')
plt.ylabel('Расстояние')
plt.show()
# Получение результатов кластеризации с заданным порогом
result = fcluster(linkage_matrix, t=60, criterion='distance')
print(result) # Вывод результатов кластеризации
# Выбираем подмножество данных для кластеризации
features = df[['price', 'sqft_living', 'floors', 'bathrooms']]
scaled_features = scaler.fit_transform(features)
# Построение дендрограммы
linkage_matrix = linkage(scaled_features, method='ward') # Метод "Ward"
plt.figure(figsize=(12, 8))
dendrogram(linkage_matrix, labels=df.index, leaf_rotation=90, leaf_font_size=10)
plt.title('Иерархическая кластеризация (дендрограмма)')
plt.xlabel('Индекс дома')
plt.ylabel('Евклидово расстояние')
plt.tight_layout()
plt.show()
Визуализация распределения кластеров
# Визуализация кластеров
plt.figure(figsize=(16, 12))
# Парный график 1: sqft_living vs price
plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned['sqft_living'], y=df_cleaned['price'], hue=result, palette='Set1', alpha=0.6)
plt.title('Living Area vs Price Clusters')
plt.xlabel('Living Area (sqft)')
plt.ylabel('Price')
# Парный график 2: bedrooms vs price
plt.subplot(2, 2, 2)
sns.scatterplot(x=df_cleaned['floors'], y=df_cleaned['price'], hue=result, palette='Set1', alpha=0.6)
plt.title('Floors vs Price Clusters')
plt.xlabel('Floors')
plt.ylabel('Price')
# Парный график 3: bathrooms vs sqft_living
plt.subplot(2, 2, 3)
sns.scatterplot(x=df_cleaned['bathrooms'], y=df_cleaned['price'], hue=result, palette='Set1', alpha=0.6)
plt.title('Bathrooms vs Price Clusters')
plt.xlabel('Bathrooms')
plt.ylabel('Price')
# Парный график 4: sqft_living vs bedrooms
plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned['sqft_living'], y=df_cleaned['price'], hue=result, palette='Set1', alpha=0.6)
plt.title('Living Area vs Price Clusters')
plt.xlabel('Living Area (sqft)')
plt.ylabel('Price')
# Настройка графиков
plt.tight_layout()
plt.show()
KMeans (неиерархическая кластеризация) для сравнения¶
# Убедитесь, что масштабирование применяется только к нужным признакам
features_used = ['sqft_living', 'price', 'floors', 'bathrooms', 'bedrooms']
data_to_scale = df_cleaned[features_used]
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data_to_scale)
random_state = 42
kmeans = KMeans(n_clusters=4, random_state=random_state)
labels = kmeans.fit_predict(data_scaled)
centers = kmeans.cluster_centers_
# Отображение центроидов
centers_original = scaler.inverse_transform(centers) # Обратная стандартизация
print("Центры кластеров:\n", centers_original)
# Визуализация результатов кластеризации KMeans
plt.figure(figsize=(16, 12))
plt.subplot(2, 2, 1)
sns.scatterplot(x=df_cleaned['sqft_living'], y=df_cleaned['price'], hue=labels, palette='Set1', alpha=0.6)
plt.scatter(centers[:, 0], centers[:, 1], s=300, c='red', label='Centroids')
plt.title('KMeans Clustering: Square vs Price')
plt.legend()
plt.subplot(2, 2, 2)
sns.scatterplot(x=df_cleaned['floors'], y=df_cleaned['price'], hue=labels, palette='Set1', alpha=0.6)
plt.scatter(centers[:, 2], centers[:, 3], s=300, c='red', label='Centroids')
plt.title('KMeans Clustering: Floors vs Price')
plt.legend()
plt.subplot(2, 2, 3)
sns.scatterplot(x=df_cleaned['bathrooms'], y=df_cleaned['price'], hue=labels, palette='Set1', alpha=0.6)
plt.scatter(centers[:, 1], centers[:, 4], s=300, c='red', label='Centroids')
plt.title('KMeans Clustering: Bathrooms vs Price')
plt.legend()
plt.subplot(2, 2, 4)
sns.scatterplot(x=df_cleaned['sqft_living'], y=df_cleaned['price'], hue=labels, palette='Set1', alpha=0.6)
plt.scatter(centers[:, 3], centers[:, 4], s=300, c='red', label='Centroids')
plt.title('KMeans Clustering: Square vs Price')
plt.legend()
plt.tight_layout()
plt.show()
PCA для визуализации сокращенной размерности¶
pca = PCA(n_components=2)
reduced_data = pca.fit_transform(data_scaled)
# Визуализация сокращенных данных
plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=result, palette='Set1', alpha=0.6)
plt.title('PCA reduced data: Agglomerative Clustering')
plt.subplot(1, 2, 2)
sns.scatterplot(x=reduced_data[:, 0], y=reduced_data[:, 1], hue=labels, palette='Set1', alpha=0.6)
plt.title('PCA reduced data: KMeans Clustering')
plt.tight_layout()
plt.show()
Анализ инерции для метода локтя (метод оценки суммы квадратов расстояний)¶
inertias = []
clusters_range = range(1, 11)
for i in clusters_range:
kmeans = KMeans(n_clusters=i, random_state=random_state)
kmeans.fit(data_scaled)
inertias.append(kmeans.inertia_)
plt.figure(figsize=(10, 6))
plt.plot(clusters_range, inertias, marker='o')
plt.title('Метод локтя для оптимального k')
plt.xlabel('Количество кластеров')
plt.ylabel('Инерция')
plt.grid(True)
plt.show()
Расчет коэффициентов силуэта¶
silhouette_scores = []
for i in clusters_range[1:]:
kmeans = KMeans(n_clusters=i, random_state=random_state)
labels = kmeans.fit_predict(data_scaled)
score = silhouette_score(data_scaled, labels)
silhouette_scores.append(score)
# Построение диаграммы значений силуэта
plt.figure(figsize=(10, 6))
plt.plot(clusters_range[1:], silhouette_scores, marker='o')
plt.title('Коэффициенты силуэта для разных k')
plt.xlabel('Количество кластеров')
plt.ylabel('Коэффициент силуэта')
plt.grid(True)
plt.show()
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.cluster import KMeans
# ========================
# Применение K-Means
# ========================
kmeans = KMeans(n_clusters=3, random_state=42)
df_clusters = kmeans.fit_predict(df_scaled)
# ========================
# Оценка качества кластеризации
# ========================
silhouette_avg = silhouette_score(df_scaled, df_clusters)
print(f'Средний коэффициент силуэта: {silhouette_avg:.3f}')
# ========================
# Визуализация кластеров
# ========================
from sklearn.decomposition import PCA
pca = PCA(n_components=2)
df_pca = pca.fit_transform(df_scaled)
plt.figure(figsize=(10, 7))
sns.scatterplot(x=df_pca[:, 0], y=df_pca[:, 1], hue=df_clusters, palette='viridis', alpha=0.7)
plt.title('Визуализация кластеров с помощью K-Means')
plt.xlabel('Первая компонентa PCA')
plt.ylabel('Вторая компонентa PCA')
plt.legend(title='Кластер', loc='upper right')
plt.show()
Средний коэффициент силуэта, равный 0.250, указывает на умеренно хорошую кластеризацию.
Средний коэффициент силуэта (silhouette score) указывает на качество кластеризации, измеряя, насколько хорошо точки внутри одного кластера близки друг к другу по сравнению с точками из других кластеров. Значения коэффициента силуэта находятся в диапазоне от -1 до 1:
1: Указывает на идеально плотные и четко разделенные кластеры.
0: Указывает на перекрытие кластеров или слабую структуру кластеризации.
Отрицательные значения: Указывают, что точки в кластере расположены ближе к другому кластеру, чем к своему.
from sklearn.cluster import AgglomerativeClustering
# ========================
# Агломеративная кластеризация
# ========================
agg_cluster = AgglomerativeClustering(n_clusters=3)
labels_agg = agg_cluster.fit_predict(df_scaled)
# ========================
# Оценка качества кластеризации
# ========================
silhouette_avg_agg = silhouette_score(df_scaled, labels_agg)
print(f'Средний коэффициент силуэта (агломеративная кластеризация): {silhouette_avg_agg:.3f}')
# ========================
# Визуализация кластеров
# ========================
pca = PCA(n_components=2)
df_pca = pca.fit_transform(df_scaled)
plt.figure(figsize=(10, 7))
sns.scatterplot(x=df_pca[:, 0], y=df_pca[:, 1], hue=labels_agg, palette='viridis', alpha=0.7)
plt.title('Визуализация кластеров с помощью агломеративной кластеризации')
plt.xlabel('Первая компонентa PCA')
plt.ylabel('Вторая компонентa PCA')
plt.legend(title='Кластер', loc='upper right')
plt.show()
Значение коэффициента силуэта лежит в диапазоне от -1 до 1. Ближе к 1: Хорошо сформированные, плотные кластеры, четко отделенные друг от друга.
Ближе к 0: Кластеры пересекаются или слабо разделены, не имеют четких границ. Точки расположены одинаково близко как к своему кластеру, так и к соседним.
Ближе к -1 (Отрицательные значения): Некоторые точки скорее относятся к другим кластерам, чем к текущему (ближе к центрам других кластеров). Очень плохая кластеризация.
Ближе к 1: Все точки внутри каждого кластера плотно сгруппированы и значительно удалены от точек других кластеров. Свидетельствует о четкой и хорошо разделенной структуре данных. Единица говорит об идеальной кластеризации.
Значение 0.225 указывает на то, что кластеры с нечеткой границей и неоптимальный выбор числа кластеров или особенности данных, затрудняющие их разделение.
Вроде усёё :)