K-means聚类算法及python代码实现

转载自https://www.cnblogs.com/ahu-lichang/p/7161613.html

K-means聚类算法(事先数据并没有类别之分!所有的数据都是一样的

1、概述

K-means算法是集简单和经典于一身的基于距离的聚类算法,采用距离作为相似性的评价指标,即认为两个对象的距离越近,其相似度就越大。

该算法认为类簇是由距离靠近的对象组成的,因此把得到紧凑且独立的簇作为最终目标。

2、核心思想

通过迭代寻找k个类簇的一种划分方案,使得用这k个类簇的均值来代表相应各类样本时所得的总体误差最小。

k个聚类具有以下特点:各聚类本身尽可能的紧凑,而各聚类之间尽可能的分开

k-means算法的基础是最小误差平方和准则,
其代价函数是:

$J(c,\mu)=\sum\limits_{i=1}^{k}\|x^{(i)}-\mu_{c^{(i)}}\|^2$

式中,$ μ c ( i ) \mu_{c^{(i)}} μc(i)`表示第i个聚类的均值。

各类簇内的样本越相似,其与该类均值间的误差平方越小,对所有类所得到的误差平方求和,即可验证分为k类时,各聚类是否是最优的。

上式的代价函数无法用解析的方法最小化,只能有迭代的方法。

3、算法步骤图解

下图展示了对n个样本点进行K-means聚类的效果,这里k取2。

![kmeans][base64str]

4、算法实现步骤

k-means算法是将样本聚类成 k个簇(cluster),其中k是用户给定的,其求解过程非常直观简单,具体算法描述如下:

  1. 随机选取 k个聚类质心点

  2. 重复下面过程直到收敛 {

    对于每一个样例 i,计算其应该属于的类:

    $c^{(i)}:=\underset{j}{\arg\min}\Vert x^{(i)}-\mu_{j}\Vert ^2$

    对于每一个类 j,重新计算该类的质心:

    μ j = ∑ i = 1 m 1 { c ( i ) = j } x ( i ) ∑ i = 1 m 1 { c ( i ) = j } \mu_j=\frac{\sum\limits_{i=1}^{m}1\{c^{(i)}=j\}x^{(i)}}{\sum\limits_{i=1}^{m}1\{c^{(i)}=j\}} μj=i=1m1{c(i)=j}i=1m1{c(i)=j}x(i)

 }

其伪代码如下:


创建k个点作为初始的质心点(随机选择)
当任意一个点的簇分配结果发生改变时
KaTeX parse error: Can't use function '$' in math mode at position 6: \quad$̲`对数据集中的每一个数据点 …\qquad$对每一个质心 $$\qquad\qquad$计算质心与数据点的距离
KaTeX parse error: Can't use function '$' in math mode at position 7: \qquad$̲`将数据点分配到距离最近的簇 …\quad$`对每一个簇,计算簇中所有点的均值,并将均值作为质心

5、K-means聚类算法python实战

需求:对给定的数据集进行聚类

本案例采用二维数据集,共80个样本,有4个类。
testSet.txt

-0.017612	14.053064	0
-1.395634	4.662541	1
-0.752157	6.538620	0
-1.322371	7.152853	0
0.423363	11.054677	0
0.406704	7.067335	1
0.667394	12.741452	0
-2.460150	6.866805	1
0.569411	9.548755	0
-0.026632	10.427743	0
0.850433	6.920334	1
1.347183	13.175500	0
1.176813	3.167020	1
-1.781871	9.097953	0
-0.566606	5.749003	1
0.931635	1.589505	1
-0.024205	6.151823	1
-0.036453	2.690988	1
-0.196949	0.444165	1
1.014459	5.754399	1
1.985298	3.230619	1
-1.693453	-0.557540	1
-0.576525	11.778922	0
-0.346811	-1.678730	1
-2.124484	2.672471	1
1.217916	9.597015	0
-0.733928	9.098687	0
-3.642001	-1.618087	1
0.315985	3.523953	1
1.416614	9.619232	0
-0.386323	3.989286	1
0.556921	8.294984	1
1.224863	11.587360	0
-1.347803	-2.406051	1
1.196604	4.951851	1
0.275221	9.543647	0
0.470575	9.332488	0
-1.889567	9.542662	0
-1.527893	12.150579	0
-1.185247	11.309318	0
-0.445678	3.297303	1
1.042222	6.105155	1
-0.618787	10.320986	0
1.152083	0.548467	1
0.828534	2.676045	1
-1.237728	10.549033	0
-0.683565	-2.166125	1
0.229456	5.921938	1
-0.959885	11.555336	0
0.492911	10.993324	0
0.184992	8.721488	0
-0.355715	10.325976	0
-0.397822	8.058397	0
0.824839	13.730343	0
1.507278	5.027866	1
0.099671	6.835839	1
-0.344008	10.717485	0
1.785928	7.718645	1
-0.918801	11.560217	0
-0.364009	4.747300	1
-0.841722	4.119083	1
0.490426	1.960539	1
-0.007194	9.075792	0
0.356107	12.447863	0
0.342578	12.281162	0
-0.810823	-1.466018	1
2.530777	6.476801	1
1.296683	11.607559	0
0.475487	12.040035	0
-0.783277	11.009725	0
0.074798	11.023650	0
-1.337472	0.468339	1
-0.102781	13.763651	0
-0.147324	2.874846	1
0.518389	9.887035	0
1.015399	7.571882	0
-1.658086	-0.027255	1
1.319944	2.171228	1
2.056216	5.019981	1
-0.851633	4.375691	1
-1.510047	6.061992	0
-1.076637	-3.181888	1
1.821096	10.283990	0
3.010150	8.401766	1
-1.099458	1.688274	1
-0.834872	-1.733869	1
-0.846637	3.849075	1
1.400102	12.628781	0
1.752842	5.468166	1
0.078557	0.059736	1
0.089392	-0.715300	1
1.825662	12.693808	0
0.197445	9.744638	0
0.126117	0.922311	1
-0.679797	1.220530	1
0.677983	2.556666	1
0.761349	10.693862	0
-2.168791	0.143632	1
1.388610	9.341997	0
0.317029	14.739025	0

代码如下:

#!/usr/bin/python
# coding=utf-8
import copy
import numpy as np
# 加载数据
def loadDataSet(fileName):  # 解析文件,按tab分割字段,得到一个浮点数字类型的矩阵
    dataMat = []              # 文件的最后一个字段是类别标签
    fr = open(fileName)
    for line in fr.readlines():
        curLine = line.strip().split('\t')
        fltLine = list(map(float, curLine))    # 将每个元素转成float类型
        dataMat.append(fltLine)
    return np.array(dataMat)

# 计算欧几里得距离
def distEclud(vecA, vecB):
    return np.sqrt(np.sum(np.power(vecA - vecB, 2))) # 求两个向量之间的距离

# 构建聚簇中心,取k个(此例中为4)随机质心
def randCent(dataSet, k):
    centroid = np.array(np.zeros((k,2)))   # 每个质心有n个坐标值,总共要k个质心
    for j in range(2):
        minJ = min(dataSet[:,j])
        maxJ = max(dataSet[:,j])
        rangeJ = float(maxJ - minJ)
        centroid[:,j] = minJ + rangeJ * np.random.rand(k)
    return centroid

# k-means 聚类算法
def kMeans(dataSet, k, distMeans =distEclud, createCent = randCent):
    m = dataSet.shape[0];counts=[]
    clusterAssments=[];centroids=[]
    clusterAssment = np.array(np.zeros((m,2)))    # 用于存放该样本属于哪类及质心距离
    # clusterAssment第一列存放该数据所属的中心点,第二列是该数据到中心点的距离
    centroid = createCent(dataSet, k)
    clusterChanged = True   # 用来判断聚类是否已经收敛
    while clusterChanged:
        clusterChanged = False
        count=0
        for i in range(m):  # 把每一个数据点划分到离它最近的中心点
            minDist = np.inf; minIndex = -1;
            for j in range(k):
                distJI = distMeans(centroid[j,:], dataSet[i,:-1])
                if distJI < minDist:
                    minDist = distJI; minIndex = j  # 如果第i个数据点到第j个中心点更近,则将i归属为j
            if clusterAssment[i,0] != minIndex: 
                clusterChanged = True;count+=1  # 如果分配发生变化,则需要继续迭代
                #print(clusterAssment[i,0],'-->',minIndex)
            clusterAssment[i,:] = minIndex,minDist**2   # 并将第i个数据点的分配情况存入字典

        for cent in range(k):   # 重新计算中心点
            ptsInClust = dataSet[clusterAssment[:,0] == cent][:,:-1]   # 去第一列等于cent的所有列
            centroid[cent,:] = np.mean(ptsInClust, axis = 0)  # 算出这些数据的中心点

        
        #此处为坑
#         centroids.append(centroid)
#         clusterAssments.append(clusterAssment)
        if clusterChanged==True:
            centroids.append(copy.copy(centroid))
            clusterAssments.append(copy.copy(clusterAssment))
            counts.append(count)
    return centroids, clusterAssments,counts
# --------------------测试----------------------------------------------------
# 用测试数据及测试kmeans算法
datMat=loadDataSet('f:/testSet.txt')
myCentroids,clustAssings,counts = kMeans(datMat,4)
print(myCentroids)
len(clustAssings)
counts # [58, 23, 3, 1]

运行结果:
![kmean2][base64str2]

画出每步迭代的图:

import matplotlib.pyplot as plt
fig1=plt.figure(1,figsize=(15,20))
for i in range(4):
    ax=fig1.add_subplot(32*10+i+1)
    s=clustAssings[i][:,0]+30
    c=clustAssings[i][:,0]+20
    ax.scatter(datMat[:,0],datMat[:,1],s,c)
    ax.scatter(myCentroids[i][:,0],myCentroids[i][:,1],s=150,c='r',marker='+')

plt.show()

结果共迭代了4步,每一步变动的点数分别为58, 23, 3, 1,画图如下:

kmeans2

6、K-means算法补充
K-means算法的缺点及改进方法

(1)值的选择是用户指定的,不同的k得到的结果会有挺大的不同,如下图所示,上边是k=3的结果,这个就太稀疏了,蓝色的那个簇其实是可以再划分成两个簇的。而下图是k=5的结果,可以看到紫色和黄色这两个簇应该是可以合并成一个簇的:

![kmeans4][base64str4]![kmeans5][base64str5]

kmeans4 kmeans5

改进:

对k的选择可以先用一些算法分析数据的分布,如重心和密度等,然后选择合适的k

(2)对k个初始质心的选择比较敏感,容易陷入局部最小值。例如,我们上面的算法运行的时候,有可能会得到不同的结果,如下面这两种情况。K-means也是收敛了,只是收敛到了局部最小值.

改进:

有人提出了另一个成为二分k均值(bisecting k-means)算法,它对初始的k个质心的选择就不太敏感.

(3)存在局限性,如下面这种非球状的数据分布就搞不定了

kmeans6

(4)数据集比较大的时候,收敛会比较慢.

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: K-Means聚类算法是一种用于对数据进行分组的机器学习算法,它可以帮助我们根据数据特征将相似的数据分为几类。Python实现K-Means聚类算法代码大致如下:import numpy as np from sklearn.cluster import KMeans# 加载数据 data = np.loadtxt("data.txt", delimiter=",")# 创建KMeans模型 kmeans = KMeans(n_clusters=3)# 训练模型 kmeans.fit(data)# 聚类心 centers = kmeans.cluster_centers_# 结果标签 labels = kmeans.labels_ ### 回答2: K-Means是一种常用的聚类算法,用于将数据集的元素划分为K个不同的组或类。以下是K-Means聚类算法Python实现代码示例: ```python import numpy as np class KMeans: def __init__(self, k=2, max_iters=100): self.k = k self.max_iters = max_iters def fit(self, X): self.centroids = self._initialize_centroids(X) for _ in range(self.max_iters): clusters = [[] for _ in range(self.k)] # Assign each data point to the nearest centroid for xi in X: distances = [np.linalg.norm(xi - centroid) for centroid in self.centroids] cluster_index = np.argmin(distances) clusters[cluster_index].append(xi) # Update centroids prev_centroids = np.copy(self.centroids) for i in range(self.k): self.centroids[i] = np.mean(clusters[i], axis=0) # Break loop if centroids do not change if np.allclose(prev_centroids, self.centroids): break def predict(self, X): return [np.argmin([np.linalg.norm(xi - centroid) for centroid in self.centroids]) for xi in X] def _initialize_centroids(self, X): indices = np.random.choice(range(len(X)), size=self.k, replace=False) return X[indices] ``` 以上代码实现了一个简单的K-Means聚类算法。`fit`方法用于训练模型,`predict`方法用于进行预测。在训练过程,首先随机选择K个初始质心,然后迭代更新每个样本的簇分配,直到达到最大迭代次数或质心不再发生变化。最后,预测时根据最近的质心将新的样本点分配到对应的簇。 请注意,这只是一个简单的K-Means实现,它可能不具有较强的鲁棒性和效率。实际应用,可以考虑使用成熟的机器学习库K-Means实现,如Scikit-learn等。 ### 回答3: K-Means聚类算法是一种常用的无监督学习算法,用于将数据集划分为K个不同的簇。下面是Python实现K-Means聚类算法代码示例: ```python import numpy as np def kmeans(data, K, max_iters=100): # 随机初始化K个心点 centers = data[np.random.choice(range(len(data)), K, replace=False)] for _ in range(max_iters): # 计算每个样本心点的欧式距离 dists = np.linalg.norm(data[:,:,np.newaxis] - centers.T[np.newaxis,:,:], axis=1) # 根据距离将样本分配到最近的簇 labels = np.argmin(dists, axis=1) # 更新每个簇的心点为该簇所有样本的平均值 centers_new = np.array([data[labels == k].mean(axis=0) for k in range(K)]) # 判断心点是否稳定不变,若不变则停止迭代 if np.all(centers == centers_new): break centers = centers_new return labels, centers # 测试数据 data = np.array([[1, 2], [1, 4], [1, 0], [4, 2], [4, 4], [4, 0]]) # 调用K-Means算法进行聚类 labels, centers = kmeans(data, K=2) # 打印聚类结果 print("聚类结果:", labels) print("聚类心点:", centers) ``` 上述代码实现K-Means聚类算法,其`data`表示输入的数据集,`K`表示要划分的簇的数量。`kmeans`函数使用随机初始化的心点,迭代计算样本心点的距离,并将样本分配到最近的簇。然后更新每个簇的心点为该簇所有样本的平均值,直到心点不再改变或达到最大迭代次数为止。最后返回每个样本所属的簇以及最终的心点。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值