手把手教你如何利用K均值聚类实现异常值的识别
首先,借助于Python随机生成两组二维数据,用于后文的实战。为了能够更加直观地洞察该数据,我们将其绘制成散点图。
#导入第三方包importnumpyasnpimportmatplotlib.pyplotasplt#随机生成两组二元正态分布随机数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()
如上图所示,图中蓝色和红之间形成鲜明的簇,其中每个簇内包含5000个数据。如果数据中存在异常点,目测蓝色的簇可能会包含更多异常,因为数据点相对分散一些。
K均值聚类的介绍
K均值聚类算法的思路非常通俗易懂,就是不断地计算各样本点与簇中心之间的距离,直到收敛为止,其具体的步骤如下:
(1)从数据中随机挑选k个样本点作为原始的簇中心。
(2)计算剩余样本与簇中心的距离,并把各样本标记为离k个簇中心最近的类别。
(3)重新计算各簇中样本点的均值,并以均值作为新的k个簇中心。
(4)不断重复(2)和(3),直到簇中心的变化趋于稳定,形成最终的k个簇。
也许上面的4个步骤还不足以让读者明白Kmeans的执行过程,可以结合下图更进一步地理解其背后的思想。
如上图所示,通过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)
如上图所示,当簇的个数为2时形成了一个明显的“拐点”,因为 K值从1到2时,折线的斜率都比较大,但是值为3时斜率突然就降低了很多,并且之后的簇对应的斜率都变动很小。所以,合理的值应该为2,与模拟的两个簇数据相吻合。
异常点识别原理
使用K均值聚类的思想识别数据中的异常点还是非常简单的,具体步骤如下:
利用“拐点法”、“轮廓系数法”、“间隔统计量法”或者“经验法”确定聚类的个数;基于具体的K值,对数据实施K均值聚类的应用;基于聚类的结果,计算簇内每个点到簇中心的距离;将距离跟阈值相比较,如果其大于阈值则认为是异常,否则正常;案例实战
为了验证我们在前文所说的的直觉(“目测蓝色的簇可能会包含更多异常”),接下来通过构造自定义函数,计算簇内的每个点与簇中心的距离,并判断其是否超过阈值的异常点下方代码可能有点长,但仔细阅读并查看对应的注释内容,相信你一定能够理解代码的思想。
defkmeans_outliers(data,clusters,is_scale=True):#指定聚类个数,准备进行数据聚类kmeans=KMeans(n_clusters=clusters)#用于存储聚类相关的结果cluster_res=[]#判断是否需要对数据做标准化处理ifis_scale:std_data=scale(data)#标准化kmeans.fit(std_data)#聚类拟合#返回簇标签labels=kmeans.labels_#返回簇中心centers=kmeans.cluster_centers_forlabelinset(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_forlabelinset(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))#返回数据的行合并结果returnpd.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()
如上图所示,蓝色的点即为异常点。从蓝色点的分布来看,上面那一簇所对应的异常点比较多(与之前的预判一致),而下面簇的异常点较少,且全部集中在散点的右侧。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。