K-means算法实现

#coding=utf-8
import numpy as np
#《数据挖掘》习题10.2
# Function: K Means
# K-Means is an algorithm that takes in a dataset and a constant k
# and returns k centroids (which define clusters of data in the dataset which are similar to one another).
#K-means函数有两个输入,一个是数据集X,以numpy矩阵的格式传进来
#第二个参数是k,
# maxIt是停止迭代的条件(最多迭代多少次)
#我们传进来的数据集是一个4*2的numpy array,故每一行代表一个点,每一列代表一个特征值
#之后我们在运算的时候就可以把每一次初始化的标签加一列放到后面(最好是这样一种数据结构
#要知道总共有多少个点,多少列。因为我们想后面在这个数据集上多加一列把他作为每一次循环,对于每一个点的归类是多少
#故我们要获取本身传进来的数据集他有多少列,多少行 X.shape )
def kmeans(X, k, maxIt):
    numPoints, numDim = X.shape#行代表了有多少个点,列代表了特征向量的维度
    dataSet = np.zeros((numPoints, numDim + 1))#创建一个初始化全零矩阵,里面传入的参数是原来的行,列是原来的列+1
    #注意这上面有两个括号,因为我们传入的shape本身有括号,然后我们的zero也有括号
    dataSet[:,  :-1] = X#新的dataset,除了最后一列,所有的列都跟数据集一样
    #第一个冒号表示所有行,第二个冒号表示所有列除去最后一列。即从第一列一直到倒数第二列,他的值都是X
    # Initialize centroids randomly(先把中心点选好,再给中心点赋值)
    #第一次初始化管中心点,没有任何根据,所以是从所有样本点中随机的选择k个数值作为中心点
    #注意random有两个参数,一个是numpoint,表示一共有多少个样本(行),这个例子中是4行,选出来多少行(size=k)
    #np.random.randint(numPoints, size = k)就返回随机选出来的两行,列数是所有列都要 用一个冒号表示)
    #通过这个函数,我们的行数从以前的0-34行里面随机的选出来2个数作为行数的坐标,列数是所有列都要
    #选出来作为初始的中心点。
    centroids = dataSet[np.random.randint(numPoints, size = k), :]
    # centroids = dataSet[0:2, :]#这个是强迫的中心点

    #Randomly assign labels to initial centorid
    centroids[:, -1] = range(1, k +1)#给中心点的最后一列初始化
    # Initialize book keeping vars.
    #选好中心点之后要设两个变量,一个是循环多少次,
    #因为我们要不断地迭代中心点,所以我们用两个变量来储存中心点。一个旧的中心点,一个新的中心点。
    iterations = 0#
    oldCentroids = None

    # Run the main k-means algorithm主函数框架,只要不停止迭代(停止有多重判断条件)(旧的中心点和新的中心点他们之间如果不相等)
    #iterations和maxIt就是说循环到第几次,每一次循环我们就给他加1,maxIt就是我们允许他一共迭代多少次
    #在shouldstop里面一共传入4个参数:老中心,新中心,迭代次数,最大迭代次数。
    #如果不 停止,就循环下去。返回bool值
    #只要 不 false  就真,该干嘛-循环下去(1.打印一些信息)
    while not shouldStop(oldCentroids, centroids, iterations, maxIt):
        print ("iteration: \n", iterations)#循环到第几轮了。
        print ("dataSet: \n", dataSet)#在当前这一轮我们的数据集是什么样子的(就是特征向量)
        print ("centroids: \n", centroids)#在当前这一轮我们的中心点是什么样子的
        # Save old centroids for convergence test. Book keeping.
        #第一次循环的时候,最开始只是初始化了一些中心点,我们现在把刚才的初始化中心点复制一份到旧的中心点里面
        oldCentroids = np.copy(centroids)
        iterations += 1#循环一次,迭代次数就加1
        #简单来说就2大步,根据目前的中心点的值和目前的数据集把最后一列的labelupdate一下

        # Assign labels to each datapoint based on centroids
        updateLabels(dataSet, centroids)

        # Assign centroids based on datapoint labels
        centroids = getCentroids(dataSet, k)

    # We can get the labels too by calling getLabels(dataSet, centroids)
    return dataSet
