经典聚类算法之K-means算法

作者简介:本人擅长运筹优化建模及算法设计,包括各类车辆路径问题、生产车间调度、二三维装箱问题,熟悉CPLEX和gurobi求解器
微信公众号:运筹优化与学习

若有运筹优化建模及算法定制需求,欢迎联系我们私聊沟通

本篇推文首先介绍K-means聚类算法以及Python代码实现,开启这个系列。

算法描述

K-means算法是一种广泛使用的聚类算法,它的目标是将若干个数据点分组到K个簇中,以使簇内的点尽可能相似,而簇间的点尽可能不同。它的核心思想是通过迭代优化簇中心的位置,以最小化簇内的平方误差总和。其中,最小化簇内的平方误差总和表达式如下:

J = ∑ i = 1 k ∑ x ∈ S i ∥ x − c i ∥ 2 J = \sum_{i=1}^{k}\sum_{x\in S_{i}}\Vert x-c_{i}\Vert^2 J=i=1kxSixci2

式中, c i c_i ci是簇 S i S_{i} Si的中心, x x x是簇 S i S_{i} Si内的点, ∥ x − c i ∥ 2 \Vert x-c_{i}\Vert^2 xci2表示点到簇中心 c i c_i ci的距离(即相似度)。

K-means算法实现简单,适用于需要快速、初步的数据分类的场景。但是,算法同样存在一定局限性,比如对初始簇中心的选择敏感,同时需要自行定义参数k的取值。

算法步骤

  • 初始化:随机选择 K 个数据点作为初始簇中心。
  • 分配:将每个数据点分配给最近的簇中心。
  • 更新:重新计算每个簇的中心(即簇内所有点的均值)。
  • 迭代:重复步骤 2 和 3 直到簇中心不再发生变化或达到预设的迭代次数。

算法代码展示

测试数据介绍

已知若干外卖订单信息,包括商家位置信息、顾客位置信息等,需要根据订单的有关信息内容,对订单进行聚类,随后根据聚类结果分配相应的外卖骑手。其中每个订单视为一个数据点,不同订单之间的距离定义为商家距离与顾客距离的之和。

算法主框架

def Kmeans(self, plot_flag = False):
        # Run k-means algorithm
        if len(self.geo_locations) < self.k:
            return -1   # Error
        points_ = self.geo_locations.copy()
        # Compute the initial means
        self.initial_means(points_)
        stop = False
        iterations = 1
        print("****** Starting K-Means ******")
        while not stop:
            # Assignment step: assign each node to the cluster with the closest mean
            points_ = self.geo_locations.copy()
            clusters = self.assign_points(points_)
            means = self.compute_means(clusters)
            stop = self.update_means(means, 0.01)
            if not stop:
                self.means = means
            iterations += 1
        print(f"K-Means completed in {iterations} iterations.")
        self.clusters = clusters
        # Plot cluster for evaluation
        if plot_flag:
            fig = plt.figure()
            ax = fig.add_subplot(111)
            markers = ['o', 'd', 'x', 'h', 'H', 7, 4, 5, 6, '8', 'p', ',', '+', '.', 's', '*', 3, 0, 1, 2]
            colors = ['r', 'k', 'b', [0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
            for cluster_id, cluster in enumerate(clusters.values()):
                bus_x = []  
                bus_y = []
                cust_x = []
                cust_y = []
                for point in cluster:
                    bus_x.append(point.bus_x)
                    bus_y.append(point.bus_y)
                    cust_x.append(point.cust_x)
                    cust_y.append(point.cust_y)
                ax.scatter(bus_x, bus_y, s=20, c=colors[cluster_id % len(colors)], marker=markers[cluster_id % len(markers)])
                ax.scatter(cust_x, cust_y, s=20, c=colors[cluster_id % len(colors)], marker=markers[cluster_id % len(markers)])
            plt.show()
        self.save()
        return self.clusters

分配:将每个数据点分配给最近的簇中心

def assign_points(self, points):
        # 将每个数据点分配给最近的簇中心
        clusters = {}
        for point in points:
            dist = []
            # 找到当前节点最近的簇中心
            for mean in self.means:
                dist.append(0.5 * math.sqrt((point.bus_x - mean.bus_x) ** 2 + (point.bus_y - mean.bus_y) ** 2) 
                    + 0.5 * math.sqrt((point.cust_x - mean.cust_x) ** 2 + (point.cust_y - mean.cust_y) ** 2))
            index = dist.index(min(dist))
            clusters.setdefault(index, []).append(point)
        return clusters

更新:重新计算每个簇的中心(即簇内所有点的均值)

def compute_means(self, clusters):
        means = []
        for cluster in clusters.values():
            mean_point = Order.Order(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
            cnt = 0.0
            for point in cluster:
                mean_point.bus_x += point.bus_x
                mean_point.bus_y += point.bus_y
                mean_point.cust_x += point.cust_x
                mean_point.cust_y += point.cust_y
                cnt += 1.0
            mean_point.bus_x /= cnt
            mean_point.bus_y /= cnt
            mean_point.cust_x /= cnt
            mean_point.cust_y /= cnt
            means.append(mean_point)
        return means

结果展示

输出每个订单属于哪个簇

  • 22
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

eternal1995

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值