手把手教你如何利用K均值聚类实现异常值的识别

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

公司主营业务:成都做网站、成都网站设计、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联公司推出泰山免费做网站回馈大家。

# 导入第三方包
import numpy as np
import 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).T
mean2 = [0, 8]
cov2 = [[0.8, 0], [0, 2]]
x2, y2 = np.random.multivariate_normal(mean2, cov2, 5000).T
# 绘制两组数据的散点图
plt.rcParams['axes.unicode_minus'] = False
plt.scatter(x1, y1)
plt.scatter(x2, y2)
# 显示图形
plt.show()

手把手教你如何利用K均值聚类实现异常值的识别

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

K均值聚类的介绍

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

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

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

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

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

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

手把手教你如何利用K均值聚类实现异常值的识别

如上图所示,通过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)

手把手教你如何利用K均值聚类实现异常值的识别

如上图所示,当簇的个数为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", y="x2", hue='OutLier', data=res,
 fit_reg=False, legend=False)
plt.legend(loc='best')
plt.show()

手把手教你如何利用K均值聚类实现异常值的识别

如上图所示,蓝色的点即为异常点。从蓝色点的分布来看,上面那一簇所对应的异常点比较多(与之前的预判一致),而下面簇的异常点较少,且全部集中在散点的右侧。


当前文章:手把手教你如何利用K均值聚类实现异常值的识别
网页路径:http://myzitong.com/article/pisjed.html