You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在Python中基于单一Z坐标值进行数据聚类,并优化聚类数量、评估聚类效果及扩展至3D聚类?

解决Z坐标聚类及扩展3D聚类的完整方案

Hey Jules, let's break down how to tackle this problem step by step—from prepping your data to evaluating clusters and scaling up to 3D.

1. 数据预处理:提取目标特征

首先你需要把JSON里的Z坐标(以及后续3D用的lat/lng)提取出来,转换成sklearn能处理的格式。这里用pandas会很方便:

import json
import pandas as pd

# 读取本地JSON文件(先把OneDrive的文件下载到本地)
with open('your_markers.json', 'r') as f:
    marker_data = json.load(f)

# 转换成DataFrame,方便后续操作
df = pd.DataFrame(marker_data)

# 提取Z坐标(一维聚类用);如果是3D聚类,就提取lat、lng、z三列
z_coords = df[['z']].values  # sklearn要求输入是2D数组,所以用双括号保持维度

2. 选择合适的聚类算法

对于你的需求(固定簇数、簇内样本距离相近),K-Means是最适合的选择——它简单高效,核心目标就是最小化簇内样本的平方距离和,完全匹配你“簇内对象彼此距离相近”的要求。

如果你后续想探索基于密度的聚类(比如自动识别异常值),可以试试DBSCAN,但K-Means更适合你当前“5-10个簇”的明确需求。

3. 确定最优簇数(5-10之间)

要找到最优的簇数,推荐两种方法结合使用:

方法1:肘部法则(Elbow Method)

观察簇内平方和(inertia)随簇数增加的变化曲线,曲线的“拐点”就是最优簇数——拐点之后inertia下降速度明显放缓,说明再增加簇数对聚类效果提升不大。

from sklearn.cluster import KMeans
import matplotlib.pyplot as plt

# 设定簇数范围(你要的5到10)
cluster_counts = range(5, 11)
inertia_values = []

for n in cluster_counts:
    kmeans = KMeans(n_clusters=n, random_state=42)  # random_state保证结果可复现
    kmeans.fit(z_coords)
    inertia_values.append(kmeans.inertia_)

# 绘制肘部曲线
plt.figure(figsize=(8, 4))
plt.plot(cluster_counts, inertia_values, 'bo-', linewidth=2)
plt.xlabel('Number of Clusters')
plt.ylabel('Inertia (Within-Cluster Sum of Squares)')
plt.title('Elbow Method for Optimal Cluster Count')
plt.grid(True)
plt.show()

方法2:轮廓系数(Silhouette Score)

轮廓系数衡量的是样本与自身簇内样本的相似度,以及与其他簇样本的差异度,取值范围是[-1,1],越接近1说明聚类效果越好。我们可以计算不同簇数下的平均轮廓系数,选最高的那个:

from sklearn.metrics import silhouette_score

sil_scores = []
for n in cluster_counts:
    kmeans = KMeans(n_clusters=n, random_state=42)
    cluster_labels = kmeans.fit_predict(z_coords)
    sil_score = silhouette_score(z_coords, cluster_labels)
    sil_scores.append(sil_score)

# 绘制轮廓系数曲线
plt.figure(figsize=(8, 4))
plt.plot(cluster_counts, sil_scores, 'ro-', linewidth=2)
plt.xlabel('Number of Clusters')
plt.ylabel('Average Silhouette Score')
plt.title('Silhouette Score for Optimal Cluster Count')
plt.grid(True)
plt.show()

4. 评估聚类效果

除了上面两种方法,还有两个常用指标可以辅助判断:

  • Calinski-Harabasz指数:值越高说明簇内越紧密、簇间越分散,聚类效果越好
  • Davies-Bouldin指数:值越低说明聚类效果越好

示例代码:

from sklearn.metrics import calinski_harabasz_score, davies_bouldin_score

# 假设你选了最优簇数n=7
optimal_n = 7
kmeans_opt = KMeans(n_clusters=optimal_n, random_state=42)
labels_opt = kmeans_opt.fit_predict(z_coords)

print(f"Calinski-Harabasz Score: {calinski_harabasz_score(z_coords, labels_opt):.2f}")
print(f"Davies-Bouldin Score: {davies_bouldin_score(z_coords, labels_opt):.2f}")

5. 2D地图上可视化聚类结果

把聚类标签和原数据结合,就可以在2D地图上用颜色、标记或文字标识区分不同Z层级的簇:

# 给原数据添加聚类标签
df['z_cluster'] = labels_opt

# 绘制2D地图标记,用颜色区分簇
plt.figure(figsize=(10, 8))
scatter = plt.scatter(df['lng'], df['lat'], c=df['z_cluster'], cmap='viridis', alpha=0.7)
plt.xlabel('Longitude (X)')
plt.ylabel('Latitude (Y)')
plt.title('2D Map Markers Colored by Z-Level Cluster')
plt.colorbar(scatter, label='Cluster ID')

# 可选:在每个标记旁添加簇编号(适合数据量不大的情况)
for idx, row in df.iterrows():
    plt.annotate(str(row['z_cluster']), (row['lng'], row['lat']), fontsize=8, ha='center')

plt.show()

6. 扩展到3D聚类

如果要基于lat(Y)、lng(X)、z(Z)三个维度聚类,只需要修改特征输入即可,流程和一维聚类完全一致:

步骤1:标准化特征(重要!)

因为lat/lng和Z的数值尺度可能差异很大(比如lat是几十,Z是几百),K-Means对尺度敏感,所以先标准化:

from sklearn.preprocessing import StandardScaler

# 提取3D特征
xyz_coords = df[['lng', 'lat', 'z']].values

# 标准化特征
scaler = StandardScaler()
xyz_scaled = scaler.fit_transform(xyz_coords)

步骤2:3D聚类与可视化

# 用最优簇数进行3D聚类
kmeans_3d = KMeans(n_clusters=optimal_n, random_state=42)
df['3d_cluster'] = kmeans_3d.fit_predict(xyz_scaled)

# 绘制3D散点图
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
scatter_3d = ax.scatter(df['lng'], df['lat'], df['z'], c=df['3d_cluster'], cmap='viridis', alpha=0.7)
ax.set_xlabel('Longitude (X)')
ax.set_ylabel('Latitude (Y)')
ax.set_zlabel('Altitude (Z)')
ax.set_title('3D Clustering of Markers')
plt.colorbar(scatter_3d, label='3D Cluster ID')
plt.show()

小提示

  • 如果你的Z值分布有明显的分层(比如多个离散的高度层),K-Means会自动把这些层分成簇,效果会很好
  • 要是数据里有异常值,可以先做简单的清洗(比如移除Z值超出合理范围的样本),避免影响聚类结果

内容的提问来源于stack exchange,提问作者Jules

火山引擎 最新活动