|
前言 通常,咱们做数据挖掘的时候经常免不了会遇到异常值检测或者异常值处理等步骤,那么什么是异常值呢?如何检测数据中是否存在异常值?如何处理数据中的异常值?本文专门探究一下这些问题。 异常值又称离群点,是指那些在数据集中存在的不合理的值,需要注意的是,不合理的值是偏离正常范围的值,不是错误值。 异常值出现的原因:数据集中的异常值可能是由于传感器故障、人工录入错误或异常事件导致。一、异常值的检测 异常值的检测通常有简单统计分析、3σ原则、箱型图、聚类等方法,以下详细对各个方法进行逐一说明。1.1简单统计分析 最常用的统计量是最大值和最小值,用来判断这个变量的取值是否超出合理的范围。在pandas中,一般使用describ属性就可以查看相关的统计量。#全国房价数据统计df_dropna=df[~df.price.isna()].reset_index(drop=True)#首先剔除NA值df_dropna.price.describe()1231.23σ原则(Z-scoreMethod) 3σ原则的前提假设是数据集符合正态分布(高斯分布),如下图: 观察上图容易得出以下数据分布情况:数值分布在区间(μ-σ,μ+σ)中的概率为0.6826数值分布在区间(μ-2σ,μ+2σ)中的概率为0.9545数值分布在区间(μ-3σ,μ+3σ)中的概率为0.9973 可以认为,数据存在随机误差,其取值几乎全部集中在(μ-3σ,μ+3σ)区间内,超出这个范围的可能性仅占不到0.3%,那么误差超过这个区间的值就识别为异常值了。 实际上,大部分真实的数据并不满足这一条件,我们就需要先对原始数据集进行Z-score变换,使用原始数据的均值(μ)和标准差(σ)进行数据的标准化。经过处理的数据也将服从标准正态分布,其均值为0,标准差为1,故3σ原则又被称为Z-scoremethod。Z=x−μσZ=\frac{x-μ}{σ}Z=σx−μ 经过Z-score标准化后得到符合正态分布的数据,我们就可以使用3σ原则来处理数据了。演示代码如下:#观察原始房价数据分布情况importmatplotlib.pyplotasplts=df_dropna.pricefig=plt.figure(figsize=(6,6))#创建自定义图像ax1=fig.add_subplot(2,1,1)#创建子图1ax1.scatter(s.index,s.values)#绘制散点图plt.grid()#添加网格ax2=fig.add_subplot(2,1,2)#创建子图2s.hist(bins=30,alpha=0.5,ax=ax2)#绘制直方图s.plot(kind='kde',secondary_y=True,ax=ax2)#绘制密度图,使用双坐标轴plt.grid()plt.show()#显示自定义图像###########################################Z-score变换_std=df_dropna.price.std()_mean=df_dropna.price.mean()df_dropna['z_price']=df_dropna.price.map(lambdaxx-_mean)/_std)#3σ原则检测异常值_mean=0_std=1mark=(_mean-3*_std>df_dropna['z_price'])|(df_dropna['z_price']>_mean+3*_std)df_dropna[mark]#异常数据1234567891011121314151617181920212223242526272829 根据实际业务需求,若数据不服从正态分布,也可以不做标准化处理,可以用远离平均值的多少倍标准差来描述(这就使Z-score方法可以适用于不同的业务场景,只是需要根据经验来确定kσ中的k值,这个k值就可以认为是阈值),演示代码如下:#Z-score方法检测异常值(阈值k=3)_mean=df_dropna.price.mean()_std=df_dropna.price.std()mark=(_mean-3*_std>df_dropna.price)|(df_dropna.price>_mean+3*_std)df_dropna[mark]#异常数据123451.3RobustZ-scoreMethod 该方法也被称为中位数绝对偏差法。它类似于Z-score方法,只是参数有所变化。由于平均值和标准差受异常值的影响很大,因此我们使用中位数和中位数的绝对偏差来改变这个参数,公式如下:R.Z.score=0.6745×(x−Median)MADwhereMAD=median(∣x−Median∣)R.Z.score=\frac{0.6745\times\left(x-Median\right)}{MAD}\\where\quadMAD=median(∣x−Median∣)R.Z.score=MAD0.6745×(x−Median)whereMAD=median(∣x−Median∣) 假设xxx服从标准正态分布,那么MADMADMAD会收敛于半正态分布的中位数,即正态分布的75%百分位,并且N(0.75)≃0.6745N(0.75)≃0.6745N(0.75)≃0.6745。计算出稳健Z分数后,可以根据以下异常值的判定准则:如果∣Z∣≤2|Z|≤2∣Z∣≤2则结果满意(satisfactory)如果2<∣Z∣<32<|Z|<32<∣Z∣<3则结果可疑(questionable)如果∣Z∣≥3|Z|≥3∣Z∣≥3则结果不满意(unsatisfactory) 演示代码:#R.Z.scoreMethod检测异常值importnumpyasnpimportscipy#计算R.Z.scoremed=np.median(df_dropna.price.values)mad=scipy.stats.median_absolute_deviation(df_dropna.price.values)r_z_score=df_dropna.price.map(lambdax0.6745*(x-med))/(np.median(mad)))#判定异常值mark=np.abs(r_z_price.values)>3df_dropna[mark]#异常数据123456789101.4箱型图(四分位距法) 箱型图提供了识别异常值的标准,异常值通常被定义为小于QL-1.5IQR或大于QU+1.5IQR的数据。QL称为下四分位数,表示全部值中有四分之一的数据取值比它小;QU称为上四分位数,表示全部值中有四分之一的数据取值比它大;IQR称为四分位数间距,是上四分位数QU与下四分位数QL之差。所以此方法又称为四分位距法,如下图。#箱型图可视化数据importmatplotlib.pyplotaspltplt.boxplot(df_dropna.price)plt.show()#四分位距法检测异常值QL=df_dropna.price.quantile(0.25)QU=df_dropna.price.quantile(0.75)IQR=QU-QLmark=(df_dropna.priceup)df_dropna[mark]#异常数据123451.6DBSCAN聚类(DENSITY-BASEDSPATIALCLUSTERINGOFAPPLICATIONSWITHNOISE) DBSCAN是一种基于密度的聚类算法,它将数据集划分为高密度区域的子组,并将稀疏区域聚类识别为异常值。集群标记-1表示该集群包含离群值,其余集群没有离群值。这种方法类似于K-means聚类。DBSCAN在多元离群值检测中具有最佳的检测效果。DBSCAN需要两个参数:epsilon:一个距离参数,定义搜索附近邻居的半径。minPts:形成集群所需的最小点数。 使用以上2个参数我们就可以把每个数据点分类成:核心点:在其半径内至少有最小数量的其他点(minPts)的点。边界点:一个点在核心点的半径内,但小于其自身半径内其他点(minPts)的最小数量。噪声点:既不是核心点也不是边界点的点。 如下图所示: 演示代码:importpandasaspdfromsklearn.clusterimportDBSCANdefDBSCAN_outliers(df):#df是列数据outlier_detection=DBSCAN(eps=2,metric='euclidean',min_samples=5)clusters=outlier_detection.fit_predict(df.values.reshape(-1,1))returnclusters12345671.7孤立随机森林聚类(ISOLATIONFOREST) 它是一种聚类算法,属于集成决策树家族,在原理上类似于随机森林。 孤立随机森林的特点有以下几点:(1)它将数据点分类为异常值和非异常值,并适用于非常高维的数据。(2)该方法基于决策树,分离出异常值。(3)如果结果是-1,这意味着这个特定的数据点是一个异常值。如果结果为1,则意味着该数据点不是异常值。演示代码:fromsklearn.ensembleimportIsolationForestimportnumpyasnpimportpandasaspddefIsolationForest_outliers(df):#df是列数据iso=IsolationForest(behaviour='new',random_state=1,contamination='auto')preds=iso.fit_predict(df.values.reshape(-1,1))returnpreds123456781.8GRUBBSTEST Grubbs’test是一个假设检验方法。原假设H0H_0H0:数据集中无异常值。备选假设H1H_1H1:数据集中有一个异常值。 其统计量被定义为:Gcalculated=max(∣Xi−Xˉ∣)SDG_{calculated}=\frac{max(|X_i-\bar{X}|)}{SD}Gcalculated=SDmax(∣Xi−Xˉ∣) 其中Xˉ\bar{X}Xˉ和SD分别代表样本均值和样本标准差。其临界值被定义为:Gcitical=N−1N(tα/(2N),N−2)2N−2+(tα/(2N),N−2)2G_{citical}=\frac{N-1}{\sqrt{N}}\sqrt{\frac{(t_{\alpha/(2N),N-2})^{2}}{N-2+(t_{\alpha/(2N),N-2})^{2}}}Gcitical=NN−1N−2+(tα/(2N),N−2)2(tα/(2N),N−2)2 如果估计值GcalculatedG_{calculated}Gcalculated>临界值GcriticalG_{critical}Gcritical,拒绝原假设,备选假设成立,即数据集中有一个值是异常值。 一下是演示代码:importnumpyasnpimportscipy.statsasstatsdefgrubbs_test(x):n=len(x)mean_x=np.mean(x)std_x=np.std(x)numerator=max(abs(x-mean_x))g_calculated=numerator/std_xprint("GrubbsCalculatedValue:",g_calculated)t_value=stats.t.ppf(1-0.05/(2*n),n-2)g_critical=((n-1)*np.sqrt(np.square(t_value)))/(np.sqrt(n)*np.sqrt(n-2+np.square(t_value)))print("GrubbsCriticalValue:",g_critical)ifg_critical>g_calculated:print("Fromgrubbs_testweobservethatcalculatedvalueislesserthancriticalvalue,Acceptnullhypothesisandconcludethatthereisnooutliers\n")else:print("Fromgrubbs_testweobservethatcalculatedvalueisgreaterthancriticalvalue,Rejectnullhypothesisandconcludethatthereisanoutliers\n")12345678910111213141516171.9可视化数据(Visualizingthedata) 数据可视化对于数据清理、数据挖掘、异常值和异常组的检测、趋势和集群识别等都很有用。下面是用于发现异常值的数据可视化图列表:Boxandwhiskerplot(boxplot)箱线图Scatterplot散点图Histogram直方图DistributionPlot.分布图QQplotQ-Q图importpandasaspdimportseabornassnsfrommatplotlibimportpyplotaspltfromstatsmodels.graphics.gofplotsimportqqplotdefBox_plots(s):plt.figure(figsize=(10,4))plt.title("BoxPlot")sns.boxplot(s)plt.show()defhist_plots(s):plt.figure(figsize=(10,4))plt.hist(s)plt.title("HistogramPlot")plt.show()defscatter_plots(s1,s2):fig,ax=plt.subplots(figsize=(10,4))ax.scatter(s1,s2)ax.set_xlabel('Age')ax.set_ylabel('Fare')plt.title("ScatterPlot")plt.show()defdist_plots(s):plt.figure(figsize=(10,4))sns.distplot(s)plt.title("Distributionplot")sns.despine()plt.show()defqq_plots(df):plt.figure(figsize=(10,4))qqplot(s,line='s')plt.title("NormalQQPlot")plt.show()12345678910111213141516171819202122232425262728293031323334353637二、异常值的处理 异常值的存在对数据集的影响有以下方面:离群值严重影响数据集的均值和标准差。这些可能在统计上给出错误的结果。它增加了误差方差,降低了统计检验的力量。如果异常值是非随机分布的,它们会降低正态性。大多数机器学习算法在异常值存在时不能很好地工作。因此,检测和去除异常值是很有必要的。它们还会影响回归、方差分析和其他统计模型假设的基本假设。 所以当我们检测到数据中存在异常值时必须做适当的处理才会使得数据挖掘工作更加准确,一般的,异常值的处理方法有:删除值、改变值、插补法和分组处理。2.1删除异常值 直接将含有异常值的记录删除,通常有两种策略:整条删除和成对删除。这种方法最简单简单易行,但缺点也不容忽视,一是,在观测值很少的情况下,这种删除操作会造成样本量不足;二是,直接删除、可能会对变量的原有分布造成影响,从而导致统计模型不稳定。#删除异常值(使用3σ原则)df_drop_outlier=df_dropna[~mark].reset_index(drop=True)#剔除异常值后的箱型图importmatplotlib.pyplotaspltplt.boxplot(df_drop_outlier.price)plt.show()12345672.2数据变换 转换变量也可以消除异常值。这些转换后的值减少了由极值引起的变化。转换方法通常有:范围缩放:Scalling对数变换:LogTransformation立方根归一化:CubeRootTransformationBox-Cox转换:Box-CoxTransformation 这些技术将数据集中的值转换为更小的值,而且不会丢失数据。如果数据有很多极端值或倾斜,数据变换有助于使您的数据正常。但是这些技巧并不总是给你最好的结果。在所有这些方法中,box-cox变换给出了最好的结果。以下代码仅仅作为演示:#Scallingfromsklearnimportpreprocessingscaler=preprocessing.StandardScaler()result=scaler.fit_transform(df_dropna.price.values.reshape(-1,1))#Logtransformationimportnumpyasnpresult=np.log(df_dropna.price.values)#CuberootTransformationimportnumpyasnpresult=np.cbrt(df_dropna.price.values)#Box-CoxTransformationimportscipyresult,maxlog=scipy.stats.boxcox(df_dropna.price.values,lmbda=None)123456789101112131415162.3统一值插补法 像缺失值的归责(imputation)一样,我们也可以归责异常值。在这种方法中,我们可以使用平均值、中位数、零值来对异常值进行替换。由于我们进行了输入,所以没有丢失数据。应选则合适的替换,这里提及一下,选择中值不受异常值的影响。以下代码仅仅作为演示。#均值替换异常值_mean=df_dropna.price.mean()df_dropna[mark]=_mean#中位数替换异常值_median=df_dropna.price.median()df_dropna[mark]=_median#0替换异常值df_dropna[mark]=0123456782.4回归插补法 发现两个相关的变量之间的变化模式,通过使数据拟合一个函数来平滑数据,也就是回归插补。 若是变量之间存在依赖关系,也就是y=f(x)y=f(x)y=f(x),那么就可以设法求出依赖关系fff,再根据x来预测yyy,这也是回归问题的实质。 实际问题中更常为见的假设是p(y)=N(f(x))p(y)=N(f(x))p(y)=N(f(x)),NNN为正态分布。假设yyy是观测值并且存在噪声数据,根据我们求出的xxx和yyy之间的依赖关系,再根据xxx来更新yyy的值,这样就能去除其中的随机噪声,这就是回归去噪的原理。2.5盖帽法 也就是截尾处理,这其实也是一种插补方法。整行替换数据框里百分位数处于99%以上和1%以下的点:将99%以上的点值=99%的点值;小于1%的点值=1%的点值。#盖帽法处理异常值up=df_dropna.price.quantile(0.99)low=df_dropna.price.quantile(0.01)df_dropna.loc[df_dropna.price>up,'price']=updf_dropna.loc[df_dropna.price
|
|