聚类算法(kmeans)详解和python实现

1,986 阅读3分钟

聚类算法简介

kmeans算法是无监督学习算法,它的主要功能就是把相似的类别规到一类中,虽然它和knn算法都是以k开头,但是knn却是一种监督学习算法.

那我们怎样去区分样本间的相似性呢?其实计算相似性的方式有很多,其中最常用的是欧示距离。

聚类算法的实现原理

假设我们有N个样本点,这N个样本点有k个分类,首先我们随机选取k个样本点作为质心,

  1. 我们遍历N个样本点x_i,计算x_i与每个质心的距离,找x_i与哪一个质心的距离最小,那么x_i就是质心距离最小的那一个类别,最后我们就可以把所有的样本点都分成不同的类别。
  2. 当我们通过(1)分好所有类之后,我们重新选择质心。再重复(1)的过程,直到算法收敛。

由于kmeans算法比较简单,在这里不详细进行介绍,直接给出算法的实现过程和结果。

算法python实现

# coding=gbk

'''
Created on 2018年2月28日

@author: sundarchen
'''

from numpy import *
from mpmath.tests.test_elliptic import zero
import matplotlib.pyplot as plt
from _ast import If

'''
k-means分类模型
@param dataSet: n*n维数组
@param kValue:  分类类型
'''
def kmeans(dataSet, kValue):
    '''构建n*(n + 1)维数组,用于存放离cluster的距离'''
    currentClusters = randomSelectCluster(dataSet, kValue)
    
    '''用于记录使用kmeans分后类的结果'''
    kmeansResult = {}
    
    totalRows = dataSet.shape[0]
    while True:
        '''重置分类后结果,后面会重新计算'''     
        kmeansResult.clear()
        
        '''重新计算所有样本到cluster的距离'''
        for currentRow in range(totalRows):
            minCluster = -1
            minDistance = 100000000
            currentRowData = dataSet[currentRow:(currentRow + 1)]
            '''计算当前的行与所有的cluster的距离,找出最小的距离'''
            for clusterIndex in range(len(currentClusters)):
                '''计算距离'''
                innerDistance = calcuateDistance(currentRowData, currentClusters[clusterIndex])
                '''记录最小距离'''
                if minDistance > innerDistance:
                    minCluster = clusterIndex
                    minDistance = innerDistance
            '''找出当前的行与哪个cluster的距离最小后,就把当前的行分配到相应的cluster下面进行分类'''
            print("minCluster ====== " + str(minCluster))
            if minCluster in kmeansResult:
                resultArray = kmeansResult[minCluster]
                resultArray.append(currentRowData)
            else:
                resultArray = []
                resultArray.append(currentRowData)
                kmeansResult[minCluster] = resultArray
            
        
        currentRow = 0
        innerClusterArray = mat(zeros((kValue, 2)))
        '''重新计算所有集合的cluster点'''
        for key in kmeansResult:
            resultArray = kmeansResult[key]
            '''重新计算中点,即集合中所有的点与点之间的距离'''
            minClusterValue = 0
            minClusterIndex = -1
            for outerIndex in range(len(resultArray)):
                innterMinClusterValue = 0
                for innerIndex in range(len(resultArray)):
                    innterMinClusterValue += calcuateDistance(resultArray[outerIndex], resultArray[innerIndex])
                if innterMinClusterValue < minClusterValue or minClusterValue == 0:
                    minClusterIndex = outerIndex
                    minClusterValue = innterMinClusterValue;
            '''存放cluster点'''
            innerClusterArray[currentRow, :] = resultArray[minClusterIndex]
            currentRow += 1
        '''算法收敛'''
        if (currentClusters == innerClusterArray).all():
            break
        '''重新找到的cluster点'''
        currentClusters = innerClusterArray
    return currentClusters, kmeansResult
'''
随机选择k个中心点
'''
def randomSelectCluster(dataSet, kValue):
    indexCache = []
    indexValue = 0
    centerSamples = mat(zeros((kValue, dataSet.shape[1])))
    totalRows = dataSet.shape[0]
    while indexValue < kValue:
        index = random.randint(0, totalRows)
        if index in indexCache:
            continue
        indexCache.append(index)
        rowData = dataSet[index, :] 
        centerSamples[indexValue,:] = rowData
        indexValue += 1
    return centerSamples


'''
计算样本和中心点的距离
'''
def calcuateDistance(rowData, clusterData):
    return math.sqrt((rowData - clusterData) * (rowData - clusterData).T)

'''
显示Kmeans结果集
'''
def showKmeans(currentClusters, kmeansResult):
    x = []
    y = []
    marker = ["a", "b", "c", "d", "e"]
    '''中心点'''
    for kmeanRow in currentClusters:
        x.append(kmeanRow[0, 0])
        y.append(kmeanRow[0, 1])
    plt.scatter(x,y, color='r', label=marker[0])
    
    '''分类点'''
    typeColor = ["m", "c", "b", "k"]
    for kmeanRow in kmeansResult:
        x1 = []
        y1 = []
        kmeansResultRow = kmeansResult[kmeanRow]
        for innerRow in kmeansResultRow:
            isFinded = False
            '''判断当前的点必须为非中心点'''
            for kmeanRow2 in currentClusters:
                if kmeanRow2[0, 0] == innerRow[0, 0] and kmeanRow2[0, 1] == innerRow[0, 1]:
                    isFinded = True
            '''上面己经画出了中心点,这里只要画非中心点就可以'''
            if not isFinded:
                x1.append(innerRow[0, 0])
                y1.append(innerRow[0, 1])
        plt.scatter(x1, y1, color=typeColor[kmeanRow], label=marker[kmeanRow + 1])
    plt.show()

以下给出测试代码


# coding=gbk

import random
import kmeans2
from numpy import *

def createDataSet():
    result = []
    totalRows = 100
    xAdder = 15
    yAdder = 10
    centerPoints = [[5, 10], [20, 10], [5, 0], [15, 0]]
    for row in range(totalRows):
        randomIndex = random.randint(0, len(centerPoints))
        randomPoints = centerPoints[randomIndex]
        randomX = random.randint(0, xAdder - 3)
        randomY = random.randint(0, yAdder - 3)
        result.append([randomPoints[0] + randomX, randomPoints[1] + randomY])
    return result


dataSet = mat(createDataSet())
currentClusters, kmeansResult = kmeans2.kmeans(dataSet, 4)

'''
显示kmeans数据
'''
kmeans2.showKmeans(currentClusters, kmeansResult)


运行结果