经纬度聚类:聚类算法比较

需求:

将经纬度数据,根据经纬度进行聚类

初始数据

data.csv

K均值聚类

简介

K均值(K-means)聚类是一种常用的无监督学习算法,用于将数据集中的样本分成K个不同的簇(cluster)。其基本思想是将数据集划分为K个簇,使得每个样本点都属于距离最近的簇的中心点,同时最小化簇内样本点之间的距离平方和。

K均值聚类算法的步骤如下:

  1. 初始化: 随机选择K个样本点作为初始的簇中心点。
  2. 分配: 对于每个样本点,计算其与K个簇中心点的距离,并将其分配到距离最近的簇中心点所在的簇。
  3. 更新: 对于每个簇,计算其所有样本点的均值,将该均值作为新的簇中心点。
  4. 重复迭代: 重复步骤2和步骤3,直到簇中心点不再发生变化或达到预设的迭代次数。
优点

简单易实现、计算速度快

缺点

对初始聚类中心点敏感、对异常值敏感,在实际应用中,通常需要进行多次运行并选择最优的聚类结果。

使用场景

适用于数据集没有明确标签信息、簇形状相对简单、簇大小相近的情况下,常用于图像压缩、文本聚类、客户分群等领域。

  1. 簇个数已知:K均值聚类需要预先指定簇的个数,因此适用于已知簇个数的情况。
  2. 数据集各个簇的形状相似:K均值聚类假设每个簇是凸形的,且具有相似的大小和密度,因此适用于各个簇形状相似的数据集。
  3. 对速度要求高:K均值聚类是一种简单且高效的算法,适用于大规模数据集和对速度要求高的场景。
  4. 聚类结果可解释性强:K均值聚类产生的簇中心代表了每个簇的平均特征,因此聚类结果具有较强的可解释性,适用于需要直观理解聚类结果的场景。
  5. 数据特征空间是欧几里得空间:K均值聚类使用欧几里得距离来度量数据点之间的相似度,因此适用于特征空间是欧几里得空间的情况。
  6. 初始中心点的选择相对灵活:虽然K均值聚类对初始中心点的选择敏感,但是可以采用多次随机初始化来减少此影响,因此对于初始中心点的选择相对灵活。
java代码实现
public class KMeansClustering {

    // 加载数据
    public static List<Point> loadData(String filename) {
        List<Point> points = new ArrayList<>();
        try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
            String line;
            while ((line = br.readLine()) != null) {
                String[] parts = line.split(",");
                double latitude = Double.parseDouble(parts[0]);
                double longitude = Double.parseDouble(parts[1]);
                points.add(new Point(latitude, longitude));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return points;
    }

    private int k; // 聚类数量
    private List<Point> centroids; // 聚类中心点

    public KMeansClustering(int k) {
        this.k = k;
        this.centroids = new ArrayList<>();
    }

    // 初始化聚类中心点
    private void initCentroids(List<Point> points) {
        Random random = new Random();
        for (int i = 0; i < k; i++) {
            Point centroid = points.get(random.nextInt(points.size()));
            centroids.add(centroid);
        }
    }

    // 计算两点之间的欧氏距离
    private double calculateDistance(Point a, Point b) {
        return Math.sqrt(Math.pow(a.getX() - b.getX(), 2) + Math.pow(a.getY() - b.getY(), 2));
    }

    /**
     * 执行K均值聚类算法。
     *
     * @param points 含有点的列表,每个点包含x和y坐标。
     * @param maxIterations 最大迭代次数。
     * @return 返回包含点的聚类列表,每个聚类是一个包含点的列表。
     */
    public List<List<Point>> kMeans(List<Point> points, int maxIterations) {
        // 初始化聚类中心点
        initCentroids(points);

        List<List<Point>> clusters = new ArrayList<>();
        for (int i = 0; i < maxIterations; i++) {
            clusters.clear();

            // 初始化簇
            for (int j = 0; j < k; j++) {
                clusters.add(new ArrayList<>());
            }

            // 分配点到最近的簇
            for (Point point : points) {
                double minDistance = Double.MAX_VALUE;
                int closestCentroidIndex = -1;
                // 计算点到各个聚类中心的距离,分配点到最近的聚类中心
                for (int j = 0; j < k; j++) {
                    double distance = calculateDistance(point, centroids.get(j));
                    if (distance < minDistance) {
                        minDistance = distance;
                        closestCentroidIndex = j;
                    }
                }
                clusters.get(closestCentroidIndex).add(point);
            }

            // 更新聚类中心点
            for (int j = 0; j < k; j++) {
                List<Point> cluster = clusters.get(j);
                double sumX = 0, sumY = 0;
                // 计算聚类中心
                for (Point point : cluster) {
                    sumX += point.getX();
                    sumY += point.getY();
                }
                centroids.set(j, new Point(sumX / cluster.size(), sumY / cluster.size()));
            }
        }

        return clusters;
    }


    public static void main(String[] args) {
        // 读取数据文件
        String inputFile = "data.csv";
        List<Point> points = loadData(inputFile);

        long l = System.currentTimeMillis();

        KMeansClustering kMeans = new KMeansClustering(4); // 聚类数量为4
        List<List<Point>> clusters = kMeans.kMeans(points, 1000); // 最大迭代次数为10

        // 输出聚类结果
        for (int i = 0; i < clusters.size(); i++) {
            System.out.println("聚类 " + (i + 1) + ":");
            for (Point point : clusters.get(i)) {
                System.out.println("(" + point.getX() + ", " + point.getY() + ")");
            }
            System.out.println();
        }
        System.out.println("耗时:" + (System.currentTimeMillis() - l) + "ms");
    }
}

class Point {
    private double x;
    private double y;

    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public double getX() {
        return x;
    }

    public double getY() {
        return y;
    }
}
输出结果
第一次
聚类 1:
(12.9042, 12.4074)
(32.1291, 32.2644)
(21.2304, 21.4737)
(15.1291, 16.2644)
(23.1281, 56.2644)
(23.5728, 43.0668)
(21.3416, 32.9398)
(33.1291, 33.1291)
(12.2304, 43.4737)
(34.5728, 43.0668)
(34.3416, 34.9398)
(23.1291, 65.2644)
(30.5728, 21.0668)
(34.3416, 21.9398)
(21.9042, 32.4074)
(23.2304, 43.4737)
(23.1291, 34.2643)

聚类 2:
(30.3416, 108.9398)
(30.3417, 108.9396)
(30.3415, 108.9395)
(30.5728, 104.0668)
(23.1291, 113.2644)
(23.1292, 113.2644)
(23.1292, 113.1232)
(30.121, 104.2121)
(32.3416, 104.2132)
(42.2304, 92.4736)
(33.3416, 89.9398)
(21.9042, 89.4074)
(21.5728, 104.0666)
(12.3416, 76.9398)
(14.9042, 78.4074)
(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值