[量化学堂-数学知识]数据异常值处理

数据异常值处理
标签: #<Tag:0x00007f5bf1a0f830>

(iQuant) #1

导论:异常值问题在数据分析中经常遇到,本文介绍了多种处理数据异常值的方法。


在金融数据分析中,常常会遇到一些值过大或者过小的情况,当用这些值来构造其他特征的时候,可能使得其他的特征也是异常点,这将严重影响对金融数据的分析,或者是影响模型的训练。下面将带大家学习一些关于异常点处理的常用方法。

1、固定比例法

这种方法非常容易理解,我们把上下2%的值重新设置,若大于99%分位数的数值,则将其设置为99%分位数值,若低于1%分位数的数值,则将其重新设置为1%分位数值

2、均值标准差法

这种想法的思路来自于正态分布,假设$X∼N(μ,σ^2)$,那么:

通常把三倍标准差之外的值都视为异常值,不过要注意的是样本均值和样本标准差都不是稳健统计量,其计算本身受极值的影响就非常大,所以可能会出现一种情况,那就是我们从数据分布图上能非常明显的看到异常点,但按照上面的计算方法,这个异常点可能仍在均值三倍标准差的范围内。因此按照这种方法剔除掉异常值后,需要重新观察数据的分布情况,看是否仍然存在显著异常点,若存在则继续重复上述步骤寻找异常点。

3、MAD法

MAD 法是针对均值标准差方法的改进,把均值和标准差替换成稳健统计量,样本均值用样本中位数代替,样本标准差用样本MAD(Median Absolute Deviation)代替:
$$md=median(x_i, i=1,2,···,n)$$

$$MAD=median(|x_i-md|,i=1,2,···,n)$$

一般将偏离中位数三倍以上的数据作为异常值,和均值标准差法相比,其中位数和$MAD$不受异常值的影响。

4、BOXPLOT法

我们知道箱线图上也会注明异常值,假设Q1和Q3分别为数据从小到大排列的25%和75%分位数,记 $ IQR=Q1-Q3 $ ,把
$$(-\infty, Q_1-3*IQR)\bigcup(Q_3+3*IQR, +\infty)$$
区间里的数据标识为异常点。分位数也是稳健统计量,因此Boxplot 方法对极值不敏感,但如果样本数据正偏严重,且右尾分布明显偏厚时,Boxplot 方法会把过多的数据划分为异常数据,因此Hubert& Vandervieren (2007)对原有Boxplot 方法进行了偏度调整。首先样本偏度定义采用了Brys(2004)提出的MedCouple方法
$$md=median(x_i, i=1,2,···n)$$

$$mc=median(\frac {(x_i-md)-(md-x_j)} {x_i-x_j}, x_i \geq md, x_j \leq md)$$

然后给出了经偏度调整boxplot方法上下限:


附件:处理数据异常值的方法

克隆策略

异常数据的影响和识别

我们以2017年4月21日的A股所有股票的净资产收益率数据为例,这是一个横截面数据

In [268]:
fields = ['fs_roe_0']
start_date = '2017-04-21'
end_date = '2017-04-21'
instruments = D.instruments(start_date, end_date)
roe = D.features(instruments, start_date, end_date, fields=fields)['fs_roe_0']

1、描述性统计

In [269]:
print('均值:',roe.mean())
print('标准差:',roe.std())
roe.describe()
均值: 6.321435932147683
标准差: 21.523493941199046
Out[269]:
count    2782.000000
mean        6.321436
std        21.523494
min      -190.077896
25%         1.913875
50%         5.625300
75%        10.413725
max       949.800476
Name: fs_roe_0, dtype: float64

可以看出,接近2800家公司的净资产收益率的平均值为6.32,标准差为21.52,最大值为949.8,最小值为-190.08

2、绘制 直方图

In [270]:
roe.hist(bins=100)
Out[270]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f969c64fa58>

四种关于异常值处理的方法

1、固定比例法

In [271]:
roe = D.features(instruments, start_date, end_date, fields=fields)['fs_roe_0']
roe[roe >= roe.quantile(0.99)] = roe.quantile(0.99)
roe[roe <= roe.quantile(0.01)] = roe.quantile(0.01)
print('均值:',roe.mean())
print('标准差:',roe.std())
roe.hist(bins=100)
均值: 6.2873811630663585
标准差: 8.225571051255967
Out[271]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f96a0a9d7f0>

2、均值标准差方法

通常把三倍标准差之外的值都视为异常值,然后将这些异常值重新赋值

In [272]:
roe = D.features(instruments, start_date, end_date, fields=fields)['fs_roe_0']

roe[roe >= roe.mean() + 3*roe.std()] = roe.mean() + 3*roe.std()
roe[roe <= roe.mean() - 3*roe.std()] = roe.mean() - 3*roe.std()
print('均值:',roe.mean())
print('标准差:',roe.std())
roe.hist(bins=100)
均值: 6.380022262323887
标准差: 8.907421676828216
Out[272]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f969b5e7a90>

3、MAD方法

In [273]:
roe = D.features(instruments, start_date, end_date, fields=fields)['fs_roe_0']
roe = roe.dropna()
median = np.median(list(roe))
MAD = np.mean(abs(roe) - median)
roe = roe[abs(roe-median)/MAD <=6]  # 剔除偏离中位数6倍以上的数据
print('均值:',roe.mean())
print('标准差:',roe.std())
roe.hist(bins=100)
均值: 6.379718635607307
标准差: 5.917957128386792
Out[273]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f969b467f60>

4、boxplot法

In [274]:
from statsmodels.stats.stattools import medcouple                       
roe = D.features(instruments, start_date, end_date, fields=fields)['fs_roe_0']
roe = roe.dropna()
def boxplot(data):
    #mc可以使用statsmodels包中的medcouple函数直接进行计算
    mc = medcouple(data)
    data.sort()
    q1 = data[int(0.25 * len(data))]
    q3 = data[int(0.75 * len(data))]
    iqr = q3-q1
    if mc >= 0:
        l = q1-1.5 * np.exp(-3.5 * mc) * iqr
        u = q3 + 1.5 * np.exp(4 * mc) * iqr        
    else:
        l = q1 - 1.5 * np.exp(-4 * mc) * iqr
        u = q3 + 1.5 * np.exp(3.5 * mc) * iqr
    data = pd.Series(data)
    data[data < l] = l
    data[data > u] = u    
    return data

print('均值',boxplot(list(roe)).mean())
print('标准差',boxplot(list(roe)).std())
boxplot(list(roe)).hist(bins=100)
均值 6.734053462301644
标准差 7.026413849518903
Out[274]:
<matplotlib.axes._subplots.AxesSubplot at 0x7f969c152630>

本文由BigQuant宽客学院推出,版权归BigQuant所有,转载请注明出处。


社区干货与精选整理(持续更新中...)
新手量化
(zhuqiu) #2

为什么同样的代码自己克隆再运行一遍,得到的结果会有一些小差别,而不是一模一样?


(iQuant) #3

您好。可能的原因如下:

  • 数据进行了更新,数据有些差异
  • 有些数据处理涉及到一些随机种子每次会不一样,但好像本文没有。

我们先进行检查下,看看最新克隆的代码是否和本帖内容一致,此外,也欢迎您在文末将数据有差异的地方标注一下。谢谢!


(mwbqq799201437) #4

用了random,计算机每次运行随机出来的数据不一样。