Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Lab 2, 3352, Гареева Карина Радиковна

.docx
Скачиваний:
0
Добавлен:
24.01.2026
Размер:
1.08 Mб
Скачать

Задание 2: Кластеризация методом K-Means

In [12]:

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsfrom sklearn.cluster import KMeansfrom sklearn.preprocessing import StandardScalerfrom sklearn.metrics import silhouette_scoreimport warningswarnings.filterwarnings('ignore')

plt.rcParams['figure.figsize'] = (10, 6)sns.set_style('whitegrid')

df = pd.read_csv('Concrete Compressive Strength.csv')print(f"\nРазмер датасета: {df.shape}")

Размер датасета: (1030, 9)

In [17]:

new_column_names = [

'Cement', 'Slag', 'Ash', 'Water',

'Plasticizer', 'Coarse_Agg', 'Fine_Agg',

'Age', 'Comp_Strength']df.columns = new_column_namesdisplay(df.head())

Cement

Slag

Ash

Water

Plasticizer

Coarse_Agg

Fine_Agg

Age

Comp_Strength

0

540.0

0.0

0.0

162.0

2.5

1040.0

676.0

28

79.986111

1

540.0

0.0

0.0

162.0

2.5

1055.0

676.0

28

61.887366

2

332.5

142.5

0.0

228.0

0.0

932.0

594.0

270

40.269535

3

332.5

142.5

0.0

228.0

0.0

932.0

594.0

365

41.052780

4

198.6

132.4

0.0

192.0

0.0

978.4

825.5

360

44.296075

In [20]:

df_kmeans = df.copy()df_kmeans['Cement/Water'] = df_kmeans['Cement'] / df_kmeans['Water']

print("Добавлен новый атрибут 'Cement/Water'")print("Первые 5 строк обновленного датасета:")display(df_kmeans.head())

Добавлен новый атрибут 'Cement/Water'

Первые 5 строк обновленного датасета:

Cement

Slag

Ash

Water

Plasticizer

Coarse_Agg

Fine_Agg

Age

Comp_Strength

Cement/Water

0

540.0

0.0

0.0

162.0

2.5

1040.0

676.0

28

79.986111

3.333333

1

540.0

0.0

0.0

162.0

2.5

1055.0

676.0

28

61.887366

3.333333

2

332.5

142.5

0.0

228.0

0.0

932.0

594.0

270

40.269535

1.458333

3

332.5

142.5

0.0

228.0

0.0

932.0

594.0

365

41.052780

1.458333

4

198.6

132.4

0.0

192.0

0.0

978.4

825.5

360

44.296075

1.034375

In [22]:

print("Пропущенные значения:")print(df_kmeans.isnull().sum())

print(f"\nБесконечные значения в Cement_Water_Ratio: {np.isinf(df_kmeans['Cement/Water']).sum()}")

# Удалим бесконечные значенияdf_kmeans = df_kmeans[~np.isinf(df_kmeans['Cement/Water'])]print(f"Размер датасета после очистки: {df_kmeans.shape}")

Пропущенные значения:

Cement 0

Slag 0

Ash 0

Water 0

Plasticizer 0

Coarse_Agg 0

Fine_Agg 0

Age 0

Comp_Strength 0

Cement/Water 0

dtype: int64

Бесконечные значения в Cement_Water_Ratio: 0

Размер датасета после очистки: (1030, 10)

In [24]:

def remove_outliers(df, columns):

df_clean = df.copy()

for col in columns:

Q1 = df_clean[col].quantile(0.25)

Q3 = df_clean[col].quantile(0.75)

IQR = Q3 - Q1

lower_bound = Q1 - 1.5 * IQR

upper_bound = Q3 + 1.5 * IQR

df_clean = df_clean[(df_clean[col] >= lower_bound) & (df_clean[col] <= upper_bound)]

return df_clean

columns_to_clean = ['Cement', 'Water', 'Cement/Water', 'Comp_Strength']df_clean = remove_outliers(df_kmeans, columns_to_clean)

print(f"Размер датасета после удаления выбросов: {df_clean.shape}")print(f"Удалено записей: {len(df_kmeans) - len(df_clean)}")print(f"Дубликатов в данных: {df_clean.duplicated().sum()}")

Размер датасета после удаления выбросов: (981, 10)

Удалено записей: 49

