Python中比较numpy的Kmeans算法和不用numpy算法的效率

kemans算法

K-means算法是硬聚类算法,是典型的基于原型的目标函数聚类方法的代表,它是数据点到原型的某种距离作为优化的目标函数,利用函数求极值的方法得到迭代运算的调整规则。K-means算法以欧式距离作为相似度测度,它是求对应某一初始聚类中心向量V最优分类,使得评价指标J最小。算法采用误差平方和准则函数作为聚类准则函数。

要求随机取1000个点,其中随机5个为中心点,.求其余995个点与每个中心点的距离距离最小的,将此点和对应的中心点归为一类.类中求平均值作为新的中心点如果新中心点和旧中心点的误差小于某个阈值,停止,否则重复以上操作。

代码实现(使用numpy的)  未使用numpy的Kmeans算法在本篇文章的下一篇

#引入需要的模块
import numpy as np
import matplotlib.pyplot as plt
import random
import time     #检验效率,时间少的效率高

figure = plt.figure()
#用numpy的方法
subplot1 = figure.add_subplot(1,2,1)
class KMeans():
    def __init__(self, k=1):
        '''
        :param k: k代表分类数
        '''
        self.__k = k
        self.__data = None         # 存放原始数据
        self.__pointCenter = None  # 存放中心点,第一次获得的中心点通过随机方式在__data里随机出来
        self.__result = []       # 存放分类结果
        for i in range(k):
            self.__result.append([])  # [[],[],[],[],[]]
            pass
        pass
    def fit(self, data, threshold, times=50000):
        '''
        进行模型训练
        :param data: 训练数据
        :param threshold: 阈值,退出条件
        :return:
        '''
        self.__data = data
        self.randomCenter()
        # print(self.__pointCenter)
        centerDistance = self.calPointCenterDistance(self.__pointCenter, self.__data)

        # 对原始数据进行分类,将每个点分到离它最近的中心点
        i = 0
        for temp in centerDistance:
            index = np.argmin(temp)
            self.__result[index].append(self.__data[i])
            i += 1
            pass
        # 打印分类结果
        # print(self.__result)
        oldCenterPoint = self.__pointCenter
        newCenterPoint = self.calNewPointCenter(self.__result)
        while np.sum(np.sum((oldCenterPoint -  newCenterPoint)**2, axis=1)**0.5)/self.__k > threshold:
            times -= 1
            result = []
            for i in range(self.__k):
                result.append([])
                pass
            # 保存上次的中心点
            oldCenterPoint = newCenterPoint
            centerDistance = self.calPointCenterDistance(newCenterPoint, self.__data)
            # 对原始数据进行分类,将每个点分到离它最近的中心点
            i = 0
            for temp in centerDistance:
                index = np.argmin(temp)
                result[index].append(self.__data[i]) # result = [[[10,20]]]
                i += 1
                pass
            newCenterPoint = self.calNewPointCenter(result)
            self.__result = result
            pass
        self.__pointCenter = newCenterPoint
        return newCenterPoint, self.__result
        pass
    def calPointCenterDistance(self, center, data):
        '''
        计算每个点和每个中心点之间的距离
        :return:
        '''
        centerDistance = []
        flag = False
        for temp in data:
            centerDistance.append([np.sum((center - temp) ** 2, axis=1) ** 0.5])
            pass
        # print(centerDistance)
        return np.array(centerDistance)
        pass
    def calNewPointCenter(self, result):
        '''
        计算新的中心点
        :param result:
        :return:
        '''
        newCenterPoint = None
        flag = False
        for temp in result:
            # 转置
            temps = np.array(temp)
            point = np.mean(temps, axis=0)
            if not flag:
                newCenterPoint = np.array([point])
                flag = True
                pass
            else:
                newCenterPoint = np.vstack((newCenterPoint, point))
            pass
        # print(newCenterPoint)
        return newCenterPoint
        pass
    def randomCenter(self):
        '''
        从原始的__data里随机出最开始进行计算的k个中心点
        :return:
        '''
        if not self.__pointCenter:
            index = random.randint(0, len(self.__data) - 1)
            self.__pointCenter = np.array([self.__data[index]])
            pass
        while len(self.__pointCenter) < self.__k:
            # 随机一个索引
            index = random.randint(0, len(self.__data) - 1)
            # 判断中心点是否重复,如果不重复,加入中心点列表
            if self.__data[index] not in self.__pointCenter:
                self.__pointCenter = np.vstack((self.__pointCenter, self.__data[index]))
                pass
            pass
        pass
    pass
if __name__ == "__main__":
    # 原始数据改为nunmpy结构
    data = np.random.randint(0, 100, 20000).reshape(10000, 2)
    # print(data)
    startTime = time.time()
    kmeans = KMeans(k=5)
    centerPoint, result = kmeans.fit(data, 0.0001)
    print('numpy方法的kmeans算法的时间',time.time() - startTime)
    # print(centerPoint)
    i = 0
    tempx = []
    tempy = []
    color = []
    for temp in result:
        temps = [[temp[x][i] for x in range(len(temp))] for i in range(len(temp[0]))]
        color += [i] * len(temps[0])
        tempx += temps[0]
        tempy += temps[1]
        i += 2
        pass
    subplot1.scatter(tempx, tempy, c=color, s=30)