# Function: Should Stop(大的函数框架就这样结束了。我们具体要做的就是要实现这几个方程。
# 一个是shouldstop(停止迭代的条件)
#一个是如何根据中心点c和数据集X来更新他的聚类类别
#重新分类之后。我们如何根据分类之后的数据集和k来算出新的中心点。
# Returns True or False if k-means is done. K-means terminates either
# because it has run a maximum number of iterations OR the centroids
# stop changing.(1.最多循环多少次。实际循环次数》预设次数==》停止
# 2.在没有达到最大迭代次数的时候,如果中心点不变==》停止)
#注意,在Python的numpy array里面。我们比较2个数据结构的时候。要注意到,到底是比较他们两个的值是不是相等
#还是比较他们两个是不是属于同一个object。
def shouldStop(oldCentroids, centroids, iterations, maxIt):
    if iterations > maxIt:
        return True
    return np.array_equal(oldCentroids, centroids)
# Function: Get Labels
# -------------
# Update a label for each piece of data in the dataset.
def updateLabels(dataSet, centroids):#传入一个表格(矩阵),和一些中心点
    # For each element in the dataset, chose the closest centroid.
    # Make that centroid the element's label.
    numPoints, numDim = dataSet.shape#获取这个数据集有多少行、列
    for i in range(0, numPoints):#根据这个数据集的每一行,根据中心点距离比较之后,
        #我们要把他的最后一列赋予一个值
        #dataSet[i, -1]:每一行的最后一列,赋予类别
        #对于每一行来讲,我们传入这一行和所有中心点,比较这一行和每一个中心点的距离
        #看那个最近,就把那个中心点的label返回到当前行
        dataSet[i, -1] = getLabelFromClosestCentroid(dataSet[i, :-1], centroids)

def getLabelFromClosestCentroid(dataSetRow, centroids):#函数参数:当前行,中心点
    # 我们先让label等于第一个中心点中的值,然后初始化一个变量mindist(我们后来会对比当前行datasetrow和每一个中心点的值)
    # 所以我们把最后一列(label)取出来
    # 对中心点中的每一个值进行循环,如果某一个点的值小于我们开始定义的最小值,我们就把
    # 那个值赋予这个最小值,所以我们可以保证这个mindistance永远是最小的,同时可以更新label
    # 这就是一个求最大最小数的一个常用算法。
    label = centroids[0, -1]#中心点的数据结构跟dataset很像,他的行数是k,列数跟dataset一样
    minDist = np.linalg.norm(dataSetRow - centroids[0, :-1])
    for i in range(1 , centroids.shape[0]):#之前0的中心点已经用过,所以现在用的是从1开始,从1到最后一个中心点的行数
        dist = np.linalg.norm(dataSetRow - centroids[i, :-1])#这个distance就是当前每一次循环的时候,我们的当前row
        # 和中心点中的某一个点的距离,这个是numpy里面专门算距离的函数,传入两个向量,返回这两个向量的距离
        if dist < minDist:
            minDist = dist
            label = centroids[i, -1]
    print("minDist:", minDist)
    return label

# Function: Get Centroids
# -------------
# Returns k random centroids, each of dimension n.
#根据重新划分的数据集以及k,我们重新算出来中心点
#我们刚刚是根据中心点来分类数据集
#这时候是根据新划分好的数据集以及K重新算更新过之后的中心点。(算均值)
def getCentroids(dataSet, k):#传入一个数据集(这儿包括标签和k值)
    # Each centroid is the geometric mean of the points that
    # have that centroid's label. Important: If a centroid is empty (no points have
    # that centroid's label) you should randomly re-initialize it.
    result = np.zeros((k, dataSet.shape[1]))#全部初始化为0,行数等于k,列数与dataset一样
    for i in range(1, k + 1):#所有归为1类的点求均值
        oneCluster = dataSet[dataSet[:, -1] == i, :-1]#一个类别里面的所有点拿出来(1把某一个标签的所有数据点都找出来)
        result[i - 1, :-1] = np.mean(oneCluster, axis = 0)#axis=0,对行求均值,axis=1就是对列(2求均值,求出来之后把它赋值到中心点中的从开始到倒数第二列中)
        result[i - 1, -1] = i#标签也要赋值一下,标签就是当前的i值,

    return result#返回新的新中心点
#返回中心点之后我们就完成了对整个代码的实现

x1 = np.array([2, 10])
x2 = np.array([2, 5])
x3 = np.array([8, 4])
x4 = np.array([5, 8])
x5 = np.array([7, 5])
x6 = np.array([6, 4])
x7 = np.array([1, 2])
x8 = np.array([4, 9])
testX = np.vstack((x1, x2, x3, x4,x5,x6,x7,x8)) #把这8个点堆叠起来形成一个8*2的矩阵,数据集是testX
result = kmeans(testX, 3, 20)
print("final result:")
print(result)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值