一、基本介绍
聚类就是对大量未知标注的数据集,按数据的内在相似性将数据集划分为多个类别,使类别内的数据相似度较大而类别间的数据相似度较小。由这个定义可以知道,数据集并没有目标值。因此聚类算法属于无监督算法。k-Means算法的思想很简单,对于给定的样本集,按照样本之间的距离大小,将样本集划分为k个簇,同时使簇内的点尽量紧密的连在一起,簇间的距离尽量的大。
二、核心思想
给定一个有n个对象的数据集,划分聚类技术将构造数据k个划分,每一个划分就代表一个簇,k≤n。也就是说它将数据划分为k个簇,而且这k个划分满足下列条件:
①每一个簇至少包含一个对象;
②每一个对象属于且仅属于一个簇。
对于给定的k,算法首先给出一个初始的划分方法,以后通过反复迭代的方法改变划分,使得每一次改进之后的划分方案都较前一次更好,即同一簇中的对象越近越好,而不同簇中的对象越远越好。目标是最小化所有对象与其参照点之间的相异度之和,这里的远近或者相异度/相似度实际上是聚类的评价函数。
三、原理演示
给出样本点和k值
随机选出k个初始点
标出聚类中心
计算剩余样本点到k个聚类中心的距离,指向最近的聚类中心
划分聚类
重新计算聚类中心
递归计算,直到准则函数收敛或不产生新的聚类
四、算法流程图
五、关键源码展示
1、导入数据
2、划分聚类
3、输出聚类情况,绘制散点图
六、扩展实验
DBSCAN与k-Means的对比实验
七、完整代码与数据集
1、完整代码
import math
import random
import matplotlib.pyplot as plt
# 读取数据
def loadData():
try:
with open("k-Means.txt", "r") as f:
flines = f.readlines()
dataSet = []
for item in flines[0:]:
dataSet.append(item.split())
# 获取数据维度
dimen = len(dataSet[0])
return dimen, dataSet
except Exception as e:
print(e)
# 欧氏距离
def distance(A, B):
dist = 0
for i in range(len(A)):
d = float(A[i]) - float(B[i])
dist += d ** 2
return dist ** 0.5
# 把每一个样本点划分到距离它最近的聚类中心所在的类
def Partition_Clustering(dataSet, center):
cluster = []
for i in range(len(center)):
cluster.append([])
for item in dataSet:
clusterID = 0
minDist = distance(item, center[0])
for i in range(len(center)):
dist = distance(item, center[i])
if dist < minDist:
minDist = dist
clusterID = i
cluster[clusterID].append(item)
return cluster
# 计算聚类中心
def getCenter(cluster, dimen):
center = []
for items in cluster:
Center = []
for i in range(dimen):
Center.append(0.0)
for item in items:
for i in range(dimen):
Center[i] += float(item[i])
if len(items) != 0:
for i in range(dimen):
Center[i] /= len(items)
center.append(Center)
return center
# 准则函数:误差平方和
def getE(center, cluster):
E = 0
for index, Center in enumerate(center):
for item in cluster[index]:
E += distance(item, Center) ** 2
return E
def k_Means(dataSet, center, dimen):
eps = 0.000001 # 精度要求
e = random.random() * 1000000 # 初始化准则函数的值
cnt = 1
while True:
cluster = Partition_Clustering(dataSet, center)
center = getCenter(cluster, dimen)
E = getE(center, cluster)
print('第{}次迭代...'.format(cnt))
show(center, cluster)
'''如果收敛,就结束,否则继续迭代'''
if math.fabs(E - e) < eps:
print('迭代结束')
return center, cluster
else:
print('继续迭代...')
e = E
cnt += 1
def show(center, cluster):
# print(cluster)
i = 1
for index, Center in enumerate(center):
print('cluster[{}]:{}->{}'.format(i, Center, cluster[index]))
i += 1
def drawDataset(data):
x = []
y = []
for xx, yy in data:
x.append(float(xx))
y.append(float(yy))
plt.scatter(x, y)
plt.show()
def draw(clusters):
# colValue = ['red', 'blue', 'gray', 'black', 'purple', 'deepskyblue', 'darkblue']
shape = ['.', 's', 'x', 'H', 'v', '^', '<', '>', '+', 'x', 'D']
num_cluster = len(clusters)
for i in range(num_cluster):
x = []
y = []
for j in range(len(clusters[i])): # 第i个簇的里面的元素
x.append(float(clusters[i][j][0]))
y.append(float(clusters[i][j][1]))
plt.scatter(x, y, color='red', label=i, marker=shape[i])
# plt.legend()
plt.show()
def main():
k = 3
dimen, dataSet = loadData()
drawDataset(dataSet)
# 随机选择k个样本点作为初始点
for i in range(0, 3):
print('k-Means[{}]'.format(i + 1))
center = []
n = []
cnt = 0
while cnt < k:
num = random.randint(0, len(dataSet) - 1)
if num not in n:
n.append(num)
center.append(dataSet[num][:])
cnt += 1
for j in range(0, len(center)):
print('Initial Point{}={}'.format(j + 1, center[j]))
center, cluster = k_Means(dataSet, center, dimen)
print('聚类结果如下')
show(center, cluster)
print()
draw(cluster)
if __name__ == "__main__":
main()
2、数据集
1 2
2 1
2 4
4 3
5 8
6 7
6 9
7 9
9 5
1 12
3 12
5 12
3 3