机器学习笔记:kMeans聚类

kMeans聚类

优点:容易实现。
缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
适用数据类型:数值型数据

K-均值算法:
先随机确定k个初始点作为质心,然后将数据集中的每个点分配到一个簇中,具体来讲为每个点找距其最近的质心,并将其分配给该质心所对应的簇。
再每个簇更新质心为该簇所有点的平均值。

Python 3.6实现kMeans算法

import numpy as np
#加载数据
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr:
        curLine = line.strip().split('\t')
        fltLine = list(map(float, curLine))
        dataMat.append(fltLine)
    return dataMat

#欧氏距离
def distEclud(vecA, vecB):
    #这里直接用系统自带的sum方法会出错
    return np.sqrt(np.sum(np.power(vecA - vecB,2)))

def randCent(dataSet, k):
    n = np.shape(dataSet)[1]
    centroids = np.mat(np.zeros((k,n)))
    for j in range(n):   
        mins = min(dataSet[:,j])
        ranges = float(max(dataSet[:,j]) - mins)
        centroids[:,j] = mins + np.random.rand(k,1)*ranges
    '''
    dataSet = matrix([[ 1.,  0.,  0.,  0.,  0.,  0.],
                      [ 0.,  1.,  0.,  0.,  0.,  0.],
                      [ 0.,  0.,  1.,  0.,  0.,  0.],
                      [ 0.,  0.,  0.,  1.,  0.,  0.],
                      [ 0.,  0.,  0.,  0.,  1.,  0.],
                      [ 0.,  0.,  0.,  0.,  0.,  1.]])
    k = 4
     ||
     ||
    \  /
     \/
    centroids = matrix([[ 0.88338869,  0.94654424,  0.86819242,  0.73792633,  0.88794344,
          0.82862507],
                        [ 0.28036179,  0.73843775,  0.96017969,  0.65308858,  0.10413186,
          0.13752974],
                        [ 0.34094064,  0.27335544,  0.87242206,  0.89623225,  0.67307911,
          0.33853315],
                        [ 0.99018662,  0.72860503,  0.31156304,  0.29371852,  0.45232003,
          0.09021325]])
    '''
    return centroids

#kMeans算法
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
    m = np.shape(dataSet)[0]
    #用来记录每条记录的簇中心位置和距离
    clusterAssment = np.mat(np.zeros((m,2)))
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        for i in range(m):
            minDist = np.inf;
            minIndex = -1;
            #计算每条记录与每个簇中心点,得到最近中心点
            for j in range(k):
                distJI = distMeas(centroids[j,:], dataSet[i,:])
                if distJI < minDist:
                    minDist = distJI;
                    minIndex = j
            #只要有一条记录的簇中心变化了,则改动clusterChanged为True
            #如果clusterChanged一直没变,也就是说算法已经收敛了
            if clusterAssment[i,0] != minIndex:
                clusterChanged = True
            #记录这条记录最近的簇中心位置和距离
            clusterAssment[i,:] = minIndex, minDist**2
        print(centroids)
        #改变簇中心位置为:
        #   以这个簇中心为中心的所有记录的距离的平均值;
        #   centroids = mean(以这个为中心的数据集.distences)
        for cent in range(k):
            ptsInClust = dataSet[np.nonzero(clusterAssment[:,0].A==cent)[0]]
            centroids[cent,:] = np.mean(ptsInClust, axis = 0)
    return centroids, clusterAssment

二分K-均值算法 Python 3.6 实现

#二分K-均值算法
def biKmeans(dataSet, k, distMeas=distEclud):
    m = np.shape(dataSet)[0]
    #用来记录每条记录的簇中心位置和距离
    clusterAssment = np.mat(np.zeros((m,2)))
    #将整个数据集划分为一个簇
    #由于np.mat需要后面取[0]
    centroids0 = np.mean(dataSet, axis=0).tolist()[0]
    #记录
    centList = [centroids0]
    #初始化数据集的簇中心位置标记和距离
    for j in range(m):
        clusterAssment[j,1] = distMeas(dataSet[j,1],np.mat(centroids0))**2
    while len(centList) < k:
        lowestSEE = np.inf
        for i in range(len(centList)):
            #得到当前的簇的数据集
            ptsInCurrCluster = dataSet[np.nonzero(clusterAssment[:,0].A==i)[0],:]
            #用之前的kmeans算法将这个数据集划分为两个簇
            #得到划分好的簇中心和数据集
            centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
            #误差平方和
            #也就是所有记录与簇中心的距离平方和
            #计算当前已经划分好的数据集的误差平方和
            sseSplit = sum(splitClustAss[:,1])
            #计算剩下的数据集的误差平方和
            sseNotSplit = sum(clusterAssment[np.nonzero(clusterAssment[:,0].A!=i)[0],1])
            #对比+替换
            if (sseSplit + sseNotSplit) < lowestSEE:
                bestCentToSplit = i
                bestNewCents = centroidMat
                bestClustAss = splitClustAss.copy()
                lowestSEE = sseNotSplit + sseSplit
        #将最优划分的两个簇的第一个簇数据集标记为原本centList的长度
        bestClustAss[np.nonzero(bestClustAss[:,0].A == 1)[0], 0] = len(centList)
        #第二个簇数据集标记为原本的簇的标记
        bestClustAss[np.nonzero(bestClustAss[:,0].A == 0)[0], 0] = bestCentToSplit
        #将原本划分的簇替代为最新划分的两个簇的前一个
        centList[bestCentToSplit] = bestNewCents[0,:]
        #追加一个簇为最新划分的后一个
        centList.append(bestNewCents[1,:])
        #将clusterAssment中bestCentToSplit簇数据集
        #替换为划分完的两个簇标记的数据集
        clusterAssment[np.nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
    print(centList)
    return centList, clusterAssment

猜你喜欢

转载自blog.csdn.net/u011094454/article/details/79039577