模糊Kmeans
基本思想:首先设定一些类及每个样本对各类的隶属度; 然后通过迭代,不断调整聚类中心和隶属度至收敛。收敛条件是隶属度的变化量小于规定的阈值。
算法实现步骤:
1.给定样本个数X(个数N),类数K,迭代收敛常数e。
2.给定或随机生成隶属度U,U[i][j]表示第j个元素对第i类的隶属度。
3.计算各类的聚类中心Zj
i=1,2........K;j=1,2......N;m>=2
4.计算新的隶属度矩阵U
i=1,2........K;j=1,2......N;m>=2
5.判断是否满足收敛条件
收敛条件:
当算法收敛时聚类结束,否则返回第三步
6根据最终迭代的隶属度结果进行分类。
j=1,2......N
时间复杂度:O(tK2mn)
空间复杂度:O((m+K)n+Km)
程序运行结果图:
Python实现
注:此程序需要引入一些外部开源库,详细内容请参照网上教程,此处省略;sample.txt和membership.txt中的数据由MATLAB随机生成一定格式数据。
import math #导入math模块
import matplotlib.pyplot as plt #导入画图库
W=2; #样本坐标维度(本程序只考虑二维坐标中的数据点)
N=180; #样本数量
K=3; #类数
M=2; #控制模糊程度的参数
E=0.0001 #达到收敛时最小误差
#从sample.txt中读入样本坐标
X = []
for i in open("sample.txt"):
row = [float(x) for x in i.split()]
if len(row):
X.append(row)
#聚类中心
Z=[ [0 for i in range(W) ] for j in range(K) ]
#计算乘方
def pow(a,b):
return a**b
#计算距离
def dis(x1,y1,x2,y2):
return math.sqrt((x1-x2)**2+(y1-y2)**2)
#更新聚类中心和隶属度
def membership(U):
#生成二维列表,K行N列
UL=[ [ 0.0 for i in range(N)] for j in range(K)]
#求聚类中心Z
for i in range(0,K,1):
s1=s2=s3=0.0
for j in range(0,N,1):
s1+=pow(U[i][j],M)*X[j][0]
s2+=pow(U[i][j],M)*X[j][1]
s3+=pow(U[i][j],M)
Z[i][0]=s1/s3
Z[i][1]=s2/s3
#求新的隶属度
for i in range(0,K,1):
for j in range(0,N,1):
s=0.0
d=dis(Z[i][0],Z[i][1],X[j][0],X[j][1])
if d==0:
UL[i][j]=1
else:
for k in range(0,K,1):
d1=dis(Z[k][0],Z[k][1],X[j][0],X[j][1])
s+=pow(d/d1,2/(M-1))
UL[i][j]=1/s
return UL
#计算新旧隶属度最大差值
def MAX_error(U,UL):
max=0.0
for i in range(0,K,1):
for j in range(0,N,1):
t=abs(UL[i][j]-U[i][j])
if max<t:
max=t
return max
#画图函数,显示结果
def showMap(UL):
#定义点的颜色,这里只定义四种,即最大为四类
mark1 = ['or', 'ob', 'og', 'ok']
mark2 = ['Dr', 'Db', 'Dg', 'Dk']
#定义横纵坐标的范围
for i in range(0,N,1):
max=0.0
index=0
#找出每个点对K个类的隶属度中最高的那个,并归为那一类
for j in range(0,K,1):
if UL[j][i]>max:
max=UL[j][i]
index=j
plt.plot(X[i][0],X[i][1],mark1[index])
#显示聚类中心
for i in range(0,K,1):
plt.plot(Z[i][0],Z[i][1],mark2[i])
plt.xlim(-5.0,20.0)
plt.ylim(-6.0,14.0)
plt.show()
#主函数
def main():
#从membership.txt文件中导入初始化的隶属度
U = []
for i in open("membership.txt"):
row = [float(x) for x in i.split()]
if len(row):
U.append(row)
#归一化
s=[0.0 for i in range(N)] #初始化每列的和
for i in range(0,K,1):
for j in range(0,N,1):
s[j]+=U[i][j]
for i in range(0,K,1):
for j in range(0,N,1):
U[i][j]=U[i][j]/s[j]
UL=membership(U)
err=MAX_error(U,UL)
#进行迭代,直到隶属度的最大差值小于常数E
while(err>E):
U=UL
UL=membership(U)
err=MAX_error(U,UL)
#绘图
showMap(UL)
#__name__作为模块的内置属性,就是.py文件的调用方式。.py文件有两种使用方式:作为模块被调用和
#直接使用。如果它等于"__main__"就表示是直接执行。 在if __name__ == "__main__":之后的
#语句作为模块被调用的时候,语句之后的代码不执行;直接使用的时候,语句之后的代码执行。
if __name__ == '__main__':
main()