Дубликатов в данных: 19

In [26]:

# Построим scatter plot для поиска кластеровfig, axes = plt.subplots(2, 2, figsize=(15, 12))

# График 1: Цемент vs Прочностьaxes[0,0].scatter(df_clean['Cement'], df_clean['Comp_Strength'], alpha=0.6, c='blue')axes[0,0].set_xlabel('Цемент (кг/м³)')axes[0,0].set_ylabel('Прочность (МПа)')axes[0,0].set_title('Цемент vs Прочность')axes[0,0].grid(True)

# График 2: Отношение цемент/вода vs Прочностьaxes[0,1].scatter(df_clean['Cement/Water'], df_clean['Comp_Strength'], alpha=0.6, c='green')axes[0,1].set_xlabel('Отношение Цемент/Вода')axes[0,1].set_ylabel('Прочность (МПа)')axes[0,1].set_title('Цемент/Вода vs Прочность')axes[0,1].grid(True)

# График 3: Возраст vs Прочностьaxes[1,0].scatter(df_clean['Age'], df_clean['Comp_Strength'], alpha=0.6, c='red')axes[1,0].set_xlabel('Возраст (дни)')axes[1,0].set_ylabel('Прочность (МПа)')axes[1,0].set_title('Возраст vs Прочность')axes[1,0].grid(True)

# График 4: Вода vs Прочностьaxes[1,1].scatter(df_clean['Water'], df_clean['Comp_Strength'], alpha=0.6, c='purple')axes[1,1].set_xlabel('Вода (кг/м³)')axes[1,1].set_ylabel('Прочность (МПа)')axes[1,1].set_title('Вода vs Прочность')axes[1,1].grid(True)

plt.tight_layout()plt.show()

In [27]:

# Анализ видимых кластеровprint("АНАЛИЗ ВИЗУАЛЬНЫХ КЛАСТЕРОВ:")print("-" * 40)print("• На графиках видны естественные группы бетонов:")print(" - Низкопрочные смеси (мало цемента, много воды)")print(" - Среднепрочные смеси (средние показатели)")print(" - Высокопрочные смеси (много цемента, оптимальное водоцементное отношение)")print("• Отношение цемент/вода хорошо разделяет смеси по эффективности")

АНАЛИЗ ВИЗУАЛЬНЫХ КЛАСТЕРОВ:

----------------------------------------

• На графиках видны естественные группы бетонов:

- Низкопрочные смеси (мало цемента, много воды)

- Среднепрочные смеси (средние показатели)

- Высокопрочные смеси (много цемента, оптимальное водоцементное отношение)

• Отношение цемент/вода хорошо разделяет смеси по эффективности

In [29]:

# Выберем признаки для кластеризацииfeatures = ['Cement', 'Water', 'Age', 'Cement/Water', 'Comp_Strength']X = df_clean[features]

# Стандартизация данныхscaler = StandardScaler()X_scaled = scaler.fit_transform(X)

print("Признаки для кластеризации:")print(features)print(f"Размерность данных: {X_scaled.shape}")

Признаки для кластеризации:

['Cement', 'Water', 'Age', 'Cement/Water', 'Comp_Strength']

Размерность данных: (981, 5)

In [32]:

# Метод локтя для определения оптимального числа кластеровinertia = []silhouette_scores = []k_range = range(2, 8)

for k in k_range:

kmeans = KMeans(n_clusters=k, random_state=42, n_init=10)

kmeans.fit(X_scaled)

inertia.append(kmeans.inertia_)

silhouette_scores.append(silhouette_score(X_scaled, kmeans.labels_))

# Визуализация метода локтяfig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))

ax1.plot(k_range, inertia, 'bo-')ax1.set_xlabel('Количество кластеров')ax1.set_ylabel('Inertia')ax1.set_title('Метод локтя')ax1.grid(True)

ax2.plot(k_range, silhouette_scores, 'ro-')ax2.set_xlabel('Количество кластеров')ax2.set_ylabel('Silhouette Score')ax2.set_title('Silhouette Score')ax2.grid(True)

plt.tight_layout()plt.show()

In [33]:

# Анализируем результаты для выбора оптимального kprint("АНАЛИЗ ОПТИМАЛЬНОГО ЧИСЛА КЛАСТЕРОВ:")print("-" * 40)for k, score in zip(k_range, silhouette_scores):

