重实现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
错误原因分析
核心原因:
new_centroids无返回值
函数内部执行了分组计算几何均值的逻辑,但没有用return语句返回计算结果,Python中函数默认返回None,这直接导致centroids = new_centroids(...)赋值后变成None。循环条件存在风险
当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
相关产品推荐
相关产品推荐

