K-均值

优点:容易实现
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
K-均值是发现给定数据集的k歌簇的算法。簇个数k是用户给定的,每个簇通过其质心,即簇中所有点的中心来描述。
算法流程
1)随机确定k个初始点作为质心;
2)将数据集中的每个点分配到一个簇中,即为每个点找与其最近的质心,并将其分配给该质心所对应的簇。

3)更新每个簇的质心,即计算每个簇中所有点的质心作为新的质心。

def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))   #簇分配矩阵,一列记录<span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">索引值,另一列记录当前点到质心的距离</span>
    centroids=createCent(dataSet,k)    <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#随机确定k个质心</span>
    clusterChanged=True
    while clusterChanged:
        clusterChanged=False
        for i in range(m):        <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#遍历找所有数据点,找到距离每个点最近的质心</span>
            minDist=inf;minIndex=-1
            for j in range(k):
                distJI=distMeas(centroids[j,:],dataSet[i,:])
                if distJI<minDist:
                    minDist=distJI;minIndex=j
            if clusterAssment[i,0]!=minIndex:clusterChanged=True
            clusterAssment[i,:]=minIndex,minDist**2
        print centroids
        for cent in range(k):
            ptsInClust=dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]  <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#数组过滤获得给定簇的所有点   </span>
            centroids[cent,:]=mean(ptsInClust,axis=0)     <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#计算所有点的均值更新簇</span>
    return centroids,clusterAssment

K-均值算法收敛但聚类效果较差的原因是:K-均值收敛到了局部最小值,而非全局最小值。
度量聚类效果的指标SSE(Sum of Squared Error,误差平方和),对应上面簇分配结果矩阵的第一列之和。SSE越小表明数据点越接近与它们的质心,聚类效果越好。
使用后处理来提高聚类性能:
1)将具有最大SSE值的簇划分为两个簇
2)合并最近的质心,或者合并两个使得SSE增幅最小的质心。

二分K-均值
算法流程:
1)将所有点作为一个簇
2)然后将簇一分为二;
3)选择其中一个簇继续划分,选择哪一个簇进行划分取决于对其划分是否可以最大程度降低SSE值;
4)不断重复上面基于SSE的划分过程,知道得到用户指定的簇数目为止。
def biKmeans(dataSet,k,distMeas=distEclud):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))
    centroid0=mean(dataSet,axis=0).tolist()[0]  <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#初始簇心为dataSet按列计算平均值</span>
    centList=[centroid0]   <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#使用列表保存所有簇心</span>
    for j in range(m): <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#开始时将簇中所有点看成一个数据集,然后对其进行K-均值划分,每次分为两个簇</span>
        clusterAssment[j,1]=distMeas(mat(centroid0),dataSet[j,:])**2
    while(len(centList)<k):
        lowestSSE=inf
        for i in range(len(centList)):
            ptsInCurrCluster=dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
            centroidMat,splitClustAss=kMeans(ptsInCurrCluster,2,distMeas)
            sseSplit=sum(splitClustAss[:,1])
            sseNotSplit=sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1]) <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#将划分误差和未划分误差的和作为本次划分的误差</span>
            print "sseSplit, and notSplit",sseSplit,sseNotSplit
            if (sseSplit+sseNotSplit)<lowestSSE:  <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#若本次划分的误差最小,则保存本次划分</span>
                bestCentToSplit=i
                bestNewCents=centroidMat
                bestClustAss=splitClustAss.copy()
                lowestSSE=sseSplit+sseNotSplit
        bestClustAss[nonzero(bestClustAss[:,0].A==1)[0],0]=len(centList)  <span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#更新簇的分配结果,新划分的簇编号分别为0和1,将编号为0的簇分配到簇编号为i的簇中,将编号为1的簇分配到簇编号为len(centList)中(新增加的簇)</span>
        bestClustAss[nonzero(bestClustAss[:,0].A==0)[0],0]=bestCentToSplit
        print 'the bestCentToSplit is:',len(bestClustAss)
        print 'the len of bestClustAss is:',len(bestClustAss)
        centList[bestCentToSplit]=bestNewCents[0,:]
        centList.append(bestNewCents[1,:])<span style="font-family: 微软雅黑; font-size: 14px; line-height: 21px; widows: auto;">#添加新的质心到簇列表中</span>
        clusterAssment[nonzero(clusterAssment[:,0].A==bestCentToSplit)[0],:]=bestClustAss
    return centList,clusterAssment

示例:对地图上的点进行聚类

def distSLC(vecA,vecB):
    a=sin(vecA[0,1]*pi/180)*sin(vecB[0,1]*pi/180)
    b=cos(vecA[0,1]*pi/180)*cos(vecB[0,1]*pi/180)*cos(pi*(vecB[0,0]-vecA[0,0])/180)
    return arccos(a+b)*6371.0


def clusterClubs(numClust=5):
    datList=[]
    for line in open('places.txt').readlines():
        lineArr=line.split('\t')
        datList.append([float(lineArr[4]),float(lineArr[3])])
    dataMat=mat(datList)
    myCentroids,clustAssing=biKmeans(dataMat,numClust,distMeas=distSLC)

    fig=plt.figure()
    rect=[0.1,0.1,0.8,0.8]
    scatterMarker=['s','o','^','8','p','d','v','h','>','<']
    axprops=dict(xticks=[],yticks=[])
    ax0=fig.add_axes(rect,label='ax0',**axprops)

    imgP=plt.imread('Portland.png')
    ax0.imshow(imgP)

    ax1=fig.add_axes(rect,label='ax1',frameon=False)
    for i in range(numClust):
        ptsInCurrCluster=dataMat[nonzero(clustAssing[:,0].A==i)[0],:]
        markerStyle=scatterMarker[i%len(scatterMarker)]
        ax1.scatter(ptsInCurrCluster[:,0].flatten().A[0],ptsInCurrCluster[:,1].flatten().A[0],marker=markerStyle,s=90)
        ax1.scatter(myCentroids[i][:,0].flatten().A[0],myCentroids[i][:,1].flatten().A[0],marker='+',s=300)
    plt.show()
结果




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值