Numpy std计算结果异常:聚类标准差大于半径问题咨询
问题:聚类标准差计算异常,出现标准差大于半径的矛盾
我编写了一段生成3个聚类(blobs)并绘制其半径与标准差的脚本。对于中心位于(20,1)的第三个聚类,得到的标准差居然大于其半径——这在数学上是不可能的(即使使用有偏未校正估计器)。从图中可见第三个聚类的问题,虚线代表标准差。我尝试移动第三个聚类,发现当聚类中心远离y=x平分线时,该问题更为明显。
原脚本代码
from calendar import c from operator import le from statistics import variance from matplotlib import axis import numpy as np import matplotlib.pyplot as plt from rich import print import matplotlib as mpl from sklearn.cluster import KMeans from sklearn.datasets import make_blobs import src src.vis.set_matplotlib_params() # generate three blobs centers=[(1,1), (11,13), (20,1)] X, y = make_blobs(n_samples=600, n_features=2, random_state=3, cluster_std=[1,3,1], centers=centers) # plot the blobs colorlist = ["#ebac23", "#b80058", "#008cf9", "#006e00", "#00bbad", "#d163e6", "#b24502", "#ff9287", "#5954d6", "#00c6f8", "#878500", "#00a76c", "#bdbdbd"] cmap=mpl.colors.ListedColormap(colorlist[0:len(np.unique(y))]) fig, ax = plt.subplots() ax.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap, marker='.', s=10) # plot the centers for center in centers: ax.scatter(center[0], center[1], c='black', marker='x', s=100) # compute the radiuses radiuses = dict() stds = dict() for center,y_int in zip(centers, np.arange(len(centers))): # for each cluster current_pts = X[y==y_int, :] N = current_pts.shape[0] print(f"Y_int={y_int}, \t current data = {current_pts}") dists = np.linalg.norm(current_pts-center, axis=1) radiuses[y_int] = np.max(dists) stds[y_int] = np.std(current_pts) print(radiuses) print(stds) # plot the radiuses for center,y_int in zip(centers, np.unique(y)): ax.add_patch(plt.Circle(center, radiuses[y_int], fill=False, color=cmap(y_int), linestyle='-', linewidth=.5)) ax.add_patch(plt.Circle(center, stds[y_int], fill=False, color=cmap(y_int), linestyle='--', linewidth=.5)) ax.set_aspect('equal') plt.show()
问题原因
核心错误在于标准差的计算逻辑:
- 代码中
np.std(current_pts)未指定轴参数,会将二维的聚类点数组扁平化,计算所有x、y坐标值的全局标准差,而非点到聚类中心距离的标准差。 - 以第三个聚类为例,中心(20,1)的x坐标均值为20、标准差1,y坐标均值为1、标准差1,但扁平化后的数据包含大量19-21的x值和0-2的y值,全局标准差会被拉高,甚至超过聚类的最大半径(点到中心的最远距离),这就造成了矛盾。
解决方案
要让标准差和半径的统计意义匹配,应该计算点到聚类中心距离的标准差,直接基于已经算出的dists数组计算即可:
将原代码中:
stds[y_int] = np.std(current_pts)
替换为:
stds[y_int] = np.std(dists)
效果验证
修正后,每个聚类的标准差会反映点到中心距离的离散程度,必然小于该聚类的最大半径,符合数学逻辑。即使聚类中心远离y=x平分线,也不会再出现标准差大于半径的异常情况。
内容的提问来源于stack exchange,提问作者arielpriarone
相关产品推荐
相关产品推荐

