两种高效便捷易于实现的异常值处理方法
前言
上一篇文章结尾,我们同样预留了几个问题。首先,我们将基于实际数据来使用修正版的拉依达准则进行异常值的筛选;另外,关于异常点的处理,当数据不是很明确服从什么分布(甚至也不服从近似正态分布,但如果是数据量很大情况下,一般是近似服从正态性的)时,运用概率分布原理筛选异常点,效果会稍差,取而代之的是用系统性方法来筛选异常值。
一:基于实际数据多方位验证修正拉依达准则的有效性
- 先回顾第二篇文章中提到的基于正常数据提取的特征结果
多分类情况下的便捷有效特征提取方法的推广
Index(['alcohol', 'total sulfur dioxide','volatile acidity', 'fixed acidity',
'citric acid', 'residual sugar','sulphates', 'free sulfur dioxide',
'pH', 'density', 'chlorides'],dtype='object')
- 数据集中插入异常点数据
再基于此数据,我们通过代码,人为在1500个样本中每个维度数据随机位置上添加30个异常点,异常点的值是原始值的5-25倍,设随机数服从均匀分布。
import random as rd
ret_all = []
for i in range(data.shape[1] - 1):##遍历每一列
ret = []
for j in range(30):##循环30次
r = rd.randint(1, data.shape[0])
mul = rd.uniform(5, 25)#随机取5-25之间的数
data.iloc[r,i] = data.iloc[r,i] * mul
ret.append(r)
ret_all.append(ret)
然后我们分别运行程序,其中第一个程序是异常点存在时,不做任何处理的筛选结果;第二个程序是对数据集做普通拉依达(去极值化)后的筛选结果,第三个程序就是通过修正版拉依达处理后的数据集的筛选结果(修正拉依达准则代码请看上一篇文章)。
异常点存在时
标准化后绝对距离
0 1.214867 1 1.216809 2 1.263637
3 1.183755 4 1.232970 5 1.086079
6 1.126362 7 1.381525 8 1.377077
9 1.259793 10 1.350094
Index(['density', 'pH', 'alcohol', 'citric acid', 'sulphates', 'chlorides',
'volatile acidity', 'fixed acidity', 'residual sugar',
'total sulfur dioxide', 'free sulfur dioxide'],dtype='object')
异常点通过普通拉依达(去极值化)筛选后的结果
标准化后绝对距离
0 1.178103 1 0.709496 2 1.635919
3 1.050437 4 0.936631 5 1.042880
6 1.193319 7 1.406353 8 1.350272
9 1.122081 10 1.490989
Index(['citric acid', 'alcohol', 'density', 'pH', 'total sulfur dioxide',
'fixed acidity', 'sulphates', 'residual sugar', 'free sulfur dioxide',
'chlorides', 'volatile acidity'],dtype='object')
通过修正版的拉依达筛选后结果
标准化后绝对距离
0 1.494828 1 1.338518 2 1.322476
3 1.266294 4 1.229666 5 1.344060
6 1.391119 7 1.140193 8 1.279546
9 1.310606 10 1.380547
Index(['fixed acidity','total sulfur dioxide','alcohol', 'free sulfur dioxide',
'volatile acidity','citric acid', 'sulphates', 'pH',
'residual sugar' 'chlorides', 'density'],dtype='object')
- 直观分析
通过上面的运算结果,我们很容易看出,经过修正版的拉依达准则来处理含异常点的数据集后,再经过距离特征筛选计算,跟不含异常点的原始数据集的结果非常相近,排名前几的和排名后几的特征名几乎一模一样;而不经过处理的异常数据集以及经过普通准则(去极值化)处理的,提取特征明显有不优之处,很多应该排名在后面的特征反而排名靠前了。
- 有效性判断
我们定义筛选的准确率 r t = n 筛 选 出 的 n 实 际 存 在 的 × 100 % r_{t}=\frac{n_{筛选出的}}{n_{实际存在的}}\times 100\% rt=n实际存在的n筛选出的×100% ,这里 n 实 际 存 在 的 = 30 n_{实际存在的}=30 n实际存在的=30 ,下图1展示的是每个维度下,筛选两种方法的筛选异常值的正确率。明显可以看出,修正版的拉依达准则优于普通拉依达(去极值化)的。
- 小结
至此,基于概率分布的异常点模型+基于距离筛选特征模型的综合建模分析可以说告一段了,从之前的实验可以看出,所有的实验结果是在我们理论分析范围内,这直接证明了之前我们提出的相关模型是有效方便的!
二:基于数据中心的异常点筛选方法
- 说明
这里把一个样本的所有维度数据当成一个对象来看(纬度高可以先特征提取和降维),最后筛选的结果直接是样本的索引值,而不再是某个样本对应的哪个维度数据是异常的,那这种方法往往涉及的运算量比较多,但适用性比较强。
- 实现步骤
- 设样本数据大小 A m , n = m × n A_{m,n}=m\times n Am,n=m×n ( m m m 个样本, n n n 维特征长度)防止维度数据间的量级带来的偏差,我们先对数据进行标准化或者归一化处理,归一化处理有: B m , n = ( A m , n − m i n ( A ) ) / ( m a x ( A ) − m i n ( A ) ) B_{m,n}=(A_{m,n}-min(A))/(max(A)-min(A)) Bm,n=(Am,n−min(A))/(max(A)−min(A)) ,
- 求解矩阵 B m , n B_{m,n} Bm,n 每一维度的中心点为 C = ( C 1 , C 2 , . . . C n ) C=(C_{1},C_{2},...C_{n}) C=(C1,C2,...Cn),令矩阵 B B B 每一行数据各与对应的中心点值做差,即 H i 1 ≤ i ≤ m , j = B i 1 ≤ i ≤ m , j − C j H_{i_{1\leq i\leq m},j}=B_{i_{1\leq i\leq m},j}-C_{j} Hi1≤i≤m,j=Bi1≤i≤m,j−Cj ,
- 再对矩阵 H H H 每一行做 p p p 阶范数运算,即 H i = ( ∑ j m ∣ H i , j ∣ p ) 1 p H_{i}=\left( \sum_{j}^{m}{|H_{i,j}|^{p}} \right)^{\frac{1}{p}} Hi=(∑jm∣Hi,j∣p)p1 。前提是每一行的 p p p 阶矩有界,即 E ∣ H i , j ∣ p < ∞ E{|H_{i,j}|^{p}} <\infty E∣Hi,j∣p<∞ ,
- 再对向量 H i H_{i} Hi 的属性值做约束处理,可以选择 H i ˉ = H i m e a n / m a x / m e d i a n ( H i ) \bar{H_{i}}=\frac{H_{i}}{mean/max/median(H_{i})} Hiˉ=mean/max/median(Hi)Hi ,
- 最后我们选择阈值 α \alpha α ,向量 H i H_{i} Hi 中元素大于 α \alpha α 的将剔除。
- 这里,中心向量 C 取值是个难点,如何选择?
一是直接对数据集做均值化处理,数据一般会标准化处理,防止数量级带来的差异,接着我们可以多深入做一步,那就是然后再取均值,
二是通过聚类的方式找到聚类中心,我们可以使用半监督聚类、无监督聚类等。
三:基于分位数找出数据中心
我们还是先从简单方法出发,对数据集进行分位数选取之后再取各维度数据均值。
- 找出每个维度数据的10%的分位数值和95%分位数值,我们只取在这两者之间的所有数据,其中数据源(节选)如下图2:
再通过如下代码,我们得到了数据各个维度的中心。
mean_ls = []
for i in data_temp.columns:
loc_down = np.percentile(data_temp[i],10)
loc_up = np.percentile(data_temp[i],95)
data_t = data_temp[i][(data_temp[i] > loc_down) & (data_temp[i] <= loc_up)]#数据做判断从而进行筛选
mean_ls.append(data_t.mean())
接着对第二节的步骤一一实现:
data_temp = (data - data.min()) / (data.max() - data.min())
def pick_discrete_points(threshold,p=2):##相对距离阈值设为2
df_ls = []
for i in data_temp.columns:
loc_down = np.percentile(data_temp[i],10)
loc_up = np.percentile(data_temp[i],95)
data_t = data_temp[i][(data_temp[i] > loc_down) & (data_temp[i] <= loc_up)]
norm_tmp = data_temp[i] - data_t.mean()#每一行都减去数据中心值
df_ls.append(norm_tmp)
df_f = pd.concat(df_ls,axis=1)
norm_new = df_f.apply(np.linalg.norm,ord=p,axis=1)##计算每一行的p阶范数
norm_fin = norm_new / norm_new.mean()##数据进行范围约束
norm_fin[norm_fin <= threshold].plot(style='bo',label='正常点')##正常点
discrete_points = norm_fin[norm_fin > threshold]
discrete_points.plot(style='ro',figsize=(15,8),label='异常点')##离散点
y = [threshold for i in range(len(norm_fin))]##标准线
print(len(discrete_points.index),discrete_points.index)
plt.plot(y,'orange',label='相对标准线')
plt.xlabel('编号',fontsize=18)
plt.ylabel('相对距离',fontsize=18)
plt.tick_params(labelsize=18)
plt.annotate('异常点(%s,%.3f)'%(discrete_points.index[5],norm_fin.loc[discrete_points.index[5]]), xy=(discrete_points.index[5],norm_fin.loc[discrete_points.index[5]]),
xytext=(300,3), arrowprops=dict(arrowstyle="fancy"),fontsize=20)
plt.legend(fontsize=15)
plt.show()
pick_discrete_points(2)
从图3中,我们很清楚观察到这将近1000个样本数据根据相对距离值的整体分布情况,标准线上方就是我们的异常点数据,至于找到的异常点数据如何处理,那有很多种选择,在之前的文章中我们都有所提到,这里就不再重复述说了。
四:总结与展望
- 这篇文章我们主要基于实际数据说明了修正拉依达准则对异常点筛选的有效性,以及运用系统性方法也做了一次异常点筛选,最终可视化了筛选结果,以上的结果来看,我们的方法和模型还是比较有效的。
- 对于系统性方法的异常值筛选,我们提到数据中心的由来可以是基于半监督聚类和无监督聚类得来,那我如何实现这些过程并证明是有效的方法呢,这也是个很不错的探讨内容,我们将在随后的文章中给出探讨结论。
- 下一篇文章我们先暂时放一次关于机器学习和数据挖掘的内容,来说下常见的随机过程离散模拟,随机过程的理论是很深奥的,我们会简单带过一点,主要是数值模拟,有离散马氏链、泊松过程等等。