k-means算法python实现

1.k-means算法的思想

k-means算法是一种非监督学习方式,没有目标值,是一种聚类算法,因此要把数据划分成k个类别,那么一般k是知道的。

那么假设k=3,聚类过程如下:

1.随机在数据当中抽取三个样本,当做三个类别的中心点(k1,k2,k3);

2.计算其余点(除3个中心点之外的点)到这三个中心点的距离,每一个样本应该有三个距离(a,b,c),然后选出与自己距离最近的中心点作为自己的标记,形成三个簇群;

3.分别计算这三个簇群的平均值,把三个平均值与之前的旧三个中心点比较,那么分两种情况:

   如果相同:结束聚类。

   如果不同:把这三个平均值当做新的中心点,然后重复第二步。

2.轮廓系数sci

那么,我们有一个评估标准:轮廓系数sci=(bi-ai)/max(bi,ai),其中i是样本,bi是i到其他簇群的所有样本距离的最小值,ai是i到本身簇群距离的平均值。需要注意:每一个样本都有一个轮廓系数,它的范围是[-1,1]

那么以这个图为例,说明一下怎么计算轮廓系数:

1.计算蓝1到自身类别的点距离的平均值ai;

2.计算蓝1分别到红、绿类别所有点的距离,求出平均值b1,b2,取其中最小值bi

那么蓝1的轮廓系数=(bi-ai)/max(bi,ai),我们希望做到外部距离最大化,内部距离最小化

sci的极端情况,如果bi>>>ai,则sci=1,这个是聚类的最好的情况;如果bi<<<ai,则sci=-1,这是糟糕的情况。

因此,当 不知道k的值时,可以通过计算轮廓系数来找到合适的k值。

3.用python实现

主要是两种方式实现了k-means算法,一种是直接调用sklearn中提供的接口,另一种是自己实现这个算法的过程,其实代码实现过程就是思想的应用,这两种方式都使用了相同的数据集,那么用到的数据集下载地址是:数据集下载

方式一:使用sklearn中提供的API实现

from copy import deepcopy
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from sklearn.cluster import KMeans
from sklearn.metrics import silhouette_score
data = pd.read_csv('venv/xclara.csv')
f1 = data['V1'].values
f2 = data['V2'].values
km = KMeans(n_clusters=3)

km.fit(data)
predict = km.predict(data)
print(predict)
plt.figure(figsize=(10,10))
colored = ['green','blue','red']
colr = [colored[i] for i in predict]
plt.scatter(f1,f2,color=colr)
a = silhouette_score(data,predict)
print(a)
plt.show()

那么这种方式直接调用了api,因此代码比较短,但是看不到具体实现细节,其运行结果如下图所示:

方式二:实现了算法的思想,完整代码如下:

有些不懂的地方我都查了查做了注释,因此可以参考

from copy import deepcopy
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt

plt.rcParams['figure.figsize'] = (16, 9)

"""
   使用plt.style.use('ggplot')命令,可以作出ggplot风格的图片。
   
"""
plt.style.use('ggplot')

data = pd.read_csv('venv/xclara.csv')

f1 = data['V1'].values
f2 = data['V2'].values

"""
    zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,
    然后返回由这些元组组成的对象,这样做的好处是节约了不少的内存。
    我们可以使用 list() 转换来输出列表。

"""
X = np.array(list(zip(f1, f2)))

print(X)
plt.scatter(f1, f2, c='black', s=6)

# 设定分区数
k = 3
# 随机获得中心点的X轴坐标
"""
   numpy.random.randint(low, high=None, size=None, dtype='l')
   函数的作用是,返回一个随机整型数,范围从低(包括)到高(不包括),即[low, high)。
   如果没有写参数high的值,则返回[0,low)的值。

"""
C_x = np.random.randint(0, np.max(X)-20, size=k)
# 随机获得中心点的Y轴坐标
C_y = np.random.randint(0, np.max(X)-20, size=k)
C = np.array(list(zip(C_x, C_y)), dtype=np.float32)


# 将初始化中心点画到输入的样例数据上
plt.scatter(f1, f2, c='black', s=7)
plt.scatter(C_x, C_y, marker='*', s=200, c='red')

# 按行的方式计算两个坐标点之间的距离
def dist(a, b, ax=1):
    return np.linalg.norm(a - b, axis=ax)

# 用于保存中心点更新前的坐标
C_old = np.zeros(C.shape)

# 用于保存数据所属中心点,就是保存每个点的标记,标记它属于哪个中心点
clusters = np.zeros(len(X))

# 迭代标识位,通过计算新旧中心点的距离
iteration_flag = dist(C, C_old, 1)

tmp = 1
# 若中心点不再变化或循环次数不超过20次(此限制可取消),则退出循环
while iteration_flag.any() != 0 and tmp < 20:
    # 循环计算出每个点对应的最近中心点
    for i in range(len(X)):
        # 计算出每个点与中心点的距离
        distances = dist(X[i], C, 1)
        # 记录0 - k-1个点中距离近的点
        cluster = np.argmin(distances)
        # 记录每个样例点与哪个中心点距离最近
        clusters[i] = cluster

    # 采用深拷贝将当前的中心点保存下来
    # print("the distinct of clusters: ", set(clusters))
    C_old = deepcopy(C)
    # 从属于中心点放到一个数组中,然后按照列的方向取平均值
    for i in range(k):
        points = [X[j] for j in range(len(X)) if clusters[j] == i]
        # print(points)
        # print(np.mean(points, axis=0))
        """
            numpy.mean(a, axis, dtype, out,keepdims )
            mean()函数功能:求取均值 ,以m * n矩阵举例
            axis 不设置值,对 m*n 个数求均值,返回一个实数
            axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
            axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
        """
        C[i] = np.mean(points, axis=0)
        # print(C[i])
    # print(C)

    # 计算新旧节点的距离
    print('循环第%d次' % tmp)
    tmp = tmp + 1
    iteration_flag = dist(C, C_old, 1)
    print("新中心点与旧点的距离:", iteration_flag)

# 最终结果图示
colors = ['r', 'g', 'b', 'y', 'c', 'm']
fig, ax = plt.subplots()
# 不同的子集使用不同的颜色
for i in range(k):
        points = np.array([X[j] for j in range(len(X)) if clusters[j] == i])
        ax.scatter(points[:, 0], points[:, 1], s=7, c=colors[i])
ax.scatter(C[:, 0], C[:, 1], marker='*', s=200, c='black')

#轮廓系数
a = silhouette_score(data,clusters)
print(a)

plt.show()

运行结果如下图:

参考了这篇博客,链接是https://www.cnblogs.com/shenfeng/p/kmeans_demo.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值