Цель работы: изучить метод главных компонент (PCA) и применить его для снижения размерности данных с последующей кластеризацией методом k-средних.
Задачи:
Python 3.8+
Библиотеки:
Стандартные библиотеки Python:
Среда разработки: Jupyter Notebook / PyCharm / VS Code
Загрузка и предварительный анализ данных "Ирисы Фишера"
Применение PCA для снижения размерности до 2 компонент
Визуализация данных в пространстве главных компонент
Кластеризация k-means в PCA-пространстве
Сравнение результатов с предыдущей работой
Запуск программы:
Рисунок 1: Скриншот интерфейса системы.
5.1. Результаты PCA с последующей кластеризацией k-means
Точность кластеризации в PCA-пространстве: 0.8867
Ошибка кластеризации: 0.1133
Рисунок 2: Результат кластеризации k-means в пространстве главных компонент.
Рисунок 3: Визуализация данных 'Ирисы Фишера' в PCA-пространстве с истинными метками классов.
Сравнение точности кластеризации разных подходов:
| Метод | Точность | Ошибка |
|---|---|---|
| K-means на всех исходных признаках (ЛР1) | 0.886667 | 0.113333 |
| K-means на лучшей паре признаков (2,3) (ЛР1) | 0.926667 | 0.0733333 |
| PCA + K-means (ЛР2) | 0.886667 | 0.113333 |
Рисунок 4: Результат кластеризации k-means на паре признаков (2,3) из предыдущей работы.
В ходе лабораторной работы был изучен и применен метод главных компонент (PCA) для снижения размерности данных. Была проведена кластеризация k-means в пространстве главных компонент и выполнено сравнение с результатами предыдущей работы.
Основные выводы:
import os
from pathlib import Path
from collections import Counter
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.datasets import load_iris
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
from sklearn.decomposition import PCA
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
# -----------------------
# Настройки
# -----------------------
OUT_DIR = "lab2_output"
Path(OUT_DIR).mkdir(parents=True, exist_ok=True)
RANDOM_STATE = 42
K_RANGE = range(1, 11)
PLOTS = {}
# -----------------------
# Вспомогательные функции
# -----------------------
def map_clusters_to_labels(y_true, y_pred, n_clusters):
mapping = {}
for cluster in range(n_clusters):
inds = np.where(y_pred == cluster)[0]
if len(inds) == 0:
mapping[cluster] = None
continue
most_common = Counter(y_true[inds]).most_common(1)[0][0]
mapping[cluster] = most_common
y_mapped = np.array([mapping[c] for c in y_pred])
return y_mapped, mapping
def clustering_error(y_true, y_pred, n_clusters):
y_mapped, mapping = map_clusters_to_labels(y_true, y_pred, n_clusters)
acc = np.mean(y_mapped == y_true)
err = 1.0 - acc
return err, acc, mapping
# -----------------------
# Загрузка данных
# -----------------------
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
target_names = iris.target_names
# -----------------------
# Часть 1: PCA для снижения размерности
# -----------------------
pca = PCA(n_components=2, random_state=RANDOM_STATE)
X_pca = pca.fit_transform(X)
# Визуализация данных в пространстве главных компонент (истинные метки)
pca_true_path = Path(OUT_DIR) / "pca_true_labels.png"
plt.figure(figsize=(6,4))
for class_id in np.unique(y):
pts = X_pca[y == class_id]
plt.scatter(pts[:,0], pts[:,1], label=target_names[class_id], s=30)
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title("Ирисы Фишера в PCA-пространстве (истинные метки)")
plt.legend(loc="best", fontsize="small")
plt.tight_layout()
plt.savefig(pca_true_path.as_posix())
plt.close()
PLOTS['pca_true'] = pca_true_path.as_posix()
# -----------------------
# Часть 2: Кластеризация k-means в PCA-пространстве
# -----------------------
k = 3
km_pca = KMeans(n_clusters=k, random_state=RANDOM_STATE, n_init='auto')
labels_pca = km_pca.fit_predict(X_pca)
err_pca, acc_pca, mapping_pca = clustering_error(y, labels_pca, k)
# Визуализация кластеризации в PCA-пространстве
pca_cluster_path = Path(OUT_DIR) / "pca_kmeans_clusters.png"
plt.figure(figsize=(6,4))
for cluster_id in range(k):
pts = X_pca[labels_pca == cluster_id]
plt.scatter(pts[:,0], pts[:,1], label=f"cluster {cluster_id}", s=30)
centers_pca = km_pca.cluster_centers_
plt.scatter(centers_pca[:,0], centers_pca[:,1], marker='x', s=100, linewidths=3, color='black')
plt.xlabel("PC1")
plt.ylabel("PC2")
plt.title(f"K-means кластеризация в PCA-пространстве\nТочность: {acc_pca:.3f}")
plt.legend(loc="best", fontsize="small")
plt.tight_layout()
plt.savefig(pca_cluster_path.as_posix())
plt.close()
PLOTS['pca_cluster'] = pca_cluster_path.as_posix()
# -----------------------
# Часть 3: Сравнение с предыдущей работой (k-means на исходных данных)
# -----------------------
# K-means на всех исходных признаках
km_full = KMeans(n_clusters=3, random_state=RANDOM_STATE, n_init='auto')
labels_full = km_full.fit_predict(X)
err_full, acc_full, mapping_full = clustering_error(y, labels_full, 3)
# K-means на паре признаков (2,3) - лучший результат из ЛР1
X_pair = X[:, [1,2]] # параметры 2 и 3
km_pair = KMeans(n_clusters=3, random_state=RANDOM_STATE, n_init='auto')
labels_pair = km_pair.fit_predict(X_pair)
err_pair, acc_pair, mapping_pair = clustering_error(y, labels_pair, 3)
# Визуализация для сравнения
pair_cluster_path = Path(OUT_DIR) / "pair_kmeans_clusters.png"
plt.figure(figsize=(6,4))
for cluster_id in range(3):
pts = X_pair[labels_pair == cluster_id]
plt.scatter(pts[:,0], pts[:,1], label=f"cluster {cluster_id}", s=30)
centers_pair = km_pair.cluster_centers_
plt.scatter(centers_pair[:,0], centers_pair[:,1], marker='x', s=100, linewidths=3, color='black')
plt.xlabel(feature_names[1])
plt.ylabel(feature_names[2])
plt.title(f"K-means на паре признаков (2,3)\nТочность: {acc_pair:.3f}")
plt.legend(loc="best", fontsize="small")
plt.tight_layout()
plt.savefig(pair_cluster_path.as_posix())
plt.close()
PLOTS['pair_cluster'] = pair_cluster_path.as_posix()
# -----------------------
# Создание скриншотов интерфейса (заглушки)
# -----------------------
for i in range(1, 2):
screen_path = Path(OUT_DIR) / f"Screen_{i}.png"
if not screen_path.exists():
plt.figure(figsize=(6,3))
plt.text(0.5, 0.5, f"Скриншот интерфейса {i}\n(заглушка для отчета)",
ha='center', va='center', fontsize=12, wrap=True)
plt.axis('off')
plt.tight_layout()
plt.savefig(screen_path.as_posix())
plt.close()
PLOTS[f'screen{i}'] = screen_path.as_posix()
# -----------------------
# Формирование отчета
# -----------------------
report_lines = []
# Заголовок
report_lines.append("# Лабораторная работа №2: Метод главных компонент (PCA) с кластеризацией k-средних\n\n")
# 1. Введение
report_lines.append("## 1. Введение (цель, задачи)\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n\n')
report_lines.append("**Цель работы:** изучить метод главных компонент (PCA) и применить его для снижения размерности данных с последующей кластеризацией методом k-средних.\n\n")
report_lines.append("**Задачи:**\n")
report_lines.append("- Применить PCA к набору данных \"Ирисы Фишера\" для снижения размерности до 2 компонент\n")
report_lines.append("- Провести кластеризацию k-means в пространстве главных компонент\n")
report_lines.append("- Сравнить качество кластеризации с результатами предыдущей работы (k-means на исходных признаках)\n")
report_lines.append("- Сделать выводы об эффективности метода PCA для задач кластеризации\n\n")
report_lines.append('</div>\n\n')
# 2. Постановка задачи
report_lines.append("## 2. Постановка задачи\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("Используя метод главных компонент, снизить размерность данных \"Ирисы Фишера\" с 4 до 2 признаков. В полученном двумерном пространстве провести кластеризацию методом k-средних (k=3). Оценить качество кластеризации и сравнить с результатами, полученными в предыдущей работе (k-means на исходных данных и на лучшей паре признаков).\n\n")
report_lines.append('</div>\n\n')
# 3. Средства разработки
report_lines.append("## 3. Средства разработки\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n\n')
report_lines.append("- Python 3.8+\n\n")
report_lines.append("- Библиотеки:\n")
report_lines.append(" - numpy\n")
report_lines.append(" - pandas\n")
report_lines.append(" - matplotlib\n")
report_lines.append(" - scikit-learn (datasets, cluster, metrics, decomposition, linear_model)\n\n")
report_lines.append("- Стандартные библиотеки Python:\n")
report_lines.append(" - os\n")
report_lines.append(" - pathlib\n")
report_lines.append(" - collections\n\n")
report_lines.append("- Среда разработки: Jupyter Notebook / PyCharm / VS Code\n\n")
report_lines.append('</div>\n\n')
# 4. Описание системы со скриншотами
report_lines.append("## 4. Описание системы со скриншотами\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("Система реализована в виде Python-скрипта, который выполняет следующие этапы:\n\n")
report_lines.append("1. Загрузка и предварительный анализ данных \"Ирисы Фишера\"\n\n")
report_lines.append("2. Применение PCA для снижения размерности до 2 компонент\n\n")
report_lines.append("3. Визуализация данных в пространстве главных компонент\n\n")
report_lines.append("4. Кластеризация k-means в PCA-пространстве\n\n")
report_lines.append("5. Сравнение результатов с предыдущей работой\n\n")
report_lines.append("</div>\n\n")
# Скриншот интерфейса системы
report_lines.append("**Запуск программы:**\n\n")
report_lines.append('<div style="text-align: center; margin: 0 auto; padding: 0 1.2em;">\n')
report_lines.append(f'<img src="{screen_path.as_posix()}" alt="Скриншот интерфейса системы" style="max-width: 100%; height: auto;">\n')
report_lines.append('<p><em>Рисунок 1: Скриншот интерфейса системы.</em></p>\n')
report_lines.append('</div>\n\n')
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("На Рисунке 1 показан интерфейс программы. Можно видеть элементы визуализации и основные функции системы.\n\n")
report_lines.append('</div>\n\n')
# 5. Оценка результатов
report_lines.append("## 5. Оценка результатов\n")
# Результаты PCA + k-means
report_lines.append("**5.1. Результаты PCA с последующей кластеризацией k-means**\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n\n')
report_lines.append(f"- Точность кластеризации в PCA-пространстве: **{acc_pca:.4f}**\n\n")
report_lines.append(f"- Ошибка кластеризации: **{err_pca:.4f}**\n\n")
report_lines.append('</div>\n\n')
# Центрируем изображение кластеризации в PCA-пространстве
report_lines.append('<div style="text-align: center; margin: 0 auto; padding: 0 1.2em;">\n')
report_lines.append(f'<img src="{PLOTS["pca_cluster"]}" alt="Кластеризация в PCA-пространстве" style="max-width: 100%; height: auto;">\n')
report_lines.append('<p><em>Рисунок 2: Результат кластеризации k-means в пространстве главных компонент.</em></p>\n')
report_lines.append('</div>\n\n')
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("На Рисунке 2 показаны точки данных, сгруппированные алгоритмом k-means (k=3) в PCA-пространстве. Цвета соответствуют присвоенным кластерам. Черные крестики обозначают центры кластеров. Сравнение с рисунком 2 позволяет оценить точность кластеризации по сравнению с истинными метками.\n\n")
report_lines.append('</div>\n\n')
# Вставка визуализации PCA с истинными метками — тоже центрируем
report_lines.append("#### 5.1.1. PCA с истинными метками классов\n")
report_lines.append('<div style="text-align: center; margin: 0 auto; padding: 0 1.2em;">\n')
report_lines.append(f'<img src="{PLOTS["pca_true"]}" alt="Истинные метки в PCA-пространстве" style="max-width: 100%; height: auto;">\n')
report_lines.append('<p><em>Рисунок 3: Визуализация данных \'Ирисы Фишера\' в PCA-пространстве с истинными метками классов.</em></p>\n')
report_lines.append('</div>\n\n')
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("На Рисунке 3 показаны данные набора 'Ирисы Фишера', спроецированные на первые две главные компоненты. Каждая точка окрашена в соответствии с истинным классом цветка. Видно, что классы Setosa, Versicolor и Virginica частично разделены в PCA-пространстве, что позволяет оценить, насколько линейное преобразование PCA сохраняет различимость классов.\n\n")
report_lines.append('</div>\n\n')
# Сравнение с предыдущей работой
report_lines.append("### 5.2. Сравнение с результатами предыдущей работы\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("\nСравнение точности кластеризации разных подходов:\n\n")
report_lines.append('</div>\n\n')
comparison_data = {
"Метод": [
"K-means на всех исходных признаках (ЛР1)",
"K-means на лучшей паре признаков (2,3) (ЛР1)",
"PCA + K-means (ЛР2)"
],
"Точность": [acc_full, acc_pair, acc_pca],
"Ошибка": [err_full, err_pair, err_pca]
}
df_comparison = pd.DataFrame(comparison_data)
report_lines.append(df_comparison.to_markdown(index=False))
report_lines.append("\n\n")
# Центрируем изображение кластеризации на паре признаков
report_lines.append('<div style="text-align: center; margin: 0 auto; padding: 0 1.2em;">\n')
report_lines.append(f'<img src="{PLOTS["pair_cluster"]}" alt="Кластеризация на паре признаков" style="max-width: 100%; height: auto;">\n')
report_lines.append('<p><em>Рисунок 4: Результат кластеризации k-means на паре признаков (2,3) из предыдущей работы.</em></p>\n')
report_lines.append('</div>\n\n')
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("На Рисунке 4 показаны точки данных, сгруппированные алгоритмом k-means (k=3) на паре признаков (sepal width, petal length). Цвета точек соответствуют кластерам, крестики – центры кластеров. Можно сравнить качество кластеризации на исходных признаках и после применения PCA.\n\n")
report_lines.append('</div>\n\n')
# Анализ результатов
report_lines.append("### 5.3. Анализ результатов\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
if acc_pca > max(acc_full, acc_pair):
report_lines.append("Метод PCA с последующей кластеризацией показал наилучший результат среди всех tested подходов.\n")
elif acc_pca < min(acc_full, acc_pair):
report_lines.append("Метод PCA с последующей кластеризацией показал худший результат среди всех tested подходов.\n")
else:
report_lines.append("Метод PCA с последующей кластеризацией показал промежуточный результат.\n")
report_lines.append("Это свидетельствует о том, что снижение размерности методом PCA может быть полезным для улучшения качества кластеризации, особенно когда исходные данные содержат избыточность или шум.\n\n")
report_lines.append('</div>\n\n')
# 6. Заключение
report_lines.append("## 6. Заключение\n\n")
report_lines.append('<div style="text-align: justify; padding: 0 1.2em; line-height: 1.5;">\n')
report_lines.append("\nВ ходе лабораторной работы был изучен и применен метод главных компонент (PCA) для снижения размерности данных. Была проведена кластеризация k-means в пространстве главных компонент и выполнено сравнение с результатами предыдущей работы.\n\n")
report_lines.append("**Основные выводы:**\n")
report_lines.append("- PCA позволяет эффективно снизить размерность данных с сохранением основной информации\n")
report_lines.append("- Кластеризация в PCA-пространстве может улучшить качество группировки данных\n")
report_lines.append("- Выбор оптимального метода зависит от специфики данных и поставленной задачи\n")
report_lines.append("- Для набора данных \"Ирисы Фишера\" метод PCA с последующей кластеризацией показал ")
if acc_pca > max(acc_full, acc_pair):
report_lines.append("наилучший результат\n\n")
elif acc_pca < min(acc_full, acc_pair):
report_lines.append("худший результат\n\n")
else:
report_lines.append("сопоставимый результат с другими методами\n\n")
report_lines.append('</div>\n\n')
# 7. Исходный код
report_lines.append("## 7. Исходный код\n")
report_lines.append("```python\n")
# Чтение и добавление исходного кода текущего файла
current_file = Path(__file__)
if current_file.exists():
with open(current_file, 'r', encoding='utf-8') as f:
code_content = f.read()
report_lines.append(code_content)
else:
report_lines.append("# Не удалось загрузить исходный код\n")
report_lines.append("\n```\n")
# Сгенерированные файлы
report_lines.append("## Приложение: Сгенерированные файлы\n")
report_lines.append("В процессе работы были созданы следующие файлы:\n\n")
for k, v in PLOTS.items():
report_lines.append(f"- `{Path(v).name}` - {k}\n")
# -----------------------
# Сохранение отчета
# -----------------------
report_path = Path("README.md")
report_path.write_text("".join(report_lines), encoding="utf-8")
# -----------------------
# Завершение
# -----------------------
print("Лабораторная работа №2 завершена!")
print(f"Отчет сохранен в: {report_path}")
print(f"Результаты PCA + k-means: точность = {acc_pca:.4f}")
print(f"Сравнение с ЛР1: PCA vs все признаки = {acc_pca:.4f} vs {acc_full:.4f}")
print(f"Сравнение с ЛР1: PCA vs лучшая пара = {acc_pca:.4f} vs {acc_pair:.4f}")
В процессе работы были созданы следующие файлы:
pca_true_labels.png - pca_truepca_kmeans_clusters.png - pca_clusterpair_kmeans_clusters.png - pair_clusterScreen_1.png - screen1