K-means聚类算法(事先数据并没有类别之分!所有的数据都是一样的)
K-means聚类
- 1 概述
- 2 核心思想
- 3 算法步骤
- 4 代码实现
1 概述
K-means算法是集简单和经典于一身的基于距离的聚类算法
采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。
该算法认为类簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。
2 核心思想
通过迭代寻找k个类簇的一种划分方案,使得用这k个类簇的均值来代表相应各类样本时所得的总体误差最小。
k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开。
k-means算法的基础是最小误差平方和准则,
其代价函数是:
![在这里插入图片描述]()
式中,μc(i)表示第i个聚类的均值。
各类簇内的样本越相似,其与该类均值间的误差平方越小,对所有类所得到的误差平方求和,即可验证分为k类时,各聚类是否是最优的。
上式的代价函数无法用解析的方法最小化,只能有迭代的方法。
3 算法步骤
(1) 随机选取 k个聚类质心点
(2)计算每个点,与K个中心点的距离,然后将每个点聚集到与之最近的中心点
(3)新的聚集出来之后,计算每个聚集的新中心点
(4)迭代步骤2和步骤3,直至满足退出条件(条件有两个:
1聚类中心点不再变化,
2迭代次数达到某个值;
具体计算时,可以取其中一个条件,或者两个条件同时取到)
4 代码实现
import numpy as np
import matplotlib.pyplot as plt
# 计算两点之间的欧式距离
def distance(e1, e2):
return np.sqrt((e1[0]-e2[0])**2+(e1[1]-e2[1])**2)
# 集合中心,arr是一个包含元组的列表,e就是一个二元组,e[0]是x,e[1]是y
def means(arr):
return np.array([np.mean([e[0] for e in arr]), np.mean([e[1] for e in arr])])
# arr中距离a最远的元素,用于初始化聚类中心
def farthest(k_arr, arr):
"""
:param k_arr:一个初始点
:param arr:所有点
:return:返回与目前聚类中心点距离最远的点
"""
f = [0, 0]
# 最远距离
max_d = 0
# e就是一个点
for e in arr:
d = 0
# i只能是0,for语句只循环了一次
for i in range(k_arr.__len__()):
# 算出每个点到所有聚类中心的距离和
d = d + np.sqrt(distance(k_arr[i], e))
# 如果d比目前最大的距离大,那最大距离就变为现在得到的最大距离
if d > max_d:
max_d = d
# f就是距离这些聚类中心最远的点
f = e
return f
"""
# arr中距离a最近的元素,用于聚类
def closest(a, arr):
c = arr[1]
min_d = distance(a, arr[1])
arr = arr[1:]
for e in arr:
d = distance(a, e)
if d < min_d:
min_d = d
c = e
return c
"""
if __name__=="__main__":
## 生成二维随机坐标,手上有数据集的朋友注意,理解arr改起来就很容易了
## arr是一个数组,每个元素都是一个二元组,代表着一个坐标
## arr形如:[ (x1, y1), (x2, y2), (x3, y3) ... ]
# arr是随机得到的包含100个点的列表
arr = np.random.randint(100, size=(100, 1, 2))[:, 0, :]
print("得到的arr是:", arr)
## 初始化聚类中心和聚类容器
m = 5
# r是一个随机数
r = np.random.randint(arr.__len__() - 1)
print("得到的r是:",r)
# 随机得到其中一个点,k_arr也就是其中的一个聚类中心
k_arr = np.array([arr[r]])
print("得到的k_arr是:",k_arr)
cla_arr = [[]]
# 循环4次,每次都往聚类中心增加1个点,1开始只有1个点,再增加4个
# 这个循环的目的就是得到聚类中心
for i in range(m-1):
# 得到距离已有聚类中心点,最远的一个点
k = farthest(k_arr, arr)
# 这样k_arr列表中的点的个数在不断增加
k_arr = np.concatenate([k_arr, np.array([k])])
print("第%d次,k_arr是%s"%(i,k_arr))
# cla_arr就是2维列表,循环4次,就在行的维度上由原来的1行再增加5行,每行存放输入一个类的点,为之后的计算做准备
cla_arr.append([])
print("cla_arr是:" )
print(cla_arr)
## 迭代聚类,
# 迭代20次
n = 20
# 这里copy给了另一个变量,不copy也行,结果不受影响
cla_temp = cla_arr
# 核心的聚类过程,一共聚类
for i in range(n): # 迭代n次
for e in arr: # 把集合里每一个元素聚到最近的类
ki = 0 # 假定距离第一个中心最近
# 计算每个点与聚类中心的距离
min_d = distance(e, k_arr[ki])
for j in range(1, k_arr.__len__()):
if distance(e, k_arr[j]) < min_d: # 找到更近的聚类中心
# 得到最小距离
min_d = distance(e, k_arr[j])
# 该点就是j类
ki = j
# cla_temp就是存储着点,第一行就是类1,第2行就是类2.。。。
cla_temp[ki].append(e)
# 迭代更新聚类中心
for k in range(k_arr.__len__()):
# 如果已经迭代了n次了,就不迭代了
if n - 1 == i:
break
# 得到聚类中心,
# 这里差一个判断语句,如果得到的聚类中心和之前的一样,就可以提前结束程序了,否则白白增加计算时长
k_arr[k] = means(cla_temp[k])
# 存放不同类的点的列表清空
cla_temp[k] = []
## 可视化展示
col = ['HotPink', 'Aqua', 'Chartreuse', 'yellow', 'LightSalmon']
# 循环5次
for i in range(m):
# 显示出中心点,k_arr是2维列表
plt.scatter(k_arr[i][0], k_arr[i][1], linewidth=10, color=col[i])
# 显示出点
plt.scatter([e[0] for e in cla_temp[i]], [e[1] for e in cla_temp[i]], color=col[i])
plt.show()