#不用numpy的方法
subplot2 = figure.add_subplot(1,2,2)
class KMeans():
    def __init__(self, k=1):    #k代表分类数,默认为1,可以改变
        self.__k = k
        self.__data = []       # 存放原始数据
        self.__pointCenter = []  # 存放中心点,第一次获得的中心点通过随机方式在__data里随机出来
        self.__result = []      #类里的数据
        for i in range(k):
            self.__result.append([])  # [[],[],[],[],[]]    #有几个类添加几个列表
            pass
        pass
    #进行模型训练
    def fit(self, data, threshold, times=500):    #data: 训练数据  threshold: 阈值,退出条件
        self.__data = data
        self.randomCenter()     #调用此方法随机出最开始的中心点
        # print(self.__pointCenter)    #输出中心点列表,无实意
        centerDistance = self.calPointCenterDistance(self.__pointCenter, self.__data)     #计算每个点和每个中心点的距离

        # 对原始数据进行分类,将每个点分到离它最近的中心点
        i = 0     #从第一个点开始
        for temp in centerDistance:
            index = temp.index(min(temp))
            self.__result[index].append(self.__data[i])
            i += 1
            pass
        # print(self.__result)    # 打印分类结果
        oldCenterPoint = self.__pointCenter     #将中心点赋值给它
        newCenterPoint = self.calNewPointCenter(self.__result)     #调用此方发,计算新的中心点
        #判断:如果前后两次中心点之间的距离是否小于某个阈值
        while self.calCenterToCenterDistance(oldCenterPoint, newCenterPoint) > threshold:    #如果大于,重复while语句的操作,次数在times次内
            times -= 1
            result = []
            for i in range(self.__k):
                result.append([])
                pass
            oldCenterPoint = newCenterPoint    # 保存上次的中心点
            centerDistance = self.calPointCenterDistance(newCenterPoint, self.__data)    #计算新中心点和个点之间的距离

            # 对原始数据进行分类,将每个点分到离它最近的中心点
            i = 0
            for temp in centerDistance:
                index = temp.index(min(temp))
                result[index].append(self.__data[i]) # result = [[[10,20]]]
                i += 1
                pass

            newCenterPoint = self.calNewPointCenter(result)
            # print(self.calCenterToCenterDistance(oldCenterPoint, newCenterPoint))
            self.__result = result
            pass
        self.__pointCenter = newCenterPoint
        return newCenterPoint, self.__result     #将新中心点和所有类中的点返回
        pass
    #计算两次中心点之间的距离,求和求均值
    def calCenterToCenterDistance(self, old, new):   #old:上次的中心点  new:新计算的中心点
        total = 0
        for point1, point2 in zip (old, new):
            total += self.distance(point1, point2)      #有几个中心点求几次
            pass
        return total / len(old)      #返回前后两次中心点距离之和的平均值
        pass
    #计算每个点和每个中心点之间的距离
    def calPointCenterDistance(self, center, data):
        centerDistance = []
        for temp in data:
            centerDistance.append([self.distance(temp, point) for point in center])     #调用distance方法求各点到各中心点的距离
            pass
        # print(centerDistance)
        return centerDistance     #返回距离
        pass
    #计算新的中心点
    def calNewPointCenter(self, result):
        newCenterPoint = []
        for temp in result:
            # 转置:将每个点与所有中心点的距离列表   转置成    每个中心点与同一类的所有点的列表
            temps = [[temp[x][i] for x in range(len(temp)) ] for i in range(len(temp[0]))]
            point = []
            for t in temps:
                # 对每个维度(类)求和,取平均即为新的中心点
                point.append(sum(t)/len(t)) # mean
                pass
            newCenterPoint.append(point)
            pass
        # print(newCenterPoint)
        return newCenterPoint     #返回新中心点的列表
        pass
    #计算两个点之间的距离,支持任意维度,欧式距离
    def distance(self, pointer1, pointer2):
        distance = (sum([(x1 - x2)**2 for x1, x2 in zip(pointer1, pointer2)]))**0.5     #公式,几个未知数和集合都可以,支持任意维度,这里是二维的
        return distance     #返回距离
        pass
    #从原始的__data里随机出最开始进行计算的k个中心点
    def randomCenter(self):
        while len(self.__pointCenter) < self.__k:
            # 随机一个索引   所有点中随机取几个中心点
            index = random.randint(0, len(self.__data) - 1)
            # 判断中心点是否重复,如果不重复,加入中心点列表    避免因重复导致的中心点个数不够
            if self.__data[index] not in self.__pointCenter:
                self.__pointCenter.append(self.__data[index])
                pass
            pass
        pass
    pass
if __name__ == "__main__":
    data = [[random.randint(1, 100), random.randint(1, 100)] for i in range(10000)]      #随机10000个点
    start = time.time()
    kmeans = KMeans(k=5)         #几个中心点(几个类)
    centerPoint, result = kmeans.fit(data, 0.0001)     #调用fit方法,参数中的0.0001为阈值
    print('普通方法的kmeans算法的时间',time.time()-start)
    # print(centerPoint)
    #界面可视化,可以清晰的看到散点图,需引入 matplotlib模块
    i = 0
    tempx = []
    tempy = []
    color = []
    for temp in result:      #遍历类中的点,用同一种颜色,需转置
        temps = [[temp[x][i] for x in range(len(temp))] for i in range(len(temp[0]))]
        color += [i] * len(temps[0])
        tempx += temps[0]
        tempy += temps[1]
        i += 2
        pass
    subplot2.scatter(tempx, tempy, c=color, s=30)    #x轴,y轴,点的颜色,点的大小


plt.show()

运行结果:

按照道理说使用numpy的Kmeans算法效率更高,但是发现在运行时,numpy的Kmeans中途可能需要做数据类型转换,这就耽误了很多时间,在把不必要的print语句注释掉效率还是蛮快的 。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值