You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重实现Kmeans算法时Centroids返回None引发AttributeError求助

重实现K-Means时centroids返回None的问题排查与解决

问题现象

重写K-Means算法过程中,更新质心的new_centroids函数返回None,导致主循环执行一次后,下一轮循环触发AttributeError: 'NoneType' object has no attribute 'equals'错误,报错信息如下:

Iteration 1: Centroids None
Centroids is None
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[24], line 9
      6 old_centroids = pd.DataFrame()
      7 iteration = 1
----> 9 while iteration < max_iteration and not centroids.equals(old_centroids):
     10     old_centroids = centroids
     11     labels = get_labels(data, centroids)

AttributeError: 'NoneType' object has no attribute 'equals'

相关代码

绘制聚类函数

def plot_clusters(data, labels, centroids, iteration):
    pca = PCA(n_components=2)
    data_2d = pca.fit_transform(data)
    centroids_2d = pca.transform(centroids)
    clear_output(wait=True)
    plt.title(f'Iteration{iteration}')
    plt.scatter(x=data_2d[:,0], y=data_2d[:,1], c=labels)
    plt.scatter(x=centroids_2d[:,0], y=centroids_2d[:,1])
    plt.show()

主循环代码

max_iteration = 100
k = 7

centroids = random_centroids(data, k)
#print(f'Centroids formed {centroids}')
old_centroids = pd.DataFrame()
iteration = 1

while iteration < max_iteration and not centroids.equals(old_centroids):
    old_centroids = centroids
    labels = get_labels(data, centroids)
    centroids = new_centroids(data, labels, k)
    print(f'Iteration {iteration}: Centroids {centroids}')
    if centroids is not None:
        plot_clusters(data, labels, centroids, iteration)
    else:
        print("Centroids is None")
    iteration +=1

已验证正常的前置函数

def random_centroids(data, k):
    centroids = []
    for i in range(k):
        centroid = data.apply(lambda x: float(x.sample())) #sample func picks a random number from each feature
        centroids.append(centroid)
    return pd.concat(centroids, axis=1)
def get_labels(data, centroids):
    distances = centroids.apply(lambda x: np.sqrt(((data-x)**2).sum(axis=1)))
                                #Geometric distances formulae
    return distances.idxmin(axis=1)

质心更新函数(问题所在)

#Obtain Geometric mean of each features(cluster centroids) 
def new_centroids(data, labels,k):     
    data.groupby(labels).apply(lambda x: np.exp(np.log(x).mean())).T

错误原因分析

  1. 核心原因:new_centroids无返回值
    函数内部执行了分组计算几何均值的逻辑,但没有用return语句返回计算结果,Python中函数默认返回None,这直接导致centroids = new_centroids(...)赋值后变成None。

  2. 循环条件存在风险
    当centroids变为None后,循环条件中的centroids.equals(old_centroids)会直接触发AttributeError,因为None对象没有equals方法,且未先做非空判断。

解决办法

1. 修复new_centroids函数,添加返回语句

修改后的函数:

#Obtain Geometric mean of each features(cluster centroids) 
def new_centroids(data, labels,k):     
    return data.groupby(labels).apply(lambda x: np.exp(np.log(x).mean())).T

注意:如果数据中存在0或负数,np.log(x)会产生NaN或无限值,导致几何均值计算失效,建议提前对数据做预处理(如归一化到正数范围),或者改用K-Means标准实现的算术均值:

def new_centroids(data, labels,k):     
    return data.groupby(labels).mean().T

2. 优化循环条件,增加非空判断

修改主循环的while条件,先确保centroids不为None,再进行相等性比较:

while iteration < max_iteration and centroids is not None and not centroids.equals(old_centroids):

3. 可选:增加异常处理(防止分组计算出错)

可以在new_centroids中添加异常捕获,避免因数据问题导致返回None:

def new_centroids(data, labels,k):
    try:
        return data.groupby(labels).apply(lambda x: np.exp(np.log(x).mean())).T
    except Exception as e:
        print(f"计算质心出错: {e}")
        return centroids  # 返回旧质心,避免循环中断

修正后主循环运行逻辑

修复后,new_centroids会正常返回更新后的质心,循环条件先判断centroids非空,再比较新旧质心是否相等,不会再触发AttributeError,K-Means迭代可以正常进行。

内容的提问来源于stack exchange,提问作者KIZ-MAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 01:16:09