print(f"k = {k}: Silhouette Score = {score:.3f}")

# Выбираем оптимальное количество кластеров (обычно там, где "локоть" и высокий silhouette)optimal_k = 3print(f"\nВыбрано количество кластеров: {optimal_k}")

АНАЛИЗ ОПТИМАЛЬНОГО ЧИСЛА КЛАСТЕРОВ:

----------------------------------------

k = 2: Silhouette Score = 0.324

k = 3: Silhouette Score = 0.362

k = 4: Silhouette Score = 0.286

k = 5: Silhouette Score = 0.293

k = 6: Silhouette Score = 0.255

k = 7: Silhouette Score = 0.274

Выбрано количество кластеров: 3

In [34]:

# Применяем K-Means с выбранным количеством кластеровkmeans = KMeans(n_clusters=optimal_k, random_state=42, n_init=10)clusters = kmeans.fit_predict(X_scaled)

# Добавляем метки кластеров в датасетdf_clean['Cluster'] = clusters

print(f"Кластеризация завершена. Создано {optimal_k} кластера.")print("Распределение по кластерам:")cluster_counts = df_clean['Cluster'].value_counts().sort_index()for cluster, count in cluster_counts.items():

print(f"Кластер {cluster}: {count} записей ({count/len(df_clean)*100:.1f}%)")

Кластеризация завершена. Создано 3 кластера.

Распределение по кластерам:

Кластер 0: 585 записей (59.6%)

Кластер 1: 331 записей (33.7%)

Кластер 2: 65 записей (6.6%)

In [35]:

# Визуализация результатов кластеризацииfig, axes = plt.subplots(2, 2, figsize=(15, 12))

# Цемент vs Прочность с кластерамиscatter1 = axes[0,0].scatter(df_clean['Cement'], df_clean['Comp_Strength'],

c=df_clean['Cluster'], cmap='viridis', alpha=0.7)axes[0,0].set_xlabel('Цемент (кг/м³)')axes[0,0].set_ylabel('Прочность (МПа)')axes[0,0].set_title('Кластеризация: Цемент vs Прочность')plt.colorbar(scatter1, ax=axes[0,0])

# Цемент/Вода vs Прочность с кластерамиscatter2 = axes[0,1].scatter(df_clean['Cement/Water'], df_clean['Comp_Strength'],

c=df_clean['Cluster'], cmap='plasma', alpha=0.7)axes[0,1].set_xlabel('Отношение Цемент/Вода')axes[0,1].set_ylabel('Прочность (МПа)')axes[0,1].set_title('Кластеризация: Цемент/Вода vs Прочность')plt.colorbar(scatter2, ax=axes[0,1])

# Возраст vs Прочность с кластерамиscatter3 = axes[1,0].scatter(df_clean['Age'], df_clean['Comp_Strength'],

c=df_clean['Cluster'], cmap='cool', alpha=0.7)axes[1,0].set_xlabel('Возраст (дни)')axes[1,0].set_ylabel('Прочность (МПа)')axes[1,0].set_title('Кластеризация: Возраст vs Прочность')plt.colorbar(scatter3, ax=axes[1,0])

# Вода vs Прочность с кластерамиscatter4 = axes[1,1].scatter(df_clean['Water'], df_clean['Comp_Strength'],

c=df_clean['Cluster'], cmap='spring', alpha=0.7)axes[1,1].set_xlabel('Вода (кг/м³)')axes[1,1].set_ylabel('Прочность (МПа)')axes[1,1].set_title('Кластеризация: Вода vs Прочность')plt.colorbar(scatter4, ax=axes[1,1])

plt.tight_layout()plt.show()

In [36]:

# Анализ характеристик кластеровprint("ХАРАКТЕРИСТИКИ КЛАСТЕРОВ:")print("=" * 50)

cluster_stats = df_clean.groupby('Cluster')[features].mean()display(cluster_stats.round(2))

ХАРАКТЕРИСТИКИ КЛАСТЕРОВ:

==================================================

Cement

Water

Age

Cement/Water

Comp_Strength

Cluster

0

216.49

185.49

30.17

1.17

26.70

1

366.84

171.46

34.30

2.15

47.04

2

327.20

214.47

240.77

1.53

43.12

In [37]:

# Визуализация средних значений по кластерамcluster_means = df_clean.groupby('Cluster')[features].mean()

