拿到数据后,数据中可能会存在一些超大或极小的值,这些值与其他的值离得较远,显得格格不入,我们称其为离群点,有时也称为异常点。对于这些值,它的存在会导致影响最终的分析结果,带偏我们的分析。举个简单的例子,10个人的收入分别为2000,2500,2300,2425,2512,2375,2700,2265,2345,10000000,只算前9个,平均值就是2380,但加上最后一个,平均值就是1002144.2,就可能严重带偏最后分析的结果。因此,对于这样的数据,我们需要将其去除或修改。
(一)数据去极值
数据去极值的方法有很多,我们介绍其中3种:绝对值差中位数法(MAD),3西格玛法和百分位法。
(1)绝对值差中位数法(MAD)
绝对值差中位数法MAD处理步骤:
• Step 1:找出所有因子的中位数𝐹𝑚𝑒𝑑𝑖𝑎𝑛;
• Step 2:得到每个因子与中位数的绝对偏差值 |𝐹𝑖 − 𝐹𝑚𝑒𝑑𝑖𝑎𝑛|;
• Step 3:得到绝对偏差值的中位数𝑀𝐴𝐷;
• Step 4:确定阈值参数 𝑛,对超出范围 [ 𝐹𝑚𝑒𝑑𝑖𝑎𝑛 − 𝑛 ⋅ 𝑀𝐴𝐷, 𝐹𝑚𝑒𝑑𝑖𝑎𝑛 + 𝑛 ⋅ 𝑀𝐴𝐷 ]的因子值做调整。
def mad(df,n):
median=df.quantile(0.5)
new_median=abs(df - median).quantile(0.5)
up=median+n*new_median
down=median-n*new_median
print(median,up,down)
return df.clip(down,up,axis=1)
(2)3西格玛处理步骤:
• Step 1:计算出因子的平均值𝐹𝑚𝑒𝑎𝑛与标准差𝜎;
• Step 2:确定阈值参数 𝑛(默认为3),对超出范围 𝐹𝑚𝑒𝑎𝑛 − 𝑛𝜎, 𝐹𝑚𝑒𝑎𝑛 + 𝑛𝜎 的因子值做调整.
def threesigma(df,n):
mean=df.mean()
std=df.std()
up=mean+n*std
down=mean-n*std
return df.clip(down,up,axis=1)
(3)百分位法处理步骤:
• Step 1:找到因子值的97.5%分位数和2.5%分位数;
• Step 2:对大于97.5%分位数的因子值,或小于2.5%分位数的因子值进行调整。
def baifenwei(df,min=0.025,max=0.975):
p=df.quantile([min,max])
return df.clip(p.iloc[0],p.iloc[1],axis=1)
下面我们利用Tushare获取数据来进行数据去极值的操作:
import tushare as ts
ts.set_token('*****************************************')
pro=ts.pro_api()
dt=pro.daily_basic(ts_code='', trade_date='20180726', fields='ts_code,trade_date,turnover_rate,volume_ratio,pe,pb')
dt
基本数据如下:
我们对其中的turnover_rate观察一下:
print(dt.turnover_rate.max())
print(dt.turnover_rate.min())
值分别为:
61.914
0.0131
可以看出,最大值和最小值存在6000多倍的差距。
接着我们做去极值处理并绘图比较
dt=dt.set_index(['ts_code','trade_date'])
from matplotlib import pyplot as plt
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
fig=plt.figure(figsize=(10,8))
plt.subplot(3,1,1)
dt.turnover_rate.plot(kind='kde')
mad(dt,5.2).turnover_rate.plot(kind='kde')
plt.title('绝对值差中位数法')
plt.subplot(3,1,2)
dt.turnover_rate.plot(kind='kde')
threesigma(dt,3).turnover_rate.plot(kind='kde')
plt.title('3西格玛法')
plt.subplot(3,1,3)
dt.turnover_rate.plot(kind='kde')
baifenwei(dt).turnover_rate.plot(kind='kde')
plt.title('百分位数法')
可以看到,去除极值后的数据更为集中,差距明显缩小。
(二)数据标准化
标准化的目的是为了消除数据的量纲和数量级的差异,让数据有更好的可比性,能用于回归分析。比如假设北京的GDP是2000亿,而贵州的GDP是300亿(是假设假设假设!!),两者差异过大,会让分析的结果出现较大偏差,因此需要对其做标准化处理,比如让北京为1,贵州则变为0.15,使得二者差异缩小。
数据标准化的方法也有很多,这里介绍3种,分别是离差标准化,标准差标准化和rank值标准化。
(1)标准差标准化
也叫Z值标准化,主要是基于原始数据的均值和标准差,将数据值减去均值后,再除以标准差,其优点是能够保留因子数据之间原始的分布关系。
标准差标准法处理步骤:
• Step 1:MAD法去除极值;
• Step 2:Z值标准化处理。
def bzh(df):
mean=df.mean()
std=df.std()
return (df-mean)/std
mad(dt,5.2)
bzh(dt)
(2)rank值标准化
先将原始序列值转换为排序值,再将排序值作为参数用Z值法做标准化。其优点是标准化之后的分布是标准正态分布,容易看出因子载荷和收益率之间的相关性的方向,并且只关注原始序列的排序关系,对原始变量的分布不作要求,适用范围相对广。
Rank值法处理步骤:
• Step 1:先将原始序列值转换为排序值,;
• Step 2:再将排序值作为参数用Z值法做标准化。
def rankbzh(df):
rk=df.rank()
mean=rk.mean()
std=rk.std()
return (rk-mean)/std
(3)离差标准化
对原始数据进行简单线性变换,将原始数据映射到[0,1]区间内,转换公式为X=(X-MIN)/(MAX-MIN)
def min_max(df):
min=df.min()
max=df.max()
return (df-min)/(max-min)
最后绘图比较一下:
plt.figure(figsize=(10,8))
mad(dt,5.2).turnover_rate.plot(kind='kde',label='原始值')
bzh(dt).turnover_rate.plot(kind='kde',label='标准差标准化')
rankbzh(dt).turnover_rate.plot(kind='kde',label='rank值标准化')
min_max(dt).turnover_rate.plot(kind='kde',label='离差标准化')
plt.legend()
可以看出,离差标准化的处理,虽然将数据都映射到了[0,1]区间,但也会使得数据极为集中。