K-Means 算法 Python 实现:3 种可视化方案对比与 1 个完整动画代码
K-Means 算法 Python 实现:3 种可视化方案对比与 1 个完整动画代码
在数据科学领域,K-Means 算法因其简洁高效而广受欢迎。但真正理解其迭代过程,往往需要直观的可视化支持。本文将带你用 Python 实现三种不同的可视化方案,并提供一个完整的动画演示代码,让你不仅掌握算法原理,还能生动展示聚类过程。
1. 环境准备与数据加载
首先确保你的 Python 环境已安装必要的库:
pip install numpy matplotlib scikit-learn我们使用经典的test.txt数据集进行演示。这个二维数据集包含 80 个样本点,非常适合可视化展示:
import numpy as np from matplotlib import pyplot as plt from sklearn.datasets import make_blobs # 加载数据集 def load_data(file_path): data = [] with open(file_path) as f: for line in f.readlines(): point = list(map(float, line.strip().split())) data.append(point) return np.array(data) # 生成示例数据(备选) def generate_sample_data(): X, _ = make_blobs(n_samples=300, centers=4, cluster_std=0.6) return X提示:实际项目中,建议先用
sklearn.datasets.make_blobs生成可控的测试数据,验证算法正确性后再应用于真实数据。
2. 三种可视化方案实现
2.1 静态分步图解法
这种方案适合教学演示,能清晰展示算法每个关键步骤:
def plot_steps(data, centers, assignments, step): plt.figure(figsize=(10, 6)) colors = ['r', 'g', 'b', 'c', 'm', 'y', 'k'] # 绘制数据点 for i in range(len(centers)): cluster_data = data[assignments == i] plt.scatter(cluster_data[:,0], cluster_data[:,1], c=colors[i], s=50, alpha=0.6) # 绘制中心点 plt.scatter(centers[:,0], centers[:,1], marker='x', s=200, c='k', linewidths=3) plt.title(f'K-Means 第 {step} 步') plt.grid(True) plt.show()典型输出效果:
- 初始随机中心点
- 第一次聚类结果
- 中心点更新后状态
- 最终收敛状态
2.2 动态迭代动画
使用 Matplotlib 的FuncAnimation实现流畅的迭代过程演示:
from matplotlib.animation import FuncAnimation class KMeansAnimator: def __init__(self, data, k=4): self.data = data self.k = k self.fig, self.ax = plt.subplots(figsize=(10, 6)) self.colors = plt.cm.rainbow(np.linspace(0, 1, k)) # 初始化中心点 self.centers = data[np.random.choice(len(data), k, replace=False)] self.assignments = np.zeros(len(data)) def update(self, frame): self.ax.clear() # 分配点到最近中心 distances = np.sqrt(((self.data - self.centers[:, np.newaxis])**2).sum(axis=2)) self.assignments = np.argmin(distances, axis=0) # 绘制聚类结果 for i in range(self.k): cluster_data = self.data[self.assignments == i] self.ax.scatter(cluster_data[:,0], cluster_data[:,1], color=self.colors[i], s=50, alpha=0.6) self.ax.scatter(self.centers[i,0], self.centers[i,1], marker='x', s=200, c='k', linewidths=3) # 更新中心点位置 new_centers = np.array([self.data[self.assignments == i].mean(axis=0) for i in range(self.k)]) # 绘制中心点移动轨迹 for i in range(self.k): self.ax.plot([self.centers[i,0], new_centers[i,0]], [self.centers[i,1], new_centers[i,1]], 'k--', alpha=0.3) self.centers = new_centers self.ax.set_title(f'K-Means 迭代 {frame+1}') return self.ax # 使用示例 data = load_data('test.txt') animator = KMeansAnimator(data) animation = FuncAnimation(animator.fig, animator.update, frames=10, interval=1000) plt.show()2.3 中心点轨迹图
这种可视化专注于展示中心点的移动路径,揭示算法收敛过程:
def plot_centroid_path(data, k=4, max_iter=10): centers_history = [] centers = data[np.random.choice(len(data), k, replace=False)] centers_history.append(centers.copy()) for _ in range(max_iter): # 分配点到最近中心 distances = np.sqrt(((data - centers[:, np.newaxis])**2).sum(axis=2)) assignments = np.argmin(distances, axis=0) # 更新中心点 new_centers = np.array([data[assignments == i].mean(axis=0) for i in range(k)]) centers_history.append(new_centers.copy()) centers = new_centers # 绘制轨迹 plt.figure(figsize=(10, 6)) colors = plt.cm.rainbow(np.linspace(0, 1, k)) for i in range(k): path = np.array([step[i] for step in centers_history]) plt.plot(path[:,0], path[:,1], 'o-', c=colors[i], markersize=8, linewidth=2, label=f'中心点 {i+1}') plt.scatter(data[:,0], data[:,1], c='gray', s=30, alpha=0.3) plt.title('K-Means 中心点移动轨迹') plt.legend() plt.grid(True) plt.show()3. 完整动画代码实现
下面是一个可直接运行的完整 K-Means 动画实现,包含交互式控件:
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation from matplotlib.widgets import Slider, Button class InteractiveKMeans: def __init__(self, data, k=4): self.data = data self.k = k self.fig, self.ax = plt.subplots(figsize=(10, 7)) plt.subplots_adjust(bottom=0.25) # 初始化参数 self.centers = data[np.random.choice(len(data), k, replace=False)] self.assignments = np.zeros(len(data)) self.colors = plt.cm.tab10(np.linspace(0, 1, k)) # 创建控件 ax_iter = plt.axes([0.2, 0.1, 0.6, 0.03]) self.slider = Slider(ax_iter, '迭代次数', 1, 20, valinit=1, valstep=1) ax_reset = plt.axes([0.8, 0.025, 0.1, 0.04]) self.reset_btn = Button(ax_reset, '重置', color='lightgoldenrodyellow') # 绑定事件 self.slider.on_changed(self.update) self.reset_btn.on_clicked(self.reset) self.animation = FuncAnimation(self.fig, self.animate, frames=20, interval=500, repeat_delay=2000) def reset(self, event): self.slider.reset() self.centers = self.data[np.random.choice(len(self.data), self.k, replace=False)] self.update(0) def update(self, val): iterations = int(self.slider.val) self.centers = self.data[np.random.choice(len(self.data), self.k, replace=False)] for _ in range(iterations): distances = np.sqrt(((self.data - self.centers[:, np.newaxis])**2).sum(axis=2)) self.assignments = np.argmin(distances, axis=0) self.centers = np.array([self.data[self.assignments == i].mean(axis=0) for i in range(self.k)]) self.draw_clusters() def animate(self, frame): distances = np.sqrt(((self.data - self.centers[:, np.newaxis])**2).sum(axis=2)) self.assignments = np.argmin(distances, axis=0) self.centers = np.array([self.data[self.assignments == i].mean(axis=0) for i in range(self.k)]) self.draw_clusters() return self.ax def draw_clusters(self): self.ax.clear() for i in range(self.k): cluster_data = self.data[self.assignments == i] self.ax.scatter(cluster_data[:,0], cluster_data[:,1], color=self.colors[i], s=50, alpha=0.6) self.ax.scatter(self.centers[i,0], self.centers[i,1], marker='x', s=200, c='k', linewidths=3) self.ax.set_title(f'K-Means 聚类 (k={self.k})') self.ax.grid(True) def show(self): plt.show() # 使用示例 data = load_data('test.txt') visualizer = InteractiveKMeans(data, k=4) visualizer.show()4. 可视化方案对比与选型
三种方案各有优劣,下面是详细对比:
| 方案类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 静态分步图 | 制作简单,步骤清晰 | 缺乏连续性,需要手动切换 | 教学演示、文档说明 |
| 动态迭代动画 | 直观展示迭代过程,视觉效果好 | 实现较复杂,性能开销大 | 现场演示、视频教程 |
| 中心点轨迹图 | 突出算法收敛特性 | 丢失了数据点分配细节 | 算法分析、参数调优 |
在实际项目中,我通常会根据需求组合使用这些技术。例如,用静态图解释算法原理,用动画展示实际运行过程,最后用轨迹图分析算法收敛情况。
