
本文介绍如何利用 scikit-learn 的 NearestNeighbors 找出每个样本的 K 个最近邻,并高效计算其指定维度(如 z 坐标)的平均值,支持任意 K 值扩展,代码简洁、可读性强且具备工程实用性。
本文介绍如何利用 scikit-learn 的 `nearestneighbors` 找出每个样本的 k 个最近邻,并高效计算其指定维度(如 z 坐标)的平均值,支持任意 k 值扩展,代码简洁、可读性强且具备工程实用性。
在空间数据分析、回归插值或局部平滑等任务中,常需对每个数据点,基于其 K 个最近邻(KNN)计算某项特征(如高度、温度、响应值)的局部平均。本教程以三维坐标数组 X = [[x, y, z], …] 为例,演示如何:① 构建 KNN 模型并获取邻居索引;② 提取邻居对应的 z 值;③ 向量化计算每个点的邻居 z 均值。
以下为完整实现(含注释与最佳实践):
import numpy as np from sklearn.neighbors import NearestNeighbors from statistics import mean # 更语义化,且自动处理空列表(但 K≥1 时无需担心)# 示例数据:每行 [x, y, z] X = np.array([[6, -3, 0.1], [-5, -9, 0.5], [3, -7, 0.8], [-10, 6, 0.5], [-4, -16, 0.9], [1, -0.5, 0.0] ]) # Step 1: 构建 KNN 模型(默认使用欧氏距离,仅基于 x,y,z 全维度计算邻近性)# 若仅需基于 x,y 定位邻居(忽略 z),则传入 X[:, :2] k = 2 nbrs = NearestNeighbors(n_neighbors=k + 1).fit(X) # 注意:+1 是因 fit 数据包含自身,kneighbors 默认含自身 distances, indices = nbrs.kneighbors(X) # Step 2: 提取每个点所有邻居(不含自身)的 z 值 # indices[i] 包含第 i 个点的 k+1 个索引(含自身),故跳过首项(即自身索引)neighbor_z_values = [[X[idx][2] for idx in row[1:]] # row[1:] 排除自身,只取真正邻居 for row in indices ] # Step 3: 计算每个点的邻居 z 均值(推荐使用 statistics.mean,更鲁棒)mean_z_per_point = [mean(z_list) for z_list in neighbor_z_values] print(" 各点邻居的 z 均值:", np.round(mean_z_per_point, 3)) # 输出示例:[0.65 , 0.65 , 0.5 , 0.7 , 0.45 , 0.45]
✅ 关键说明与注意事项:
- 自包含性处理 :NearestNeighbors.kneighbors() 默认将查询点自身视为最近邻(距离为 0)。因此若需严格 K 个 其他 邻居,应设 n_neighbors=K+1 并切片 row[1:],如上所示。
- 维度灵活性:只需修改 X[idx][2] 中的索引(如 [1] 取 y 值、[-1] 取最后一维),即可适配任意目标特征列。
- 性能优化:对大规模数据,避免嵌套列表推导式;可改用 NumPy 索引向量化(见进阶版):
# 向量化版本(更高效)indices_flat = indices[:, 1:].flatten() # 展平所有邻居索引(不含自身)all_neighbor_z = X[indices_flat, 2].reshape(-1, k) # 形状 (n_samples, k) mean_z_vectorized = np.mean(all_neighbor_z, axis=1) - 距离度量选择:如需基于 x,y 平面距离(忽略 z),请传入 X[:, :2] 训练模型,再用原始 X 提取 z 值,确保“定位”与“取值”逻辑解耦。
- 边界情况:当 k=0 或数据量不足时,NearestNeighbors 会报错,建议在生产环境中添加 try-except 或预校验。
掌握此方法后,你可轻松将其集成至 KNN 回归、局部加权平均(LWMA)、异常值检测或地理空间插值等 pipeline 中——核心思想始终是:先定位邻居,再聚合目标特征。