plt.figure(figsize=(12, 6))cluster_means.T.plot(kind='bar', figsize=(12, 6))plt.title('Средние значения признаков по кластерам')plt.xlabel('Признаки')plt.ylabel('Среднее значение')plt.xticks(rotation=45)plt.legend(title='Кластер')plt.grid(True, alpha=0.3)plt.tight_layout()plt.show()

<Figure size 1200x600 with 0 Axes>

In [38]:

# Дополнительный анализ: центры кластеров в оригинальном масштабеprint("ЦЕНТРЫ КЛАСТЕРОВ (оригинальные значения):")print("=" * 50)

# Преобразуем центры обратно в оригинальный масштабcluster_centers_original = scaler.inverse_transform(kmeans.cluster_centers_)centers_df = pd.DataFrame(cluster_centers_original, columns=features)centers_df['Cluster'] = range(optimal_k)

display(centers_df.round(2))

ЦЕНТРЫ КЛАСТЕРОВ (оригинальные значения):

==================================================

Cement

Water

Age

Cement/Water

Comp_Strength

Cluster

0

216.49

185.49

30.17

1.17

26.70

0

1

366.84

171.46

34.30

2.15

47.04

1

2

327.20

214.47

240.77

1.53

43.12

2

In [40]:

print("ВЫВОДЫ ПО КЛАСТЕРИЗАЦИИ K-MEANS")print("=" * 50)

# Анализируем характеристики каждого кластераcluster_0_stats = cluster_stats.loc[0]cluster_1_stats = cluster_stats.loc[1] cluster_2_stats = cluster_stats.loc[2]

conclusions = [

f"✓ Успешно создано {optimal_k} кластера бетонных смесей",

f"✓ Качество кластеризации (Silhouette Score): {silhouette_score(X_scaled, clusters):.3f}",

"",

"ХАРАКТЕРИСТИКИ КЛАСТЕРОВ:",

f"• Кластер 0 ({cluster_counts[0]} зап.): Низкопрочные смеси",

f" - Прочность: {cluster_0_stats['Comp_Strength']:.1f} МПа",

f" - Мало цемента: {cluster_0_stats['Cement']:.0f} кг/м³",

f" - Высокое водоцементное отношение",

"",

f"• Кластер 1 ({cluster_counts[1]} зап.): Высокопрочные смеси",

f" - Прочность: {cluster_1_stats['Comp_Strength']:.1f} МПа",

f" - Много цемента: {cluster_1_stats['Cement']:.0f} кг/м³",

f" - Оптимальное водоцементное отношение",

"",

f"• Кластер 2 ({cluster_counts[2]} зап.): Среднепрочные смеси",

f" - Прочность: {cluster_2_stats['Comp_Strength']:.1f} МПа",

f" - Средние показатели по всем параметрам",

"",

"ПРАКТИЧЕСКОЕ ЗНАЧЕНИЕ:",

"• Кластеризация выявила технологические группы бетонов",

"• Можно оптимизировать составы для каждой группы",

"• Метод K-means хорошо подошел для данного датасета",

"• Отношение цемент/вода оказалось важным признаком"]

for conclusion in conclusions:

print(conclusion)

ВЫВОДЫ ПО КЛАСТЕРИЗАЦИИ K-MEANS

==================================================

✓ Успешно создано 3 кластера бетонных смесей

✓ Качество кластеризации (Silhouette Score): 0.362

ХАРАКТЕРИСТИКИ КЛАСТЕРОВ:

• Кластер 0 (585 зап.): Низкопрочные смеси

- Прочность: 26.7 МПа

- Мало цемента: 216 кг/м³

- Высокое водоцементное отношение

• Кластер 1 (331 зап.): Высокопрочные смеси

- Прочность: 47.0 МПа

- Много цемента: 367 кг/м³

- Оптимальное водоцементное отношение

• Кластер 2 (65 зап.): Среднепрочные смеси

- Прочность: 43.1 МПа

- Средние показатели по всем параметрам

ПРАКТИЧЕСКОЕ ЗНАЧЕНИЕ:

• Кластеризация выявила технологические группы бетонов

• Можно оптимизировать составы для каждой группы

• Метод K-means хорошо подошел для данного датасета

• Отношение цемент/вода оказалось важным признаком

In [ ]: