k均值聚类算法优缺点_手把手教你如何利用K均值聚类实现异常值的识别

首先,借助于Python随机生成两组二维数据,用于后文的实战。为了能够更加直观地洞察该数据,我们将其绘制成散点图。

# 导入第三方包import numpy as npimport matplotlib.pyplot as plt# 随机生成两组二元正态分布随机数np.random.seed(1234)mean1 = [0.5, 0.5]cov1 = [[0.3, 0], [0, 0.1]]x1, y1 = np.random.multivariate_normal(mean1, cov1, 5000).Tmean2 = [0, 8]cov2 = [[0.8, 0], [0, 2]]x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T# 绘制两组数据的散点图plt.rcParams['axes.unicode_minus'] = Falseplt.scatter(x1, y1)plt.scatter(x2, y2)# 显示图形plt.show()
f2584b49fed60dc0ab758e5936ab870e.png

如上图所示,图中蓝色和红色之间形成鲜明的簇,其中每个簇内包含5000个数据。如果数据中存在异常点,目测蓝色的簇可能会包含更多异常,因为数据点相对分散一些。

K均值聚类的介绍

K均值聚类算法的思路非常通俗易懂,就是不断地计算各样本点与簇中心之间的距离,直到收敛为止,其具体的步骤如下:

(1)从数据中随机挑选k个样本点作为原始的簇中心。

(2)计算剩余样本与簇中心的距离,并把各样本标记为离k个簇中心最近的类别。

(3)重新计算各簇中样本点的均值,并以均值作为新的k个簇中心。

(4)不断重复(2)和(3),直到簇中心的变化趋于稳定,形成最终的k个簇。

也许上面的4个步骤还不足以让读者明白Kmeans的执行过程,可以结合下图更进一步地理解其背后的思想。

8eb2222c5e69f3e09d16a477b9faf1ab.png

如上图所示,通过9个子图对Kmeans聚类过程加以说明:子图1,从原始样本中随机挑选两个数据点作为初始的簇中心,即子图中的两个五角星;子图2,将其余样本点与这两个五角星分别计算距离(距离的度量可选择欧氏距离、曼哈顿距离等),然后将每个样本点划分到离五角星最近的簇,即子图中按虚线隔开的两部分;子图3,计算两个簇内样本点的均值,得到新的簇中心,即子图中的五角星;子图4,根据新的簇中心,继续计算各样本与五角星之间的距离,得到子图5的划分结果和子图6中新的簇内样本均值;以此类推,最终得到理想的聚类效果,如子图9所示,图中的五角星即最终的簇中心点。

在上文中,我们生成了两组随机数据,从图中一眼就可以看出需聚为两类,然而在实际应用中,很多数据都无法通过可视化或直觉判断聚类的个数(即K值)。但这不代表没有方法锁定最佳的K值,在书《从零开始学Python数据分析与挖掘》的第十五章介绍了“拐点法”、“轮廓系数法”和“间隔统计量法”,感兴趣的朋友可以去了解一下。这里就使用书中的自定义函数,测试一下K应该对应的值:

# 将两组数据集汇总到数据框中X = pd.DataFrame(np.concatenate([np.array([x1, y1]), np.array([x2, y2])], axis=1).T)X.rename(columns = {0:'x1',1:'x2'}, inplace = True)# 自定义函数的调用k_SSE(X, 10)
0d8cd35aebbc008900a133c60d61a037.png

如上图所示,当簇的个数为2时形成了一个明显的“拐点”,因为 K值从1到2时,折线的斜率都比较大,但是值为3时斜率突然就降低了很多,并且之后的簇对应的斜率都变动很小。所以,合理的值应该为2,与模拟的两个簇数据相吻合。

异常点识别原理

使用K均值聚类的思想识别数据中的异常点还是非常简单的,具体步骤如下:

  • 利用“拐点法”、“轮廓系数法”、“间隔统计量法”或者“经验法”确定聚类的个数;
  • 基于具体的K值,对数据实施K均值聚类的应用;
  • 基于聚类的结果,计算簇内每个点到簇中心的距离;
  • 将距离跟阈值相比较,如果其大于阈值则认为是异常,否则正常;

案例实战

为了验证我们在前文所说的的直觉(“目测蓝色的簇可能会包含更多异常”),接下来通过构造自定义函数,计算簇内的每个点与簇中心的距离,并判断其是否超过阈值的异常点下方代码可能有点长,但仔细阅读并查看对应的注释内容,相信你一定能够理解代码的思想。

def kmeans_outliers(data, clusters, is_scale = True): # 指定聚类个数,准备进行数据聚类 kmeans = KMeans(n_clusters=clusters) # 用于存储聚类相关的结果 cluster_res = [] # 判断是否需要对数据做标准化处理 if is_scale: std_data = scale(data) # 标准化 kmeans.fit(std_data) # 聚类拟合 # 返回簇标签 labels = kmeans.labels_ # 返回簇中心 centers = kmeans.cluster_centers_ for label in set(labels): # 计算簇内样本点与簇中心的距离 diff = std_data[np.array(labels) == label,] -  - np.array(centers[label]) dist = np.sum(np.square(diff), axis=1) # 计算判断异常的阈值 UL = dist.mean() + 3*dist.std() # 识别异常值,1表示异常,0表示正常 OutLine = np.where(dist > UL, 1, 0)  raw_data = data.loc[np.array(labels) == label,] new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine}) # 重新修正两个数据框的行编号 raw_data.index = new_data.index = range(raw_data.shape[0]) # 数据的列合并 cluster_res.append(pd.concat([raw_data,new_data], axis = 1)) else: kmeans.fit(data) # 聚类拟合 # 返回簇标签 labels = kmeans.labels_ # 返回簇中心 centers = kmeans.cluster_centers_ for label in set(labels): # 计算簇内样本点与簇中心的距离 diff = np.array(data.loc[np.array(labels) == label,]) -  - np.array(centers[label]) dist = np.sum(np.square(diff), axis=1) UL = dist.mean() + 3*dist.std() OutLine = np.where(dist > UL, 1, 0) raw_data = data.loc[np.array(labels) == label,] new_data = pd.DataFrame({'Label':label,'Dist':dist,'OutLier':OutLine}) raw_data.index = new_data.index = range(raw_data.shape[0]) cluster_res.append(pd.concat([raw_data,new_data], axis = 1)) # 返回数据的行合并结果  return pd.concat(cluster_res)# 调用函数,返回异常检测的结果res = kmeans_outliers(X,2,False)# res# 绘图sns.lmplot(x="x1
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值