(理论部分略)
步骤:
手肘法 : 计算误差平方和(SSE),即所有样本的聚类误差
SSE:
S
S
E
=
∑
i
=
1
k
∑
p
∈
C
i
∣
p
−
m
i
∣
2
SSE=\sum^k_{i=1}\sum_{p\in C_i} |p-m_i|^2
SSE=i=1∑kp∈Ci∑∣p−mi∣2
c i c_i ci是第i簇,p为 c i c_i ci的中样本点, m i m_i mi为 c i c_i ci的质心,随着k的增大,样本划分更精细,每个簇的聚合程度会逐渐提高,因此SSE会减少,呈现下图:
因此,可以按以下规律选择k值:
因此,一般取肘部时的k值。
def classify(data, centers):
'''
聚类,计算数据和类别中心点聚类,与对应类别距离最短的为一类
Args:
data: 数据 ndarray(n,2)
centers: 类别的中心点 ndarray(c,2)
Returns: 新的类别(list),新的距离(float)
'''
# 类别数
clen = centers.shape[0]
# 类的列表,装每个类有哪些数据
classes = [ [] for i in range(clen)]
# 距离
sumDis = 0
for i in range(data.shape[0]):
per_data = data[i]
# np.tile()将数据横向或纵向拷贝 -> [1,3] 纵向拷贝三份得到,[[1,3],[1,3],[1,3]]
# 采用tile将数据拷贝clen份,然后在对应维度上进行相减,可以得到 当前坐标点与所有类别的距离
diff = np.tile(per_data, (clen, 1)) - centers
# ((x2-x1)^2+(y2-y1)^2) 距离平方求和
sqDiff = diff**2
sqDiff = sqDiff.sum(axis=1)
# 对距离排序
sortIdx = sqDiff.argsort()
# 取距离最小的类别索引,把当前数据放入该类别
classes[sortIdx[0]].append(list(per_data))
# 记录最小距离
sumDis += sqDiff[sortIdx[0]]
return classes, sumDis
def updateCenters(classes):
# 新的中心点
new_centers = []
for i in range(len(classes)):
per_class = classes[i] # list
# 转ndarray
per_class = np.array(per_class)
center = per_class.sum(axis=0) / len(per_class)
new_centers.append(center)
return np.array(new_centers)
def kmeans(data, centers, sumDis):
'''
聚类
Args:
data:数据
centers:类别中心点
sumDis:距离总和
Returns:
'''
# 聚类
classes, new_sumDis = classify(data, centers)
# 如果新的中心点不变,则结束
if sumDis == new_sumDis:
return
# 更新中心点
new_centers = updateCenters(classes)
# 再聚类
kmeans(data, new_centers, new_sumDis)