#
为了减少迭代次数,我们可以尽量把质心初始化在数据分布的内部
def
randCent(data, k):
#
定义随机选取质心的函数
data_min = data.iloc[:, :].min()
#
返回每一个特征的最小值
data_max = data.iloc[:, :].max()
#
返回每一个特征的最大值
data_cent = np.random.uniform(data_min,data_max,(k, data.shape[1]))
#
在最大值和最小值之间随机生成K个质心,返回k*data.shape[1]形状
return
data_cent
#
将以上打包成函数,计算一个样本到所有质心的距离并且返回最小距离和对应的索引
def
one_sample_datacent_distance(one_sample, data_cent):
one_sample_distance
= np.sum(np.power((one_sample.values - data_cent), 2), axis=1)
#
一个样本到所有质心的距离
distance_min_values = one_sample_distance.min()
#
最小的距离的值
distance_min_index = np.where(one_sample_distance == distance_min_values)
#
距离最短的的索引
return
distance_min_values, distance_min_index
def
kmeans(data,k ):
m, n
= data.shape
#
提取出数据集的行列
data_cent =
randCent(data, k)
add_3
= np.zeros((m,3
))
add_3[:,0]
=
np.inf
add_3[:,
1: 3] = -1
data_add_3
= pd.concat([data, pd.DataFrame(add_3)] , axis=1, ignore_index =
True)
clusterChanged
= True
#
设定一个循环因子,用来控制循环
while
clusterChanged:
clusterChanged
= False
#
先更改循环因子,防止陷入死循环
for
i
in
range(m):
#
对数据集中每一条样本进行循环
dist_min_values, dist_min_index = one_sample_datacent_distance(data_add_3.iloc[i, :n], data_cent)
#
计算当前样本到k个质心的距离
data_add_3.iloc[i, n] = dist_min_values
#
将距离的最小值放到容器的第一列(即第n列)
#
找到最小距离所在位置的索引即为簇的标号,将簇标号放入容器第二列(即第n+1列)
data_add_3.iloc[i, n+1] =
dist_min_index[0]
#
判断最后两列是否相等(簇是否还会变化),不相等则进入下一次循环,相等则终止循环
clusterChanged =
not
(data_add_3.iloc[:, -1] == data_add_3.iloc[:, -2
]).all()
#
重新计算当前的质心
if
clusterChanged:
cent_df
= data_add_3.groupby(n+1).mean()
#
对每个簇进行求均值
data_cent = cent_df.iloc[:,:n].values
#
将均值赋值给质心(确定了新质心)
data_add_3.iloc[:, -1] = data_add_3.iloc[:, -2]
#
当前簇标号赋值给最后一列
return
data_cent, data_add_3
效果如下:
from sklearn.datasets import make_blobs #导入生成数据集的包
#自己创建数据集
X, y = make_blobs(n_samples=500,n_features=2,centers=4,random_state=1)
plt.scatter(X[:, 0], X[:, 1]
,marker='o' #点的形状
,s=8 #点的大小
数据集的分布图:
调用自己写的k-means进行聚类:
test_cent, test_cluster = kmeans(pd.DataFrame(X), 4)
#plt.scatter(testSet.iloc[:,0],testSet.iloc[:,1])
plt.scatter(test_cluster.iloc[:,0], test_cluster.iloc[:, 1], c=test_cluster.iloc[:, -1])
plt.scatter(test_cent[:,0],test_cent[:,1],c='r');
效果还是非常好