需求:
将经纬度数据,根据经纬度进行聚类
初始数据
K均值聚类
简介
K均值(K-means)聚类是一种常用的无监督学习算法,用于将数据集中的样本分成K个不同的簇(cluster)。其基本思想是将数据集划分为K个簇,使得每个样本点都属于距离最近的簇的中心点,同时最小化簇内样本点之间的距离平方和。
K均值聚类算法的步骤如下:
- 初始化: 随机选择K个样本点作为初始的簇中心点。
- 分配: 对于每个样本点,计算其与K个簇中心点的距离,并将其分配到距离最近的簇中心点所在的簇。
- 更新: 对于每个簇,计算其所有样本点的均值,将该均值作为新的簇中心点。
- 重复迭代: 重复步骤2和步骤3,直到簇中心点不再发生变化或达到预设的迭代次数。
优点
简单易实现、计算速度快
缺点
对初始聚类中心点敏感、对异常值敏感,在实际应用中,通常需要进行多次运行并选择最优的聚类结果。
使用场景
适用于数据集没有明确标签信息、簇形状相对简单、簇大小相近的情况下,常用于图像压缩、文本聚类、客户分群等领域。
- 簇个数已知:K均值聚类需要预先指定簇的个数,因此适用于已知簇个数的情况。
- 数据集各个簇的形状相似:K均值聚类假设每个簇是凸形的,且具有相似的大小和密度,因此适用于各个簇形状相似的数据集。
- 对速度要求高:K均值聚类是一种简单且高效的算法,适用于大规模数据集和对速度要求高的场景。
- 聚类结果可解释性强:K均值聚类产生的簇中心代表了每个簇的平均特征,因此聚类结果具有较强的可解释性,适用于需要直观理解聚类结果的场景。
- 数据特征空间是欧几里得空间:K均值聚类使用欧几里得距离来度量数据点之间的相似度,因此适用于特征空间是欧几里得空间的情况。
- 初始中心点的选择相对灵活:虽然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)
(